Activating Exa always failed with "Exa API key is invalid or unauthorized (HTTP 403)" — the key was never actually tested. Two bugs stacked: 1. mcp.exa.ai is behind Cloudflare, which bans urllib's default Python-urllib/3.x agent with 403 / "error code: 1010" before Exa sees the request. verify.py sent no User-Agent and mapped any 403 to "API key is invalid". Reproduced on the server with no key set at all — same "invalid key" message. 2. The probed endpoint cannot validate a key anyway: JSON-RPC initialize against the MCP endpoint returns 200 no matter what ?exaApiKey= carries (checked with a real key, a bogus key, and no key). Fixing only the headers would have flipped the bug to accepting every key, including garbage. With a key, the probe is now a minimal POST to api.exa.ai/search with the key in the x-api-key header — the only call that exercises the credential (200 valid, 401/403 + Exa JSON error invalid, 402 out of credits, 429 valid but throttled). With no key it probes MCP initialize and reports reachability only, never validity. Both requests send a User-Agent; the MCP one also sends Accept: application/json, text/event-stream (else HTTP 406). An opaque 401/403 with no Exa JSON error is now reported as "blocked before reaching the API — the key was not tested", instead of blaming the credential. Also re-aligns manifest/fragment versions to 5 / 1.0.4 (were 2/1.0.1 vs 4/1.0.3; skald reads installed_version from the manifest, so the update badge would never have appeared) and raises verify.timeout_secs 15 -> 20.
60 lines
1.6 KiB
JSON
60 lines
1.6 KiB
JSON
{
|
|
"id": "exa",
|
|
"name": "Exa",
|
|
"version": 5,
|
|
"version_string": "1.0.4",
|
|
"version_release_date": "2026-08-20",
|
|
"type": "mcp_remote",
|
|
"scope": "global",
|
|
"tags": [
|
|
"search",
|
|
"mcp",
|
|
"remote",
|
|
"ai"
|
|
],
|
|
"docs": [
|
|
{
|
|
"lang": "en",
|
|
"description": "AI-powered web search, content extraction, and embeddings via Exa's neural search engine. Perfect for deep research, finding specific information, and analyzing page content. Works without an API key (free rate-limited tier) \u2014 add your own key for higher limits.",
|
|
"llm_short_description": "Neural web search \u2014 semantic search and content extraction that understands intent, not just keywords. Free tier available without API key."
|
|
}
|
|
],
|
|
"auth": {
|
|
"type": "api_key"
|
|
},
|
|
"env": [
|
|
{
|
|
"name": "exaApiKey",
|
|
"label": "Exa API key (optional)",
|
|
"description": "Get one at https://dashboard.exa.ai/api-keys. Without it, Exa works with free rate limits. Add your key for unlimited production use.",
|
|
"required": false,
|
|
"secret": true,
|
|
"example": "exa-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
}
|
|
],
|
|
"mcp_config": {
|
|
"url": "https://mcp.exa.ai/mcp?exaApiKey={SECRET:exaApiKey}",
|
|
"transport": "streamable-http"
|
|
},
|
|
"verify": {
|
|
"command": "python3 verify.py",
|
|
"timeout_secs": 20
|
|
},
|
|
"homepage": "https://exa.ai",
|
|
"icon_small": "icon_sm.png",
|
|
"icon_large": "icon_lg.png",
|
|
"tools": [
|
|
{
|
|
"name": "web_search_exa",
|
|
"display_name": "Web Search"
|
|
},
|
|
{
|
|
"name": "web_fetch_exa",
|
|
"display_name": "Web Fetch"
|
|
}
|
|
],
|
|
"requires": [
|
|
"API_KEY"
|
|
]
|
|
}
|