can1357/oh-my-pi · error · Error
MCP error: ${response.error.message}
Error message
MCP error: ${response.error.message} What it means
`fetchExaTools` performs an MCP `tools/list` JSON-RPC request against https://mcp.exa.ai/mcp. When the response carries an `error` object (JSON-RPC error or MCP protocol error), the function logs it and rethrows it as `Error("MCP error: <message>")`, surfacing the server-side reason to the caller.
Source
Thrown at packages/coding-agent/src/exa/mcp-client.ts:84
if (isSearchResponse(candidate)) {
return candidate;
}
}
return payload;
}
/** Fetch available tools from Exa MCP */
export async function fetchExaTools(apiKey: string | null, toolNames: string[]): Promise<MCPTool[]> {
const params = new URLSearchParams();
if (apiKey) params.set("exaApiKey", apiKey);
params.set("toolNames", toolNames.join(","));
const url = `https://mcp.exa.ai/mcp?${params.toString()}`;
const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
if (response.error) {
logger.error("MCP tools/list error", { toolNames, error: response.error });
throw new Error(`MCP error: ${response.error.message}`);
}
return response.result?.tools ?? [];
}
/** Fetch available tools from Websets MCP */
export async function fetchWebsetsTools(apiKey: string): Promise<MCPTool[]> {
const url = `https://websetsmcp.exa.ai/mcp?exaApiKey=${encodeURIComponent(apiKey)}`;
const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
if (response.error) {
logger.error("Websets MCP tools/list error", { error: response.error });
throw new Error(`MCP error: ${response.error.message}`);
}
return response.result?.tools ?? [];
}
View on GitHub (pinned to 9690622007)
Solutions
- Read `response.error.message` in the thrown text — it states the server's exact reason (auth, unknown tool, rate limit).
- Verify EXA_API_KEY is current (test with a direct curl to the Exa API) and update .env/env.
- Retry with backoff for transient 5xx/rate-limit errors; check Exa status if it persists.
- Confirm the requested toolNames exist against Exa's current MCP catalog and remove stale names.
Example fix
// before
const tools = await fetchExaTools(staleApiKey, ["web_search_exa"]); // MCP error: invalid api key
// after
const apiKey = findApiKey();
if (!apiKey) throw new Error("EXA_API_KEY missing");
const tools = await fetchExaTools(apiKey, ["web_search_exa"]); Defensive patterns
Strategy: try-catch
Validate before calling
const apiKey = findApiKey();
if (!apiKey) throw new Error("EXA_API_KEY missing — set it in env or .env before listing Exa MCP tools"); Type guard
function isMCPToolsResponse(r: unknown): r is MCPToolsResponse {
return typeof r === "object" && r !== null && !("error" in r && r.error);
} Try / catch
let tools: MCPTool[];
try {
tools = await fetchExaTools(apiKey, toolNames);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
if (msg.startsWith("MCP error:") && /rate|429/i.test(msg)) {
await Bun.sleep(retryDelay);
tools = await fetchExaTools(apiKey, toolNames);
} else {
throw err; // auth/unknown-tool errors are not retryable
}
} Prevention
- Keep EXA_API_KEY fresh and validate it with a cheap API call at startup.
- Check the thrown message for the server's reason before choosing retry vs abort.
- Cache tool schemas (fetchMCPToolSchema already does) to reduce tools/list traffic.
- Handle the error at the MCPWrappedTool.execute boundary, which already converts thrown errors into tool-result text instead of crashing the run.
When it happens
Trigger: Calling `fetchExaTools` (directly or via `tools`/`fetchMCPToolSchema`) when the Exa MCP server rejects `tools/list`: invalid or expired EXA_API_KEY passed as the exaApiKey query param, unknown/unsupported toolNames, rate limiting, or Exa-side protocol/deployment errors.
Common situations: Rotated or revoked EXA_API_KEY still cached in env/.env; requesting a tool name Exa no longer exposes; regional outages or MCP protocol version mismatch; hitting the endpoint without network egress allowed.
Related errors
- MCP request failed: ${response.status} ${response.statusText
- Failed to parse MCP response
- Gemini Files API upload finalization failed with HTTP ${fina
- Gemini Files API delete failed with HTTP ${response.status}
- MCP resource read error: ${message}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/516f759ff72d6d4f.
Report an issue: GitHub.