Skip to content

Commit e6dd94f

Browse files
authored
Fixed Raycast extension MCP spawn (#42)
Co-authored-by: Tomas Vesely <448809+wham@users.noreply.github.com>
1 parent 926b723 commit e6dd94f

1 file changed

Lines changed: 90 additions & 36 deletions

File tree

raycast/src/search.tsx

Lines changed: 90 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ async function callMCPSearch(query: string): Promise<SearchResult[]> {
8787
let errorData = "";
8888
let hasReceivedResponse = false;
8989
let responseTimeout: NodeJS.Timeout;
90+
let isInitialized = false;
9091

9192
// Set a timeout for the MCP response
9293
responseTimeout = setTimeout(() => {
@@ -100,36 +101,98 @@ async function callMCPSearch(query: string): Promise<SearchResult[]> {
100101
const output = data.toString();
101102
responseData += output;
102103

103-
// Check if we have a complete JSON-RPC response
104+
// Process line-delimited JSON-RPC messages
104105
const lines = responseData.split("\n");
106+
responseData = lines.pop() || ""; // Keep incomplete line for next data event
107+
105108
for (const line of lines) {
106-
if (
107-
line.trim() &&
108-
line.includes('"jsonrpc":"2.0"') &&
109-
line.includes('"id":1')
110-
) {
111-
hasReceivedResponse = true;
112-
clearTimeout(responseTimeout);
113-
114-
// Process the response immediately
115-
try {
116-
const results = parseMCPResponse(line);
117-
mcpProcess.kill(); // Clean up the process
118-
resolve(results);
119-
return;
120-
} catch (error) {
121-
console.error("Parse error:", error);
109+
if (!line.trim()) continue;
110+
111+
try {
112+
const message = JSON.parse(line);
113+
114+
// Handle initialize response
115+
if (message.id === 1 && message.result && !isInitialized) {
116+
console.log("MCP server initialized");
117+
isInitialized = true;
118+
119+
// Send initialized notification
120+
const initializedNotification = {
121+
jsonrpc: "2.0",
122+
method: "notifications/initialized",
123+
params: {},
124+
};
125+
mcpProcess.stdin.write(
126+
JSON.stringify(initializedNotification) + "\n"
127+
);
128+
129+
// Now send the actual search request
130+
const searchRequest = {
131+
jsonrpc: "2.0",
132+
id: 2,
133+
method: "tools/call",
134+
params: {
135+
name: "search",
136+
arguments: {
137+
query: query,
138+
fields: [
139+
"title",
140+
"url",
141+
"repository",
142+
"created_at",
143+
"author",
144+
"type",
145+
"state",
146+
],
147+
},
148+
},
149+
};
150+
mcpProcess.stdin.write(JSON.stringify(searchRequest) + "\n");
151+
continue;
152+
}
153+
154+
// Handle search response
155+
if (message.id === 2 && message.result) {
156+
hasReceivedResponse = true;
157+
clearTimeout(responseTimeout);
158+
159+
try {
160+
const results = parseMCPResponse(line);
161+
mcpProcess.kill(); // Clean up the process
162+
resolve(results);
163+
return;
164+
} catch (error) {
165+
console.error("Parse error:", error);
166+
mcpProcess.kill();
167+
reject(
168+
new Error(`Failed to parse MCP response: ${error.message}`)
169+
);
170+
return;
171+
}
172+
}
173+
174+
// Handle errors
175+
if (message.error) {
176+
console.error("MCP returned error:", message.error);
122177
mcpProcess.kill();
123-
reject(new Error(`Failed to parse MCP response: ${error.message}`));
178+
reject(
179+
new Error(
180+
`MCP error: ${message.error.message || "Unknown error"}`
181+
)
182+
);
124183
return;
125184
}
185+
} catch (parseError) {
186+
// Ignore parse errors for incomplete messages
187+
console.log("Skipping unparseable message:", line);
126188
}
127189
}
128190
});
129191

130192
mcpProcess.stderr.on("data", (data) => {
131193
const error = data.toString();
132194
errorData += error;
195+
console.error("MCP stderr:", error);
133196
});
134197

135198
mcpProcess.on("error", (error) => {
@@ -145,32 +208,23 @@ async function callMCPSearch(query: string): Promise<SearchResult[]> {
145208
}
146209
});
147210

148-
// Send the search request via JSON-RPC
149-
const searchRequest = {
211+
// Start the MCP handshake with initialize request
212+
const initializeRequest = {
150213
jsonrpc: "2.0",
151214
id: 1,
152-
method: "tools/call",
215+
method: "initialize",
153216
params: {
154-
name: "search",
155-
arguments: {
156-
query: query,
157-
fields: [
158-
"title",
159-
"url",
160-
"repository",
161-
"created_at",
162-
"author",
163-
"type",
164-
"state",
165-
],
217+
protocolVersion: "2025-06-18",
218+
clientInfo: {
219+
name: "raycast-github-brain",
220+
version: "1.0.0",
166221
},
222+
capabilities: {},
167223
},
168224
};
169225

170226
try {
171-
mcpProcess.stdin.write(JSON.stringify(searchRequest) + "\n");
172-
// Don't end stdin immediately - let the process handle the response first
173-
// The stdin will be closed when we kill the process after getting the response
227+
mcpProcess.stdin.write(JSON.stringify(initializeRequest) + "\n");
174228
} catch (error) {
175229
console.error("Error writing to MCP stdin:", error);
176230
clearTimeout(responseTimeout);

0 commit comments

Comments
 (0)