MCP Server
Call core.today's AI models with 9 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.
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 9 tools operate within the calling API key's permission scope. 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 } |
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