MCP

    MCP

    Desearch MCP is available over hosted Streamable HTTP. Create an API key in Console, then add the server in your agent.

    Endpoint: https://mcp.desearch.ai/mcp
    Auth for tool calls: Authorization: <DESEARCH_API_KEY> (raw key, as in the examples below). Authorization: Bearer <DESEARCH_API_KEY> and x-api-key: <DESEARCH_API_KEY> also work.

    Discovery without a key is allowed: initialize and tools/list return successfully with no API key. Calling a tool (tools/call) without a key returns an unauthorized error (isError: true; HTTP 401 on a direct request). Send a key with tools/call.

    Connect with OAuth (remote connector)

    The remote connector URL is https://mcp.desearch.ai/mcp.

    MCP clients that support OAuth can connect to that URL and sign in through OAuth. On the Desearch sign-in page you paste your Desearch API key, which you get from https://console.desearch.ai/api-keys.

    Sending the API key directly still works: in the Authorization header as the raw key or as Bearer <key>, or in the x-api-key header.

    Choose your client

    ClientTransportNotes
    Claude CodeHosted HTTPCLI add with HTTP transport
    CursorHosted HTTPmcp.json url + headers
    VS CodeHosted HTTPuse servers key (not mcpServers)
    Claude DesktopHosted HTTP if the client supports URL serversUse the hosted URL
    Codex / otherHosted HTTPMatch the client's header settings
    npm / stdioHosted HTTPUse the hosted endpoint

    Claude Code (hosted HTTP)

    bash
    claude mcp add --transport http desearch https://mcp.desearch.ai/mcp \ --header "Authorization: ${DESEARCH_API_KEY}"

    If your CLI build expects a different header flag, use the equivalent custom-header option and keep the same key value.

    Cursor (~/.cursor/mcp.json)

    json
    { "mcpServers": { "desearch": { "url": "https://mcp.desearch.ai/mcp", "headers": { "Authorization": "<DESEARCH_API_KEY>" } } } }

    VS Code (user mcp.json / servers)

    json
    { "servers": { "desearch": { "type": "http", "url": "https://mcp.desearch.ai/mcp", "headers": { "Authorization": "<DESEARCH_API_KEY>" } } } }

    Generic HTTP shape

    json
    { "url": "https://mcp.desearch.ai/mcp", "headers": { "Authorization": "<DESEARCH_API_KEY>" } }

    Verify

    1. Restart or reload the MCP session in the client.
    2. Confirm the Desearch server shows as connected.
    3. Confirm tools appear from live tools/list. Inventory can change. Do not hard-code a tool count.
    4. Call a simple tool such as web-search with {"query":"desearch"} or ai-search with {"prompt":"desearch docs"}.

    Health

    GET https://mcp.desearch.ai/health returns 200 JSON when the host is up (ok, version, transport, endpoint, and an auth string that states initialize / tools/list are public and tools/call needs a key). GET https://mcp.desearch.ai/api/health returns the same body. This is a probe only; it is not a substitute for a keyed tools/call.

    Troubleshoot

    SymptomCheck
    Error / unauthorized on tools/callSend Authorization: <key> (Bearer <key> or x-api-key also work); discovery (initialize / tools/list) does not need a key
    Server missingConfig path for that client; restart client
    Tools list emptyNetwork to mcp.desearch.ai; key scope
    npm install failuresUse the hosted HTTP endpoint

    npm / stdio

    Use the hosted endpoint at https://mcp.desearch.ai/mcp. That is the recommended setup.

    Keys are created in Console. See Auth and API keys.

    Tools available on the host

    Always prefer your client's live tools/list. Inventory and titles can change; do not hard-code a tool count.

    #ToolTitle
    1ai-searchAI Search
    2x-searchX Search
    3web-searchWeb Search
    4web-links-searchWeb Links Search
    5x-links-searchX Links Search
    6x-posts-by-urlsGet X Posts by URLs
    7x-post-by-idGet X Post by ID
    8x-posts-by-userSearch X Posts by User
    9x-post-retweetersList X Post Retweeters
    10x-user-postsGet X User Timeline
    11x-user-repliesGet X User Replies
    12x-post-repliesGet X Post Replies
    13extractExtract Page Content
    14web-crawlCrawl Web Page (Legacy)
    15x-trendsGet X Trends

    People Search tools

    Confirm these People Search tools in your client's live tools/list.

    ToolTitleBilled
    people-searchPeople SearchYes. Reserves count people at the platform's rate and settles to the people delivered
    people-search-statusGet People Search StatusNo
    people-search-controlControl People SearchNo
    people-search-tasksList People Search TasksNo
    people-profilesList People FoundNo

    Example: ai-search

    Required field is prompt (not query).

    Optional tools uses short ids. Use web and twitter. Schema default when omitted: ["web", "twitter"]. Do not send youtube. It is not in the ai-search tools enum.

    json
    { "name": "ai-search", "arguments": { "prompt": "latest AI search API notes", "result_type": "LINKS_WITH_FINAL_SUMMARY", "tools": ["web", "twitter"], "model": "NOVA" } }

    Expected shape when result_type is LINKS_WITH_FINAL_SUMMARY: search results with HTTP links, a completion/summary field, and usage/cost fields. When tools includes both web and twitter, a successful call can also include a tweets array of X posts in the same JSON body alongside search. An empty search or tweets array (or a tool_no_results warning) means that tool finished with no hits. Do not assume both arrays are always filled on every SUMMARY call. result_type: ONLY_LINKS returns a JSON object with a search array of link objects (title, link, snippet, highlights) and no completion. An empty search array means the search finished with no hits.

    Example: x-search

    json
    { "name": "x-search", "arguments": { "query": "desearch", "count": 3 } }

    Expected shape when the call succeeds: post payloads.

    Example: web-search

    json
    { "name": "web-search", "arguments": { "query": "desearch web search API" } }

    Expected shape when the call succeeds: a data array of link results.

    Example: x-links-search

    json
    { "name": "x-links-search", "arguments": { "prompt": "desearch", "count": 10 } }

    Use count of at least 10 when you set it. Expected shape includes miner tweet payloads when the call succeeds.

    Examples: extract and web-crawl

    json
    { "name": "extract", "arguments": { "url": "https://www.desearch.ai", "format": "text" } }
    json
    { "name": "web-crawl", "arguments": { "url": "https://www.desearch.ai", "format": "text" } }

    Example: web-links-search

    Live schema expects prompt plus tools. The only accepted short id is web (default ["web"]). Other short ids are rejected. If you set count, send an integer from 10 to 200.

    json
    { "name": "web-links-search", "arguments": { "prompt": "open source browser automation tools", "tools": ["web"], "count": 10 } }

    Expected shape when the call succeeds: a JSON object with search_results, a list of link objects. An empty list means the search finished with no hits. This tool does not search X. For X post links use x-links-search.

    Truncated item (placeholders, not a live URL):

    json
    { "search_results": [ { "title": "Example result", "link": "https://example.com/result", "snippet": "Short source text.", "highlights": ["Short source text."], "published_date": "2026-10-01T00:00:00" } ] }

    Other X tools (name + arg hints)

    Follow live tool schemas. Argument names:

    ToolArg hints
    x-posts-by-urlsURL list per live schema
    x-post-by-idpost id
    x-posts-by-useruser
    x-post-retweeterspost id (empty users list can be valid)
    x-user-postsusername
    x-user-repliesuser
    x-post-repliespost_id. Returns a best-effort sample of replies as a single page, not the full thread, with no cursor or total. An empty array can be valid.

    Example: people-search

    Required fields are platform (x, youtube, linkedin, or tiktok) and query. Optional: count (5 to 200), min_score, exclude, idempotency_key, metadata (0.3.0 and later, below), and the platform's filters as top-level arguments, such as min_followers or verified_only on X and require_contact on YouTube and TikTok. A filter that does not apply to the platform is rejected before the billed call.

    json
    { "name": "people-search", "arguments": { "platform": "x", "query": "Indie hackers building in public who share their MRR", "count": 10 } }

    Expected shape when the call succeeds: a JSON object with task_id, status (queued), reserved_cost_usd, and a next hint to poll. The search runs in the background, usually for 1 to 5 minutes. Send the same idempotency_key to retry without starting, or paying for, a second search.

    Then call people-search-status with {"task_id": "<task_id>"}. It returns the status, progress, cost, and the people found so far, each with handle, url, audience, location, score, why, reasons, contacts, links, and evidence_url. Pass next_cursor back as cursor to get only the people found since the last call. When done is true and has_more is false, you have everyone the search found. people-search-control takes task_id and action (pause, resume, or cancel).

    CSV and JSON Lines exports are not MCP tools. Call GET /desearch/people/tasks/{task_id}/export on the REST API. Flow and billing: People Search.

    Why a search ended and what each person confirms (0.3.2 and later)

    From desearch-mcp-server 0.3.2, an ended task in people-search-status, people-search-control, and people-search-tasks carries completion_reason, coverage_limited (only when it is true: a budget stopped the search before count, so more people may match), and warnings (each code, platform, attempted, failed, and by_reason). Each person carries fully_verified and unverified_criteria: up to five criteria their own profile or posts do not confirm, each marked (unknown), (not met), or (applies) for a disqualifier that applies. A person whose profile could not be read after search carries enrichment: "failed" and enrichment_error. The next hint says when a budget stopped the search early, or when it found nobody to check or nobody who fit.

    json
    { "status": "completed", "done": true, "completion_reason": "count_reached", "warnings": [ {"code": "partial_enrichment", "platform": "linkedin", "attempted": 20, "failed": 7, "by_reason": {"not_found": 7}} ], "people": [ { "handle": "jane-doe", "score": 82, "fully_verified": false, "unverified_criteria": ["Topic signal: posts about backend development (unknown)"] } ] }

    The output above is shortened. What each field means: the Reading results section of People Search.

    Label searches with metadata (0.3.0 and later)

    From desearch-mcp-server 0.3.0, people-search takes an optional metadata object: your own string labels for the search, such as the campaign or workspace it belongs to. The search itself does not use them.

    json
    { "name": "people-search", "arguments": { "platform": "x", "query": "Indie hackers building in public who share their MRR", "count": 10, "metadata": {"campaign": "q4-outbound", "workspace_id": "ws_42"} } }

    A search takes up to 10 keys. Keys are 1 to 40 characters of a-z, 0-9, _, ., or -, and values are strings of up to 200 characters. The tool rejects a bad label before the billed call. people-search, people-search-status, people-search-control, and people-search-tasks return the labels as metadata when the task has any.

    To find those searches later, pass up to 3 labels to people-search-tasks. A task must match all of them exactly:

    json
    { "name": "people-search-tasks", "arguments": { "metadata": {"campaign": "q4-outbound"} } }

    Keep the same metadata filter when you pass cursor to get more results.

    Example: x-trends

    Hosted MCP lists x-trends. Schema: required woeid; optional count in 30 to 100.

    json
    { "name": "x-trends", "arguments": { "woeid": 23424977, "count": 30 } }

    Successful tool result is a JSON object with a trends array (name / query / rank), plus location metadata. Do not pass count below 30.

    Connect

    • Connect over MCP (hosted HTTP) with your Desearch API key.
    • Endpoint: https://mcp.desearch.ai/mcp.
    • See live tools/list for the current tool inventory.