@@ -5,15 +5,22 @@ type McpConfig = {
55 mcpServers : Record < string , { url : string ; headers ?: Record < string , string > } >
66}
77
8- /** Reads the .mcp.json config from the game directory */
8+ /** Reads the .mcp.json config, searching the game directory and ancestor directories */
99const readMcpConfig = ( gameDir : string ) : McpConfig | null => {
10- const mcpPath = path . join ( gameDir , ".mcp.json" )
11- if ( ! fs . existsSync ( mcpPath ) ) return null
12- try {
13- return JSON . parse ( fs . readFileSync ( mcpPath , "utf-8" ) )
14- } catch {
15- return null
10+ let dir = path . resolve ( gameDir )
11+ const root = path . parse ( dir ) . root
12+ while ( dir !== root ) {
13+ const mcpPath = path . join ( dir , ".mcp.json" )
14+ if ( fs . existsSync ( mcpPath ) ) {
15+ try {
16+ return JSON . parse ( fs . readFileSync ( mcpPath , "utf-8" ) )
17+ } catch {
18+ return null
19+ }
20+ }
21+ dir = path . dirname ( dir )
1622 }
23+ return null
1724}
1825
1926/** Sends a JSON-RPC request to the MCP server and parses the SSE response */
@@ -28,16 +35,21 @@ const mcpRequest = async (url: string, headers: Record<string, string>, method:
2835 body : JSON . stringify ( { jsonrpc : "2.0" , id : 1 , method, params } ) ,
2936 } )
3037
38+ if ( ! response . ok ) throw new Error ( `MCP server returned ${ response . status } ${ response . statusText } ` )
39+
3140 const text = await response . text ( )
3241 // Parse SSE format: lines starting with "data: " contain the JSON payload
3342 for ( const line of text . split ( "\n" ) ) {
3443 if ( ! line . startsWith ( "data: " ) ) continue
3544 try {
3645 const parsed = JSON . parse ( line . slice ( 6 ) )
46+ if ( parsed . error ) throw new Error ( `MCP error: ${ parsed . error . message ?? JSON . stringify ( parsed . error ) } ` )
3747 if ( parsed . result ) return parsed . result
38- } catch { }
48+ } catch ( e ) {
49+ if ( e instanceof Error && e . message . startsWith ( "MCP error:" ) ) throw e
50+ }
3951 }
40- return null
52+ throw new Error ( `MCP server returned no result. Response body: ${ text . slice ( 0 , 200 ) } ` )
4153}
4254
4355/** Returns the MCP server URL from .mcp.json, or null if not configured */
@@ -65,26 +77,25 @@ export const listMcpPrompts = async (gameDir: string): Promise<{ name: string; d
6577 }
6678}
6779
68- /** Fetches a skill prompt 's content from the MCP server configured in .mcp.json */
69- export const fetchSkillPrompt = async ( skillName : string , gameDir : string ) : Promise < string | null > => {
80+ /** Fetches a step 's instructions from the MCP server configured in .mcp.json */
81+ export const fetchSkillPrompt = async ( stepName : string , gameDir : string ) : Promise < string > => {
7082 const config = readMcpConfig ( gameDir )
71- if ( ! config ) return null
83+ if ( ! config ) throw new Error ( `No .mcp.json found in ${ gameDir } ` )
7284
7385 const server = Object . values ( config . mcpServers ) [ 0 ]
74- if ( ! server ) return null
86+ if ( ! server ) throw new Error ( "No MCP server configured in .mcp.json" )
7587
76- try {
77- const result = await mcpRequest ( server . url , server . headers ?? { } , "prompts/get" , { name : skillName } )
78- if ( ! result ?. messages ?. length ) return null
88+ const result = await mcpRequest ( server . url , server . headers ?? { } , "prompts/get" , { name : stepName } )
89+ if ( ! result ?. messages ?. length ) throw new Error ( `MCP server returned no instructions for "${ stepName } "` )
7990
80- return result . messages
81- . map ( ( m : any ) => {
82- if ( typeof m . content === "string" ) return m . content
83- if ( m . content ?. text ) return m . content . text
84- return ""
85- } )
86- . join ( "\n" )
87- } catch {
88- return null
89- }
91+ const text = result . messages
92+ . map ( ( m : any ) => {
93+ if ( typeof m . content === "string" ) return m . content
94+ if ( m . content ?. text ) return m . content . text
95+ return ""
96+ } )
97+ . join ( "\n" )
98+
99+ if ( ! text . trim ( ) ) throw new Error ( `MCP server returned empty instructions for " ${ stepName } "` )
100+ return text
90101}
0 commit comments