@feniix/pi-exa
Exa API extension for pi — web search, content fetching, and advanced search via Exa AI
Package details
Install @feniix/pi-exa from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@feniix/pi-exa- Package
@feniix/pi-exa- Version
5.0.1- Published
- Jun 15, 2026
- Downloads
- 1,042/mo · 117/wk
- Author
- feniix
- License
- MIT
- Types
- extension, skill
- Size
- 317.2 KB
- Dependencies
- 3 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./extensions/index.ts"
],
"skills": [
"./skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@feniix/pi-exa
Exa AI extension for pi with search, fetch, research, and answer capabilities.
Features
- web_search_exa: default web search (highlights + short text snippets).
- web_fetch_exa: fetch page content by URL.
- web_search_advanced_exa: advanced search options and category filters (disabled by default).
- web_research_exa: deep-research synthesis (disabled by default).
- web_answer_exa: quick grounded answers.
- web_find_similar_exa: discover related URLs.
- exa_research_step/status/summary/reset: local, stateful research-planning tools that recommend explicit Exa retrieval calls without executing them.
Why pi-exa vs the hosted Exa MCP?
The hosted Exa MCP at https://mcp.exa.ai/mcp is a fine default for one-shot search and fetch. pi-exa exists for cases the hosted MCP cannot address by design:
- Tools the hosted MCP does not expose.
web_answer_exa(Exa's/answerendpoint) andweb_find_similar_exa(/findSimilar) are not advertised by the hosted MCP under any flag. If you need grounded answers with citations or "more like this" discovery, you call them directly through pi-exa. - Local stateful planning.
exa_research_step / status / summary / resetkeep an in-memory research plan that survives across calls in a single pi session. A stateless remote MCP cannot offer this — there is no per-session memory to update. - Local key custody and allowlists. Your
EXA_API_KEYandenabledToolsallowlist stay on the workstation. The hosted MCP requires sending your key to a third party on every request. - Pre-flight validation. pi-exa rejects category/filter combinations Exa silently ignores (e.g.,
category: "people"with non-LinkedInincludeDomains), so you find out at the call site instead of in a quiet, empty result set. - Forward-compatibility on the
/researchsunset. Every pi-exa tool routes through Exa's canonical endpoints —/search,/contents,/answer,/findSimilar. The hosted MCP'sdeep_researcher_start/deep_researcher_checktools route through the deprecated/researchendpoint. When Exa enforces that sunset, those hosted tools break or lose async semantics; pi-exa'sweb_research_exa(which uses/searchwithtype: "deep-reasoning") keeps working.
| Capability | Hosted Exa MCP | pi-exa |
|---|---|---|
web_search_exa |
yes | yes |
web_fetch_exa |
yes | yes |
web_search_advanced_exa |
opt-in | opt-in |
web_answer_exa (/answer) |
no | yes |
web_find_similar_exa (/findSimilar) |
no | yes |
Local research planner (exa_research_*) |
no | yes |
| Local API key custody | no | yes |
Routes deep research through /search |
no | yes |
Install
pi install npm:@feniix/pi-exa
For ephemeral use:
pi -e npm:@feniix/pi-exa
Configuration
You need an Exa API key from dashboard.exa.ai/api-keys for retrieval tools. The local exa_research_* planning tools work without an API key because they do not call Exa network APIs.
If you configure enabledTools, it acts as a strict allowlist. Include the exa_research_* names if you want the planner tools available with an explicit allowlist.
Recommended: environment variable
export EXA_API_KEY="your-key"
Recommended for private overrides: explicit config file
Use a private config file when you want to store an API key outside shared project settings:
{
"apiKey": "your-key",
"enabledTools": [
"exa_research_step",
"exa_research_status",
"exa_research_summary",
"exa_research_reset",
"web_search_exa",
"web_fetch_exa",
"web_answer_exa",
"web_find_similar_exa"
],
"advancedEnabled": false,
"researchEnabled": false
}
Then run pi with:
pi -e npm:@feniix/pi-exa -- --exa-config-file ~/.config/pi/exa.json
Shared non-secret settings
Supports standard pi settings locations:
- project:
.pi/settings.json - global:
~/.pi/agent/settings.json
Example:
{
"pi-exa": {
"enabledTools": [
"exa_research_step",
"exa_research_status",
"exa_research_summary",
"exa_research_reset",
"web_search_exa",
"web_fetch_exa",
"web_answer_exa",
"web_find_similar_exa"
],
"advancedEnabled": false,
"researchEnabled": false
}
}
apiKey is accepted in settings files for compatibility, but pi-exa will warn when it is loaded there. Prefer EXA_API_KEY or --exa-config-file for secrets.
CLI flags
--exa-api-key <key>: API key override.--exa-enable-advanced: enableweb_search_advanced_exa.--exa-enable-research: enableweb_research_exa.--exa-config-file <path>: load configuration from file.--exa-config <path>(deprecated alias for--exa-config-file).--exa-timeout-ms <ms>: default per-call timeout for Exa-backed tools (built-in 60000).--exa-research-timeout-ms <ms>: override forweb_research_exa(built-in 180000; deep-reasoning runs longer).
The timeout bounds the JS-side wait.
exa-jsdoes not yet acceptAbortSignal(exa-labs/exa-js#158), so the underlying HTTP request continues until Exa resolves it and Exa still bills for the completed call. The timeout error message states this explicitly.
Tools
exa_research_step
Records one step in an in-memory research-planning session. Params include topic, stage, note, optional criteria, sources, gaps, assumptions, nextAction, branch/revision metadata, thought_number, total_thoughts, and next_step_needed.
exa_research_status
Reports the current local planning state: topic, step count, active stage, branches, criteria coverage, source pack summary, open gaps, assumptions, and recommended next action.
exa_research_summary
Generates human-readable research planning output. Modes: brief, execution_plan, source_pack, and payload. Payload mode suggests a web_research_exa payload only; it does not run retrieval.
exa_research_reset
Clears the active in-memory planning session.
web_search_exa
Params: query (required), numResults.
Returns: formatted snippets with optional highlights and metadata (costDollars, searchTime).
web_fetch_exa
Params: urls (required array), maxCharacters, highlights, summary (query), maxAgeHours.
web_search_advanced_exa
Params:
query(required)numResults(1-100, default 10)category: one ofcompany,research paper,news,pdf,personal site,financial report,peopletype: canonicalauto | fast | instant; legacykeyword | neural | hybridstill accepted (Exa's/searchendpoint continues to accept them). Deep types (deep-reasoning | deep-lite | deep) are rejected here — useweb_research_exafor those.- Date filters:
startPublishedDate,endPublishedDate(ISO dates). - Domain filters:
includeDomains,excludeDomains. - Text filters:
includeText(single-element array; only return results whose text contains this string, up to 5 words),excludeText(single-element array; exclude results whose text contains this string, up to 5 words). The Exa API accepts at most one string per filter. userLocation: two-letter ISO country code (e.g.,US,GB,DE).moderation: whentrue, filter unsafe content.additionalQueries: alternative query formulations to broaden coverage.textMaxCharacters: max chars of page text per result (default 3000).contextMaxCharacters: max chars for the aggregated context string. Maps to Exa's deprecatedcontextoption and may be removed in a future Exa API release.- Highlights:
enableHighlights(gate),highlightsMaxCharacters(preferred),highlightsNumSentences(legacy fallback),highlightsQuery(overrides the search query for highlight ranking). ProvidinghighlightsQueryorhighlightsMaxCharactersimpliesenableHighlights: true; passingenableHighlights: falseexplicitly always disables highlights. - Summary:
enableSummaryand/orsummaryQuery(providingsummaryQueryimpliesenableSummary: true; passingenableSummary: falseexplicitly always disables the summary). - Freshness:
maxAgeHours(0 = always fresh, -1 = cache-only),livecrawlTimeout(ms; capped at 60000 = 60s). - Subpages:
subpages(1-10),subpageTarget(single keyword or list of keywords used to select which subpages to crawl, e.g.'about'or['about', 'pricing']).
Notes:
- Deep types are rejected here. Use
web_research_exafordeep-reasoning,deep-lite, ordeep. - Invalid categories return an error instead of silently falling back to an unfiltered search.
- The
companyandpeoplecategories do not supportstartPublishedDate,endPublishedDate, orexcludeDomains; thepeoplecategory only accepts LinkedIn domains forincludeDomains. These are enforced pre-flight. startCrawlDate/endCrawlDateare intentionally not exposed — Exa silently ignores them as of 2026-04-15.
web_research_exa
Params include:
query(required)type:deep-reasoning | deep-lite | deepsystemPromptoutputSchema(typemay be"object"or"text", default"text"; object mode is capped at 10 properties / depth 2 and gives per-field grounding). The default is required for synthesis to run — Exa's/searchendpoint only returns anoutputfield when anoutputSchemais provided (see issue #115 and the Search API Reference for Coding Agents).- optional
additionalQueries, filters,numResults, andtextMaxCharacters
web_answer_exa
Params include query (required), systemPrompt, text, and outputSchema.
web_find_similar_exa
Params include url (required), numResults, textMaxCharacters, excludeSourceDomain, date filters, and domain filters.
Integration tests
Live integration coverage is available for web_search_exa, web_fetch_exa, and web_research_exa.
These tests are:
- skipped by default
- only enabled when you opt in manually
- always skipped in CI
Run them locally with a real API key:
EXA_API_KEY=your-key npx vitest run packages/pi-exa/__tests__/integration.test.ts -- --exa-live
You can also enable them with an environment variable instead of the CLI flag:
PI_EXA_LIVE=1 EXA_API_KEY=your-key npx vitest run packages/pi-exa/__tests__/integration.test.ts
MCP server
pi-exa also exposes its tool surface as an MCP stdio server, suitable for any MCP-aware host (Claude Desktop, Claude Code, etc.). The server uses the same portable tool implementations as the Pi adapter — only the gating and credential resolution differ.
Run with:
npx pi-exa
Environment configuration
| Variable | Effect |
|---|---|
EXA_API_KEY |
Exa API key. Required for retrieval tools; planner tools work without it. |
EXA_ENABLE_ADVANCED |
Truthy (1 / true / yes) enables web_search_advanced_exa. |
EXA_ENABLE_RESEARCH |
Truthy enables web_research_exa. |
EXA_ENABLED_TOOLS |
Comma-separated allowlist. Highest precedence. Empty/whitespace-only values emit a warning and fall through to the per-tool toggle defaults. |
EXA_CONFIG_FILE |
Path to a JSON config file (same shape as the CLI --exa-config-file). Use for apiKey, enabledTools, advancedEnabled, researchEnabled. |
EXA_CONFIG |
Deprecated alias for EXA_CONFIG_FILE. Still read; prefer EXA_CONFIG_FILE. |
EXA_TIMEOUT_MS |
Default per-call timeout in ms for Exa-backed tools. Built-in 60000. Underlying HTTP request continues until Exa resolves it; see exa-labs/exa-js#158. |
EXA_RESEARCH_TIMEOUT_MS |
Override for web_research_exa only. Built-in 180000. |
Precedence
Same rules as the Pi adapter:
EXA_ENABLED_TOOLS(env) — strict allowlist.enabledTools(config file) — strict allowlist; an empty array means "no tools".EXA_ENABLE_ADVANCED/EXA_ENABLE_RESEARCH(env) oradvancedEnabled/researchEnabled(config file).- Default: 8 tools on (4 cheap Exa + 4 planner);
web_search_advanced_exaandweb_research_exahidden.
Example: Claude Desktop / claude_desktop_config.json
{
"mcpServers": {
"pi-exa": {
"command": "npx",
"args": ["-y", "@feniix/pi-exa"],
"env": {
"EXA_API_KEY": "your-key",
"EXA_ENABLE_ADVANCED": "1"
}
}
}
}
Example: generic mcp.json
{
"mcpServers": {
"pi-exa": {
"command": "npx",
"args": ["pi-exa"],
"env": { "EXA_API_KEY": "your-key" }
}
}
}
Notes
exa_research_*planning tools are enabled by default when no explicitenabledToolsallowlist is configured, local-only, and do not require an Exa API key.web_search_advanced_exaandweb_research_exaare opt-in and disabled by default.- Research/tool output may include both
textanddetails.parsedOutputdepending onoutputSchema.type.