Skip to content

Comments

feat: multi-provider search MCP server (v1.20.0)#39

Merged
iamladi merged 12 commits intomainfrom
feat/multi-provider-search-mcp
Feb 14, 2026
Merged

feat: multi-provider search MCP server (v1.20.0)#39
iamladi merged 12 commits intomainfrom
feat/multi-provider-search-mcp

Conversation

@iamladi
Copy link
Owner

@iamladi iamladi commented Feb 14, 2026

Summary

  • New multi-provider search MCP server replacing the old perplexity-mcp/ with a unified search-mcp/ supporting Exa, Brave, and Perplexity via OpenRouter
  • Provider priority cascade: Exa > Brave > Perplexity — auto-selects best available based on environment keys
  • Review fixes: resolved Exa timeout memory leak (setTimeout in Promise.race never cleared) and removed redundant clearTimeout in Perplexity SSE handler
  • 87 tests across 6 test files covering all providers, formatting, types, and server entry point

Changes

Area Files What
Types & validation types.ts Unified SearchResult, SearchInput, provider param matrix, clampNumResults, validateProviderParams
Providers providers/brave.ts, exa.ts, perplexity.ts Adapter per provider with consistent error handling, timeouts, response mapping
Formatting format.ts Unified markdown + <meta> tag output
Server index.ts MCP server with Zod schema, provider detection, dispatch
Tests __tests__/*.test.ts 87 tests: happy paths, error handling, timeouts, edge cases
Cleanup utils/perplexity-mcp/ removed Old single-provider implementation replaced

Test plan

  • bun test utils/search-mcp/ — 87 pass, 0 fail
  • bun run validate — plugin manifest valid, versions synced
  • Manual: verify MCP server starts with EXA_API_KEY set, defaults to Exa
  • Manual: verify fallback to Brave when only BRAVE_API_KEY is set
  • Manual: verify cross-provider param validation returns clear errors

Implements searchExa adapter following TDD strict workflow:
- DI-based ExaClient interface for testability
- Maps recency to startPublishedDate using recencyToDate()
- Maps search options (type, domains, category, include_text)
- Clamps num_results to max 100 via clampNumResults()
- Handles null titles, empty results, cost tracking
- Extracts images and combines content from results
- 15 test cases covering all adapter behaviors

Task: Create the Exa adapter with TDD strict (tests first)
Created Perplexity adapter for search-mcp with strict TDD workflow:
- Tests cover SSE parsing, citation extraction, image extraction, error handling
- Implementation uses dependency injection for testability
- Supports LLM tuning params (temperature, top_p, top_k, max_tokens, etc.)
- Handles timeout and error scenarios with standardized error messages

Task: Create the Perplexity adapter with TDD strict (tests first)
Implements Brave Search API adapter following TDD strict workflow.
Tests written first (RED), then minimal implementation (GREEN).

- 10 test cases covering all specified behaviors
- Dependency injection for testability
- 30s timeout with AbortController
- Recency to freshness mapping (day→pd, week→pw, month→pm, year→py)
- Proper error handling for non-OK responses and timeouts
- Result parsing: web.results → SearchResult format
- types.ts: SearchInput, SearchResult, SearchProvider, ToolOutput types
- types.ts: recencyToDate(), clampNumResults(), validateProviderParams()
- types.ts: PROVIDER_PARAM_MATRIX for FR-7 param validation
- format.ts: formatResponse() converts SearchResult to MCP text output
- package.json: add exa-js@2.4.0 dependency
- 31 tests covering types and format modules
- index.ts: MCP server with search_web tool registration
- Detects available providers from env vars at startup
- Validates DEFAULT_PROVIDER against available providers
- Static Zod enum for provider param (FR-6)
- Provider-specific param validation in handler (FR-7)
- Dynamic tool description listing available providers
- Dispatches to correct adapter based on provider param
- 15 tests covering provider detection, resolution, and description
- .mcp.json: replace perplexity entry with search entry, add env vars
- web-search-researcher.md: mcp__perplexity__search_web → mcp__search__search_web
- README.md: update MCP config example and directory structure
…d type safety

- Add AbortController timeout (30s) and error normalization to Exa adapter (FR-8)
- Replace exaClient! non-null assertion with defensive null check in index.ts
- Add typed BraveWebResult/BraveSearchResponse interfaces, remove `any` casts
- Fix brave.ts clampNumResults to use literal "brave" instead of input.provider
- Add 3 new tests for exa timeout and error normalization (87 total passing)
Replaced by the multi-provider search-mcp which supports Perplexity, Exa, and Brave.
Instead of hardcoding "30s", derive from actual timeoutMs parameter.
Clear Exa setTimeout after Promise.race resolves to prevent timer
memory leak. Remove redundant clearTimeout from Perplexity catch block
(finally block handles it). Reorder default provider cascade to
Exa > Brave > Perplexity based on result quality preference.
Bump version for multi-provider search MCP server, review fixes,
and provider priority reordering.
The .mcp.json hardcoded DEFAULT_PROVIDER=perplexity, which defeated
the ALL_PROVIDERS priority order (Exa > Brave > Perplexity) set in
index.ts. Removing it lets the code cascade work as intended.
@iamladi iamladi merged commit de0e57f into main Feb 14, 2026
1 check passed
@iamladi iamladi deleted the feat/multi-provider-search-mcp branch February 14, 2026 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant