MCP server
Hotel prices as tools for your agent.
Point Claude Code, Cursor, VS Code or any MCP client at api.directrate.dev/mcp with your API key. Sixteen tools, each one the REST endpoint it names, billed exactly as that endpoint is — no surcharge, one allowance.
1 · Paste this into your agent
Read https://directrate.dev/mcp/install.md and set up the directrate MCP server for me.
The agent reads install.md — where the key comes from, each client's config, how to check it — and does the rest.
2 · Or add it by hand
Settings → Connectors → Add custom connector
Name: directrate
URL: https://api.directrate.dev/mcp
→ Add → Connect → sign in to directrate → choose the API key it may use → Allowclaude mcp add --transport http directrate https://api.directrate.dev/mcp --header 'x-api-key: ${DIRECTRATE_API_KEY}'{
"mcpServers": {
"directrate": {
"url": "https://api.directrate.dev/mcp",
"headers": {
"x-api-key": "${env:DIRECTRATE_API_KEY}"
}
}
}
}{
"servers": {
"directrate": {
"type": "http",
"url": "https://api.directrate.dev/mcp",
"headers": {
"x-api-key": "${input:directrate-key}"
}
}
},
"inputs": [
{
"id": "directrate-key",
"type": "promptString",
"description": "directrate API key",
"password": true
}
]
}{
"mcpServers": {
"directrate": {
"command": "npx",
"args": [
"-y",
"@directrate/mcp"
],
"env": {
"DIRECTRATE_API_KEY": "<your key>"
}
}
}
}- Claude.aiClaude.ai, Claude Desktop and the Claude mobile apps sign in with OAuth: no key to paste. The consent page creates a key for the connector (or you pick one); revoke that key in the dashboard to disconnect. ChatGPT: Settings → Connectors → Create, the same URL, the same sign-in.
- Claude CodeOne command. Keep the single quotes: Claude Code stores the header as written and expands ${DIRECTRATE_API_KEY} each time it starts, so the key never lands in ~/.claude.json.
- Cursor~/.cursor/mcp.json, or .cursor/mcp.json in the project. Cursor fills ${env:DIRECTRATE_API_KEY} from the environment it was started in.
- VS Code.vscode/mcp.json. VS Code asks for the key once and keeps it in its secret store.
- Windsurf / stdioFor clients that only run local stdio servers (Windsurf, older Cursor, Claude Desktop without a connector): the @directrate/mcp package on npm bridges to the HTTP endpoint and forwards everything unchanged. The key sits in the file's env block (no environment expansion there), so keep the file private.
Every snippet reads the key from DIRECTRATE_API_KEY or a prompt; none contains one.
3 · Ask
- “What does Hyatt Place Chicago (CHIZP) cost on 10 November, and is the member rate lower?”
- “Is any OTA undercutting Clarion Elmhurst (Choice IL263) next Friday?”
- “Which Marriott within 2 miles of the Loop is cheapest in points that week?”
The model picks the tool: hotels to find the code, shop or award for one night, parity for the OTAs, compset for a set, calendar for a window. Every answer states the credits it cost.
Before step 1
A key from the dashboard. The Free plan gives 250 credits a month without a card — about 250 hotel nights on the 1-credit chains.
The tools
Sixteen tools. Each one is a REST endpoint, priced as that endpoint.
A tool call is replayed through the API's own /v1 pipeline, so the key, the rate limit, the quota and the meter run once per call. The result carries credits_charged — the number X-Credits-Charged shows on REST.
shopPOST /v1/shopEvery rate plan of one hotel night: public, member, after tax.
The source's shop weight (1 credit on most; radisson 3, bestwestern 5, wyndham 6); cached 0.25×.
awardPOST /v1/awardThe same night in points, with cents-per-point and certificates.
As shop.
parityPOST /v1/parityThe direct rate beside Booking.com, Expedia, Hotels.com, Trip.com, Agoda.
The direct shop + 1 per channel; +1 when an id was resolved live.
calendarPOST /v1/calendarLowest per night over up to 60 nights.
0.1–0.25 a night on priced-calendar chains at los 1; the shop weight per live night elsewhere.
compsetPOST /v1/compsetUp to 25 hotels of one chain, lowest each.
0.25–0.5 per hotel returned, minimum 1 a call.
stayPOST /v1/stayA 2–14 night stay as one vs its nights alone.
The shop weight + the calendar nights.
hotelsGET /v1/hotelsThe chain's hotel codes around a place.
1 a search (wyndham, bestwestern: the shop weight); a repeat within 24 h 0.25×.
hotelGET /v1/hotelName, address, rating, amenities for one code.
0 from the table; 1 for a live detail fetch.
matrixPOST /v1/matrixUp to 200 hotels × 366 nights as a batch job.
The job's worst case reserved up front; cells from the store at the cache-hit rate.
jobGET /v1/jobs/{id}A batch job's progress and spend.
Free.
job_cellsGET /v1/jobs/{id}/cellsA job's cells, paged.
Free.
watchesGET /v1/watchesThe key's watches and what they cost a day.
Free.
watch_createPOST /v1/watchesRefresh nights on a schedule, webhook on a condition.
Credits_per_refresh, every refresh, while active.
watch_deleteDELETE /v1/watches/{id}Stop a watch.
Free.
chainsGET /v1/chainsEvery source with its credit weights and capabilities.
Free.
usageGET /v1/usageCredits used and remaining this month.
Free.
What an answer looks like
A line for the model, the JSON for your code.
The first line is what the model reads; structuredContent is the same JSON the REST endpoint returns, with credits_charged and request_id added.
shop · hyatt CHIZP · 2026-11-10
Hyatt Place Chicago/Downtown-The Loop · 2026-11-10 · 1 night(s): available; lowest public 621.97 USD, member 609.53; 58 rate plans
1 credit charged · request_id a49409acfb35897d{
"chain": "hyatt",
"hotel": {
"code": "CHIZP",
"name": "Hyatt Place Chicago/Downtown-The Loop"
},
"checkin": "2026-11-10",
"los": 1,
"status": "available",
"rates": [
"… 58 rate rows"
],
"lowest": {
"bar": 621.97,
"member": 609.53,
"bar_after_tax": 734.26,
"member_after_tax": 719.57
},
"source": "live",
"credits_charged": 1,
"request_id": "a49409acfb35897d"
}hotels · ihg · “Chicago, IL” · limit 3
ihg: 3 hotels of 54 (more pages) — CHIAM Prospect Heights; CHIAR Chicago NW - Arlington Heights; CHIBB Bolingbrook
1 credit charged · request_id 8c1f…{
"chain": "ihg",
"supported": true,
"hotels": [
{
"code": "CHIAM",
"name": "Prospect Heights",
"distance_mi": 21.4
},
"…"
],
"total": 54,
"next_cursor": "…",
"credits_charged": 1,
"request_id": "8c1f…"
}A refused call is a tool error, not a crash: isError, the envelope with error.code and request_id, and retry_after_sec on a rate limit. Bad arguments never reach /v1 and cost nothing.
FAQ
MCP, briefly
What does a tool call cost?
Exactly what the REST call costs: the gateway replays a tool call through its own /v1 pipeline, so the key middleware, the rate limit, the quota check and the meter run once per call and the result carries credits_charged — the same number X-Credits-Charged would show. There is no MCP surcharge and no separate quota; REST, the Apify Actor and MCP draw on one allowance. A cached answer is 0.25×, errors and unknown codes are free, and chains and usage cost nothing.
Which clients work today?
Two ways in. Sign in: Claude.ai, Claude Desktop, Claude mobile and ChatGPT add the URL as a custom connector and sign in with OAuth (dynamic client registration, PKCE) — the consent page binds the connection to one API key of yours. A header: Claude Code, Cursor, VS Code, Windsurf, the Claude Agent SDK, LangChain and OpenAI Agents adapters, and anything that runs the @directrate/mcp package from npm send x-api-key. Both are the same key, the same credits.
Is it stateless? Does it stream?
Stateless: one JSON-RPC request, one JSON answer, no session id, no server-sent events (GET /mcp answers 405). That is what lets it run on the same edge Worker as the API, with the same latency, in every region. A long calendar on a slow chain can take over a minute; raise the client's request timeout rather than retrying, which would shop it twice.
How does the model know what a call will cost before it makes it?
The server's instructions state the billing rules at initialize, every tool's description states its own price, and the chains tool (free) gives the per-source weights — the model is told to read chains and usage before a wide calendar or a large comp set. The quota is checked before a call, so a request that would exceed it is refused with quota_exceeded and nothing is charged.
Can I use the same key for REST and MCP?
Yes — it is the same key and the same account. Mint one per client in the dashboard (up to five), name it by the thing that uses it, and revoke it there when that thing goes away; revocation reaches every edge within about ten seconds. A connector that signed in holds a token for one of those keys, so revoking the key disconnects it too.
What does the OAuth sign-in give the connector?
A short-lived access token (an hour, refreshed automatically) that stands for one API key you chose on the consent page — nothing more than that key could do. The token is bound to this server (the resource is https://api.directrate.dev/mcp), refresh tokens rotate on every use and a replayed one revokes the whole grant. Your sign-in is the same magic link or Google as the dashboard; no password exists.