Liquid MCP server
The exchange runs a Model Context Protocol (MCP) server. Add it to an agent, and the agent can look up models and prices, preview a purchase, read your balance and buy answers.
To add it, run liquid mcp claude or follow Connect. Tools that buy charge your balance and need a spend key.
Use the server while you build. In an application, call the REST API.
Connect
The server is at https://router.inference.ai.exchange/mcp. It uses the Streamable HTTP transport and keeps no session.
There are two ways to sign in:
- Sign in with your browser. You choose the workspace, whether the app can buy answers, and the spend limit. Use this when the client supports OAuth.
- Use a key. Send an API key as a bearer token, for a client that cannot open a browser.
Sign in with your browser
Only an owner or admin of the workspace can approve a key. The page shows where the browser goes next. Approve only if you started the request from that app.
The key that the client gets:
- Is named
Liquid MCP: <app name>. Revoke it on the API keys page. - Expires in 7 days. The client then asks you to sign in again.
- Has the spend limit that you set. The default is $10.
- Cannot buy answers unless you tick the box on the consent page. It can still look up models and read the balance and receipts.
The box is unticked for a client that asks for mcp:read, and ticked for mcp:spend. You decide either way.
The Liquid CLI
The Liquid CLI adds the server in one step. Use claude, codex, cursor or opencode. A liquid claude or liquid opencode session has it already.
liquid mcp claude
Claude Code
claude mcp add --transport http liquid https://router.inference.ai.exchange/mcp
claude mcp login liquid
You can also run /mcp in a session, select liquid, and choose Authenticate.
Codex CLI
codex mcp add liquid --url https://router.inference.ai.exchange/mcp
codex mcp login liquid
Run the login on the machine where Codex runs, because Codex receives the code on http://127.0.0.1.
Cursor
Add this to ~/.cursor/mcp.json, then sign in from the MCP settings.
{
"mcpServers": {
"liquid": { "url": "https://router.inference.ai.exchange/mcp" }
}
}
OpenCode
Add this to ~/.config/opencode/opencode.json. OpenCode starts the sign-in when the server answers 401, or when you run opencode mcp auth liquid.
{
"mcp": {
"liquid": {
"type": "remote",
"url": "https://router.inference.ai.exchange/mcp",
"enabled": true
}
}
}
Claude Desktop and Claude on the web
The exchange is not in the connector directory, so add a custom connector.
- Open Customize → Connectors, and choose Add custom connector. On a Team or Enterprise plan, an owner first adds it in Organization settings → Connectors, and each member then chooses Connect.
- Set the name to
Liquid Inferenceand the server URL tohttps://router.inference.ai.exchange/mcp. Leave the OAuth client ID and secret empty. - Choose Add, then Connect, and approve the request in your browser.
Use a key
A key with the spend scope can buy answers. A key that can only view the market can use the other tools. See API keys. Export the key once:
export LIQUID_API_KEY=sk-liquid-...
Claude Code
claude mcp add --transport http liquid https://router.inference.ai.exchange/mcp \
--header "Authorization: Bearer $LIQUID_API_KEY"
Codex CLI
Add this to ~/.codex/config.toml.
[mcp_servers.liquid]
url = "https://router.inference.ai.exchange/mcp"
bearer_token_env_var = "LIQUID_API_KEY"
Cursor and OpenCode
Use the config from above and add the key:
- Cursor: add
"headers": { "Authorization": "Bearer ${env:LIQUID_API_KEY}" }to theliquidentry. - OpenCode: add
"oauth": false, which makes OpenCode use your key, and"headers": { "Authorization": "Bearer {env:LIQUID_API_KEY}" }.
Tools
Each tool calls the REST endpoint of the same name in the API reference, with the same key rules, rate limits and billing.
Tools that read
These tools need any key, unless the table says read.
| Tool | What it does | Key |
|---|---|---|
list_models |
Lists the models that the exchange serves now. | any |
get_model |
Gets one model card: context window, features, prices. | any |
compare_models |
Compares two to five models. Takes models. |
any |
get_book |
Lists the resting offers. | any |
get_markets |
Gets the providers and offers for each model. | any |
get_routers |
Lists router models, such as liquid.auto. |
any |
get_fills |
Lists the newest settled jobs of a model. It never names a buyer. | any |
get_volume |
Gets the tokens and jobs per model, router and preset. | any |
get_throughput |
Gets the measured speed of each provider and model. | any |
get_lifetime |
Gets the jobs settled and tokens processed in all. | any |
count_tokens |
Estimates input tokens. Takes prompt, system. |
any |
dry_run |
Previews a purchase and buys nothing. See Preview a purchase. | any |
get_video |
Gets the status of a video job. See Video. | any |
get_balance |
Gets the workspace balance. | read |
get_limits |
Gets what the key can spend: grade, balance, spend limit and the part used. | read |
get_job |
Gets the receipt of one job. | read |
list_receipts |
Lists the jobs of the workspace, newest first. | read |
list_models and get_book take kind, max_price_usd, limit and offset (see Find a model). get_volume and get_throughput take days (1 to 90). list_receipts takes limit, cursor, and model and outcome as comma-separated lists.
Tools that buy
These tools charge the balance. They need a spend key.
| Tool | What it buys | Arguments |
|---|---|---|
send_message |
One answer. See Buy an answer. | See that section. |
generate_image |
One to ten images. | model, prompt; optionally n, size, quality, output_format, references |
generate_speech |
Spoken audio. | model, input, voice; optionally response_format (mp3, opus, aac, flac, wav or pcm), speed, references |
create_video |
A video job. It returns the job ID. | model, prompt; optionally seconds (at most 20), size, callback_url, references |
transcribe |
A transcript of an audio file. | model, audio_base64; optionally language, prompt, response_format, max_usd_per_second |
decide |
Answers to typed questions. | state, questions; optionally model, orderings |
embed |
Vectors for one text or up to 2,048 texts. | model, input; optionally dimensions, encoding_format |
Every tool that buys also takes cap_usd and the purchasing controls.
Preview a purchase
Call dry_run before send_message. It shows the estimated charge and the winning offer. It places no job and holds no funds.
| Argument | Meaning |
|---|---|
model |
The model to preview. |
input_tokens |
Default 4,000. |
max_output_tokens |
Default 1,000. |
cap_usd, task, tools |
Optional. task is general or coding. |
kind previews a job that is not chat. Send the size of the job with it:
kind |
Size |
|---|---|
image_generation |
n (default 1), optionally size. |
audio_speech |
characters, optionally voice (default alloy). |
video_generation |
Optionally seconds and size. |
audio_transcription |
seconds of audio. |
embedding |
Optionally inputs (default 1) and dimensions. input_tokens sizes each text. |
decision |
questions, optionally orderings (default 4). input_tokens sizes the situation. |
Find a model
kindtakes one kind or a comma-separated list:text,image,video,speech,transcription,embeddingordecision.supports(list_modelsonly) keeps models that support a parameter, such asinput.image.max_price_usdkeeps the offers whose rate is at most that price. It needs exactly onekind. It leaves out offers priced in another unit, and router models.
| Kind | Unit of max_price_usd |
|---|---|
text |
A million output tokens |
embedding, decision |
A million input tokens |
| Other kinds | One image, character or second |
Buy an answer
send_message charges the balance by the tokens used. cap_usd is the most that the job can cost. An offer with a higher worst case does not compete.
The reply has the answer, the usage and the exchange block with the job ID and charge.
send_message takes model and a prompt, messages or both. Optional arguments:
| Group | Arguments |
|---|---|
| Input | system, image_urls (https or data:image URLs), attachments |
| Sampling | temperature, top_p, top_k, min_p, repetition_penalty, frequency_penalty, presence_penalty, seed, logit_bias, stop |
| Output | logprobs, top_logprobs, reasoning_effort, response_format (text, json_object or a JSON schema object), max_tokens |
| Tools | tools, tool_choice |
| Cost and retry | cap_usd, idempotency_key |
messagesholds earlier turns as{role, content}. The role isuser,assistantortool. Thepromptfollows them.- An
assistantturn can holdtool_callsand no content. Atoolturn needs atool_call_id. - An
attachmentis a reference (see Media and references), with inline data for audio, video and files. max_tokensdefaults to 1024, which keeps the hold small.- An argument that no offer supports gets the refusal of the endpoint, and nothing is charged.
When the model calls a tool, the reply has tool_calls. The server does not run them. To continue, send the assistant turn with its tool_calls, then a tool turn with the result.
To retry a call that timed out without a second purchase, send the same idempotency_key. Every tool that buys takes it. The job runs to its end even when the client leaves, so a retry without a key can buy twice.
Other buying tools
decide
decide calls POST /v1/systemone. See the Decisions API.
stateis the situation, as text or data.questionsholds typed questions by ID.modelis a decision model fromlist_models. The default isliquid.decision. Liquid Auto cannot answer decisions.orderingssets how many orderings of the options each question gets. The cap holds one read of the prompt for each ordering.
embed
embed calls POST /v1/embeddings and charges per input token. Vectors are floats, so keep the list short.
transcribe
transcribe charges for each second of audio.
- Send the file as base64 or as a
data:audio/URL inaudio_base64. The limit is 1,000,000 characters, about 750 KB of audio. The tool does not fetch a URL. For a larger file, callPOST /v1/audio/transcriptions.
Images and speech
generate_image and generate_speech return the media and the exchange block.
Media and references
create_video, generate_image and generate_speech take references. generate_image and create_video also take reference_image_urls.
| Argument | Meaning |
|---|---|
image_url |
create_video only. An https or data:image address. The video starts from this frame. |
reference_image_urls |
Images that the output should keep. |
references |
The full form: a list of {type: "image", url}, {type: "audio", data, format}, {type: "video", url} and {type: "file", file_data, filename}. |
callback_url |
create_video only. An https address that the exchange calls when the job ends. |
- A call has at most 16 references. The model must accept image input.
- A request to
/mcpis limited to 20 MB. Base64 is a third larger than the file. For a large file, use anhttpsaddress.
Video
get_video takes video_id and optionally wait_seconds (up to 300), include_file and chunk. A completed job also returns:
download_url, a link that works without a key for 15 minutes.- The video file as an embedded resource, up to 4 MB. A client drops a result that is too big, with the link in it.
For a larger file, open download_url. If there is no link, send chunk (0, 1, and so on). Each chunk is 1 MB as base64, and the reply states chunk_count. A file can be at most 64 MB this way. Send include_file as false to get the status and the link only.
Purchasing controls
The tools that buy, and dry_run, take the x-liquid-* header controls as arguments. A value that cannot be read is a tool error, and nothing is charged.
| Argument | Header | Value |
|---|---|---|
retention |
x-liquid-retention |
none keeps only offers whose provider does not retain the content. allowed is the default. |
ttft_ms |
x-liquid-ttft-ms |
The most time to the first token, in milliseconds. |
tag |
x-liquid-tag |
A label for the spend. At most 63 letters, digits, dots, dashes and underscores. |
rules |
x-liquid-rules |
An object with other rules of the exchange block, such as exchange.privacy.regions. It cannot set exchange.candidates.models.allow or exchange.rank.strategy. |
Reference
Resources and prompts
A resource read needs the same key as its tool. liquid://models, book, markets, routers, balance and lifetime hold the data of the tools with those names. liquid://models/{model}, liquid://book/{model}, liquid://markets/{model} and liquid://fills/{model} hold the data for one model. liquid://jobs/{job_id} holds one receipt.
completion/complete gives model IDs for model and models, and recent job IDs for job_id.
Three prompts write a request and buy nothing: find_cheapest (task, optionally max_usd), audit_spend (optionally days) and compare_models (models).
Widgets
A client that supports MCP Apps shows a widget under some results. The tool names it in _meta.ui.resourceUri. A widget has no button that buys.
Each widget is ui://liquid/<name>: models, model, book, receipts, job, markets, fills, account (get_balance, get_limits), image, speech, video (create_video, get_video) and dry-run. The name follows the tool, such as book for get_book.
The widgets are resources with the media type text/html;profile=mcp-app. The server offers the io.modelcontextprotocol/ui extension.
Results and progress
A result holds the data as text and as structuredContent. A list is the object { "items": [...] }. A failed tool has no structuredContent.
get_video with wait_seconds checks the job until it completes, fails or the time ends. For a notifications/progress message at each check, the client must send _meta.progressToken and accept text/event-stream. A refusal for the grade of the key then arrives as a tool error, not a 403.
The server does not use elicitation. When a purchase is refused with a proposed_retry, the result says to ask the person.
Tool errors
A failed tool returns a result with isError set. It holds the HTTP status and the error body of the endpoint. See Errors.
How the sign-in works
This section is for people who build a client.
- A request to
/mcpwithout a key returns401with aWWW-Authenticateheader. It names the resource metadata (RFC 9728) at/.well-known/oauth-protected-resource/mcp. - That metadata names the authorization server (RFC 8414,
/.well-known/oauth-authorization-server). - The client identifies itself in one of two ways.
- Metadata document. The
client_idis thehttpsaddress of a JSON document, and the server metadata setsclient_id_metadata_document_supportedtotrue.- The address must have a path, no query or fragment, no port, and a public host name.
- The document must name its own address as
client_id, listredirect_uris, and usetoken_endpoint_auth_methodnone. It must be at most 8 KB with a JSON content type. - The exchange reads it at sign-in. It follows no redirect and refuses a host in its own network.
- Registration. The client registers at the
registration_endpoint(RFC 7591). All clients are public:token_endpoint_auth_methodisnone.- Each
redirect_urimust usehttps,httponlocalhost,127.0.0.1or[::1], or an app scheme. - An app scheme is a reverse-DNS name (it has a
.) or one ofclaude,cursor,goose,vscode,vscode-insiders,windsurfandzed.
- Each
- Metadata document. The
- The client opens the
authorization_endpointwithresponse_type=code,code_challenge_method=S256and astate. PKCE is required. Aresource(RFC 8707) must be the address of the MCP server or the router origin, or the exchange returnsinvalid_target. - The
scopeismcp:read,mcp:spend, or both.mcp:spendincludes reading. Any other scope getsinvalid_scope. With no scope, the client asks formcp:read. The token response names the scope that the person granted, which can be less. - Each redirect to the client, success or error, carries
iss(RFC 9207). The client must compare it with the issuer that it recorded and refuse a code on a mismatch. - The client sends the code to the
token_endpointwith thecode_verifier. The response has the key asaccess_token, withtoken_typeBearerandexpires_inin seconds. There are no refresh tokens.
The /mcp route refuses a browser request with an Origin that is not the exchange or a loopback address.
A code works one time and ends after 5 minutes. If a client sends a code twice, the exchange revokes the key that the first request got. A wrong code_verifier uses up the code.
Asking for more access
A 401 carries scope="mcp:read" in WWW-Authenticate. If a key cannot do what a tool needs, the server answers 403 with error="insufficient_scope" and the needed scope, such as scope="mcp:spend". The JSON body still has the tool error.