X-API-Key header to remove the hourly request limit and raise rate limits. See Authentication and Rate limits.
Which URL to use
Usehttps://api.keenable.ai/mcp unless your client signs in through a browser. It works with no key or with an X-API-Key header, which covers Claude Code, Cursor, Codex with a key and most config files. Use https://api.keenable.ai/mcp-oauth only for a client that signs in, such as a custom connector in Claude: it asks every caller that sends no credential to sign in to Keenable, so the client opens the sign-in page. All three URLs serve the same two tools.
Installation
One-click install
Add Keenable to Cursor, VS Code or Replit with one click:Install via CLI
For local coding agents, the Keenable CLI sets up the MCP server for you. Once you’ve installed the CLI, run this command and confirm the change:Claude Code
Add the Keenable MCP server with the Claude Code CLI:--header flag to run on the public tier instead.
Claude Code ships its own WebSearch and WebFetch tools and picks between them and Keenable from one lookup to the next. To leave Keenable as the only search, add "permissions": { "deny": ["WebSearch", "WebFetch"] } to ~/.claude/settings.json. See Claude for the details and how to undo it.
Codex
Add the server to~/.codex/config.toml:
http_headers line and change the URL to https://api.keenable.ai/mcp-public. /mcp publishes OAuth discovery metadata, which can make Codex start a browser sign-in when it has no key. See Keyless endpoint for listings and presets.
Codex ships its own web search, turned on by default, and keeps using it instead of Keenable. The Keenable tools show up in
/mcp, but Codex calls them only when you ask for them by name. To fix this, add web_search = "disabled" to the same file, above the [mcp_servers.keenable] line. It’s a top-level key: if you write it under a table header, it belongs to that table and Codex never reads it. See Codex for the finished file, the other modes and an AGENTS.md snippet.Other MCP clients
Most other clients connect to the same remote URL. Each one has its own config path and its own name for the URL key:
Most clients take this shape. Replace
url with serverUrl or httpUrl if the table lists that key:
headers block to run on the public tier instead. If a client fails on OAuth discovery, as Antigravity and some marketplace presets do, use https://api.keenable.ai/mcp-public instead. See Keyless endpoint for listings and presets.
If your client has its own web tools, such as WebSearch, WebFetch, brave_search or tavily_search, turn them off in the client settings. With both sets active, the agent picks between them inconsistently from one call to the next.
Keyless endpoint for listings and presets
If you ship Keenable to your users with no key, for example in a preset, a catalog entry or a docs example, use this URL:/mcp-public, the search_web_pages schema doesn’t list the mode parameter. Keyless calls get the same public tier: 1,000 requests per hour and up to 10 per second per IP address (see Rate limits). The difference is that /mcp-public publishes no OAuth discovery metadata. Clients that read discovery before anything else, such as Google Antigravity, connect right away instead of offering a sign-in.
An X-API-Key header works on /mcp-public the same way as on /mcp and lifts the hourly limit. If the server doesn’t accept the key, it returns 403, not 401, so the client shows the error instead of offering a sign-in. Use /mcp for setups where users add their own key, and /mcp-oauth for setups where they sign in.
OpenAI API (Responses API)
Use this to call the MCP server from your own code through OpenAI’s Responses API. To add Keenable inside the ChatGPT app, use the Plugins Directory listing instead. Pass the server as a remotemcp tool:
headers entry to run on the public tier instead.
Claude (remote connector)
Claude (Desktop, claude.ai, mobile) connects to the remote MCP server as a connector, with no config file.1
Add the connector
Keenable is listed in Claude’s Connectors Directory as Keenable Web Search: open Customize → Connectors, click +, search for it, and click Connect. There is nothing to paste.To run on your own Keenable account, add it by URL instead: Customize → Connectors → + Add → Add custom connector with the URL below, then click Connect and sign in to Keenable. An API key in a request header works too; see Claude.
2
Enable it in a chat
Click + in the lower left of the message box, hover Connectors, and toggle Keenable on for the conversation.
Local bridge (with an API key)
If a client supports only local stdio servers, it needs a bridge to send your key. Claude Desktop’s config file is the common case. The bridge needs Node.js 18 or newer. The@keenable/mcp package runs as a subprocess and forwards requests to the same endpoint:
claude_desktop_config.json). Drop the env block to run the bridge without a key. The same block works in the config of any other stdio-only client.
The bridge connects to https://api.keenable.ai/mcp when your client starts it, and waits up to 10 seconds for an answer. If it can’t connect, for example while you’re offline or when Keenable rejects your API key, it starts anyway and the tools still appear. A tool call then returns an error that starts with Keenable request failed: and gives the reason, such as Malformed API key or fetch failed (ECONNREFUSED), so your agent can tell you what went wrong. Fix the network connection, the proxy or the key in the env block. You don’t need to restart the client: every tool call tries to connect again.
The client’s MCP log shows the same reason in a line that starts with keenable-mcp: could not connect to https://api.keenable.ai:. A session that started without a connection doesn’t get the server’s instructions for the agent. They come back the next time your client starts the bridge.
Node.js doesn’t use the HTTPS_PROXY variable unless you tell it to. If you connect through a proxy on Node.js 22.21 or newer, add "NODE_USE_ENV_PROXY": "1" to the env block next to your proxy variables.
Available tools
The MCP server has two tools.search_web_pages
Search the web and return ranked results with URLs, titles, and snippets.
Never return results from these hosts, such as
["pinterest.com", "quora.com"]. Each host also covers its subdomains. Up to 1,000 hosts. No paths, suffixes such as .gov, or wildcards. A scheme, a leading www. or a trailing / is cleaned up. A single host name as a string also works. A rejected entry returns a tool error that names the entry and the reason. See Exclude domains.Search the index as it stood at this point in time: pages acquired after it are excluded. Accepts a timestamp, a date (a date resolves to
00:00:00 UTC, not to the end of the day), a Unix epoch, or a relative delta such as 7d, which means the index as it stood 7 days ago. See Point-in-time search.Maximum length, in characters, of the snippet returned per result. Must be between 180 and 10000. When omitted, a default snippet length is used.
Maximum number of results to return. Must be between 1 and 50. When omitted, up to 10 results are returned.
Search mode:
pro for higher-quality results or realtime for the fastest results. When omitted, the search runs in pro mode.text for readable text, or json for the raw response serialized as JSON, for when your code parses the fields.ID of the agent session making the call, up to 128 characters. Send the same value on every call in one session so its requests can be grouped.
Set to
true to leave cards out of the results. A card is a result that Keenable writes itself for queries such as prices, exchange rates or facts about a company, at a URL such as https://cards.keenable.ai/w/<id>. A card URL works for one day. With disable_cards, ordinary web pages take the place of cards, so you get the same number of results.Date and time filters
acquired_after, acquired_before, published_after, and published_before each accept one of the following formats:
- Date in RFC 3339
full-dateform (YYYY-MM-DD), which covers that whole day in UTC. On an_afterbound it resolves to00:00:00on that date. On a_beforebound it resolves to23:59:59.999on that date, so pages from the named day are kept at either end. Pass a timestamp instead to cut at an exact instant. - Timestamp in ISO 8601 form (
YYYY-MM-DDTHH:MM:SS[.sss][±HH:MM]). If the timestamp has no timezone offset, the server reads it as UTC. - Relative delta (
<number><unit>, such as7dor30min). It resolves to the request time minus the delta, truncated to the minute, even ifquery_timeis set. Supported units:min(minutes),h(hours),d(days),w(weeks),mo(30 days),y(365 days).
You can use a relative delta on either bound of a window, together with another delta or an absolute value on the other bound. This window keeps pages published between one year and six months ago:
2026-05-18T14:23:45Z, acquired_after: "2h" resolves to 2026-05-18T12:23:00Z. A document acquired at 12:22:59Z is dropped, and one acquired at 12:23:00Z is kept.
On a _before bound, a date and a timestamp cut at different points: acquired_before: "2026-05-01" keeps a page acquired at 2026-05-01T14:31:13Z, while acquired_before: "2026-05-01T00:00:00Z" drops it. Use the date form to mean “up to and including that day”, and the timestamp form to cut at midnight.
Point-in-time search
query_time runs the search against the index as it stood at an instant. A page
acquired after that instant isn’t a candidate at all, so you get the answer the index
would have given then, not today’s answer filtered down. Use it to reproduce an agent
run, to build an evaluation set that doesn’t drift as the index grows, or to ask what
was knowable before an event.
Relative deltas in the other filters still count back from the request time, not from
query_time. To bound results around a past instant, use dates. This request returns
pages published in the 30 days before 13 June 2026, as the index stood then:
query_time resolves to 00:00:00 UTC, which is the opposite end of the
day from a date-only _before bound: query_time: "2026-06-13" cuts at the start of
13 June, while acquired_before: "2026-06-13" keeps all of it.
fetch_page_content
Fetch a URL and extract its content as clean markdown. By default the tool fetches only URLs in the Keenable index. Pass live=true to fetch directly from the source, including URLs that aren’t indexed.
Maximum number of characters of content to return. Longer content is truncated. The MCP default is lower than the API’s 50,000, so a long page fits in an agent’s context; for one detail from a long page, use
prompt instead.Fetch the page live from the source instead of returning Keenable’s indexed copy. Enables fetching URLs that are not indexed.
Optional extraction instruction, at most 2000 characters. When set, an LLM reads the fetched page and the returned content is only the output for this instruction instead of the full page. Example:
List all pricing tiers with their monthly prices.text for readable text, or json for the raw response serialized as JSON, for when your code parses the fields.ID of the agent session making the call, up to 128 characters. Send the same value on every call in one session so its requests can be grouped.
text format, the tool returns the page title, the URL and the content as markdown. If the content is cut at max_chars, a Truncated: line gives the full length. In json format, it returns the response described in the Fetch reference.
Server info
Theinitialize response describes the server for client UIs and MCP directories. Besides name and version, serverInfo carries:
Metadata for integrators
If your platform embeds Keenable’s MCP tools, you can read billing usage and control tool behaviour through the MCP_meta field. _meta travels alongside the tool result or request and isn’t part of the tool content the model sees, so none of it enters the agent’s context. Baseten uses both channels to run the tools as hosted tools on its Model APIs.
Usage metadata (response _meta)
Every billed (authenticated) tool call returns usage under _meta["keenable/usage"], so you can attribute cost per call without parsing the tool’s text output. If a call has no API key, it isn’t billed and the result has no keenable/usage.
true when drawn from purchased credits; false while still on the free monthly allowance. See credits.Credits your organization has left after the call: the unused part of the free allowance for the current billing period plus purchased credits. An estimate that can lag your other calls by a few seconds. Left out if the balance couldn’t be read.
Operator overrides (request _meta)
To control tool behaviour yourself instead of letting the model decide, send overrides under _meta["keenable/overrides"] on a tools/call request. They take precedence over the arguments the model generated.
This request forces realtime mode and skips the cache: