MCP server
Fitaio runs a remote Model Context Protocol server at https://fitaio.app/mcp. Point an MCP client at it with your API key and the assistant can read your workouts, body metrics, nutrition, plans and stats, and log new entries when the key has write scope.
The server is stateless Streamable HTTP. Every request carries your key; there are no sessions to keep alive and no server-initiated streams.
The endpoint
| URL | https://fitaio.app/mcp |
| Transport | Streamable HTTP, POST only |
| Protocol versions | 2025-11-25, 2025-06-18, 2025-03-26 |
| Auth | Authorization: Bearer fitaio_... header, or the key in the URL (below) |
| Limits | Same as the REST API: 120 requests a minute per key, 300 per IP |
Clients that send headers
Claude Code, Cursor and VS Code let you attach headers. Use the plain endpoint with a bearer token:
{
"url": "https://fitaio.app/mcp",
"headers": { "Authorization": "Bearer fitaio_YOUR_KEY" }
}Clients that cannot send headers
Claude Desktop connectors and ChatGPT connectors take a URL only. Put the key in the path:
https://fitaio.app/mcp/k/fitaio_YOUR_KEYTreat that URL like a password. Anyone who has it can use the key until you revoke it on the Developer screen in the app.
Set up your client
Tools
The server exposes 22 tools: 14 that read and 8 that write. Read tools work with any key; write tools need a key created with read and write scope. The tool reference lists every argument with an example call and result.
Good prompts
- "What did I train this week, and which lifts moved up?"
- "Log today's push workout: bench 4 sets of 8 at 82.5 kg, overhead press 3 sets of 8 at 50 kg."
- "Compare my volume per muscle group over the last 30 days with the 30 before."
- "Set a goal of 78 kg by 19 December and log 82.4 kg for this morning."
- "Build me a four-day plan for a cut and make it active."
Protocol notes
initializereturns the server's protocol version and thetoolscapability.tools/listreturns every tool with a JSON Schema for its arguments and annotations (readOnlyHint,destructiveHint,idempotentHint).tools/callresults carry the JSON instructuredContentand a one-line summary incontent[0].text.- Errors from calling a tool (validation, not found, insufficient scope) come back as a tool result with
isError: trueand a plain-text explanation incontent[0].text, not the REST API's structurederrorobject; a failed tool call has nostructuredContent. Errors in the request itself, such as an invalid or missing key, are HTTP-level responses that use the same{ error: { code, message } }envelope as the REST API. GET /mcpanswers 405: there is no SSE stream to open.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
HTTP 401 with invalid_key |
The key is wrong, revoked, or the client did not send the header | Check the Developer screen; for URL-only clients use the /mcp/k/ form |
Tool call returns isError: true with a scope message |
A write tool was called with a read-only key | Create a key with read and write scope |
HTTP 429 rate_limited |
More than 120 calls in a minute | Wait a minute; ask the assistant to batch reads |
| Tools list is empty in the client | The client cached an old connection | Remove and re-add the server |