MCP Server
Call core.today's AI models with 12 tools directly from Claude, Cursor, VS Code, and other MCP clients.
What is MCP
The Model Context Protocol (MCP) lets AI clients call external tools in a standardized way. The core.today MCP server reuses the exact same credit-deduction and usage-logging path as the REST API — from model search through prediction creation and polling.
Endpoint
https://api.core.today/v1/mcpA single Streamable HTTP endpoint. It is stateless — no session state lives on the server, so requests can land on any of several ECS tasks and still work.
Authentication
Authenticate with either header — there is no functional difference. A reseller's end-customer can connect with a phantom token minted for them (predictions:create scope) instead of an API key; spend is then charged to that customer's wallet, not the team pool.
X-API-Key
X-API-Key: cdt_your_api_keyAuthorization: Bearer
Authorization: Bearer cdt_your_api_keyConnect
Pick an API key and install it into the client you use.
Choose a key. Until then the command shows a placeholder key.
Add the Core.Today MCP server to Cursor settings in one click.
Set up manually
{
"mcpServers": {
"coretoday": {
"url": "https://api.core.today/v1/mcp",
"headers": {
"X-API-Key": "cdt_YOUR_KEY"
}
}
}
}Verify the connection
Ask Claude
What's my core.today balance?get_balance is free, read-only, and instant, so this one line proves the whole auth path works end to end.
Your first prompt
Make an image with core.today. First use search_models to find a cheap model, then estimate_credits to tell me the cost, then go ahead.Without this, Claude will call create_prediction on the first model it notices.
Money and safety
- create_prediction draws on your team's shared credits — any teammate's Claude can spend the team balance.
- An API key's "allowed models / allowed IPs" settings are not enforced on the MCP path. The only guardrail that's actually enforced is the rate limit.
- Failed calls are refunded automatically, but successful ones are not. A video model can cost 7,400+ credits per call, and the daily free allowance is 50.
Connection check
After installing, ask your client (e.g. Claude): "What's my core.today balance?" — get_balance is free and read-only, so it responds immediately and proves auth worked end to end.
To verify without a client, call tools/call directly with curl:
curl -X POST https://api.core.today/v1/mcp \
-H "X-API-Key: cdt_your_api_key" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "get_balance", "arguments": {} }
}'Tool reference
All 12 tools operate within the calling credential's permission scope (an API key = full team scope; a phantom token = only its minted scopes). The cost badge shows what's consumed: credits deducted, storage quota only, or nothing (rate limit only).
| Tool | Cost | Params | Returns |
|---|---|---|---|
search_modelsFind models | No credits | query?, category?, limit? (1-100, default 20) | { models[], total_matched, categories[] } |
get_model_schemaCheck model spec | No credits | model_id | { input_schema, pricing, example_input, output_type } |
estimate_creditsPreview cost | No credits | model, input? | { credits, pricing_type, ... } — no side effects |
create_predictionGenerate image/video | Uses credits | model, input, is_public?, output_folder? | { job_id, status, ... } — spends credits up front |
get_predictionCheck result | No credits | job_id | { status, output_files[], ... } |
cancel_predictionCancel generation | No credits | job_id | { status: "canceled" } |
create_upload_urlUpload a file | No credits · storage quota | filename, folder?, is_public? | { upload_url, upload_fields, file_url, object_key, curl_example } |
import_file_from_urlImport file from URL | No credits · storage quota | url (max 50MB), filename?, folder?, is_public? | { object_key, file_url, folder, filename, content_type, size_bytes } |
get_balanceCheck balance | No credits | (none) | { team_id, team_name, credits } |
search_documentsSearch documents | Uses credits | database_uid, query? (OpenSearch DSL), where? (simple filter), size? (1-100, default 10), sort? | { total, hits: [{ id, score, data, _meta }], took_ms } |
get_documentGet document | Uses credits | database_uid, doc_id | { id, data, _meta } |
upsert_documentUpsert document | Uses credits | database_uid, doc_id, data | { id, data, _meta, result: "created" | "updated" } |
Workflow example
The typical generation flow is search → check schema → preview cost → create → poll.
- search_models to find a model matching your requirements.
- get_model_schema to check the input schema and exact pricing.
- estimate_credits to preview the cost before running (no side effects).
- create_prediction to start the job — credits are deducted up front and a job_id is returned.
- Poll get_prediction(job_id) every 2-5 seconds until status is completed/failed/canceled.
// 1) search_models
search_models({ query: "product photo background removal" })
// 2) get_model_schema — inspect the chosen model's input schema
get_model_schema({ model_id: "<chosen model_id>" })
// 3) estimate_credits — preview cost, no side effects
estimate_credits({ model: "<chosen model_id>", input: { ... } })
// 4) create_prediction — spends credits, returns job_id
create_prediction({ model: "<chosen model_id>", input: { ... } })
// → { "job_id": "job_xxx", "status": "processing", ... }
// 5) get_prediction — poll every 2-5s until terminal status
get_prediction({ job_id: "job_xxx" })
// → { "status": "completed", "output_files": [ { "object_key": "...", "url": "..." } ] }Choosing a file input method
When a model input needs a file, pick the method that fits your situation.
create_upload_url
Use this for a local file. Get a presigned URL, upload it with the returned curl_example, then use file_url as the create_prediction input.
import_file_from_url
Use this when the file already lives at a public URL. The server fetches it for you and stores it in team storage — private/internal addresses are blocked and there's a 50MB cap.
Pass the URL directly
If the model's input field accepts a URL string directly, you can skip the upload tools entirely and pass the public URL as-is.
Error codes
When a tool call fails, the ToolError message is formatted as "code: message". Agents should branch on the code before the colon — the message is a human-readable description and its wording may change.
| code | Meaning |
|---|---|
| insufficient_credits | The team is out of credits. Top up, or use estimate_credits to find a cheaper model. |
| rate_limited | The API key's per-minute/hour/day budget was exceeded. Retry after the wait time (seconds) named in the message. |
| not_found | The requested resource (model, job_id, etc.) wasn't found. Use search_models to confirm the exact model_id. |
| validation_error | The input doesn't match the model's input_schema. Use get_model_schema to check the exact schema. |
| internal_error | An unexpected server-side error occurred. It was logged internally — retry shortly. |
insufficient_credits: Required 12.50, available 3.20
rate_limited: per_minute limit reached (60/window). Retry after 42s.
not_found: model 'xyz/bad-model' not found. Try search_models first.
validation_error: input.prompt: field required
internal_error: unexpected server error (logged)Limits
- Rate limiting: Read-only tools (search_models, get_prediction, etc.) consume 1 unit of the API key's per-minute budget per call. create_prediction already runs rate limiting on the prediction path itself, so the gate doesn't count it separately — no double charge. The initialize/tools/list handshake isn't charged.
- Stateless: The server runs stateless Streamable HTTP (any of several ECS tasks behind the ALB can answer). GET isn't supported and returns 405 — only POST/DELETE are allowed.
- Output URL expiry: The url in output_files returned by create_prediction/get_prediction is a signed, expiring URL. object_key is the durable identifier — store it and re-sign later when needed.
- import_file_from_url limits: File size is capped at 50MB, and private/internal network addresses are blocked (SSRF prevention).
- LLM chat: Chat-style LLM calls (OpenAI/Anthropic-compatible) go through a separate LLM gateway (/llm/openai/v1, /llm/anthropic/v1), not the MCP tools.
Watch out for key exposure