Protocols

MCP server

AI agents use your app through tools made from its own surface. No install, no plugin.

Every Yatta app is also an MCP server, on /mcp. MCP is the standard way AI agents (coding assistants, chat apps, automation tools) use an app: the client asks for the tool list, then calls tools. Yatta builds that list from your app itself — you write nothing extra.

List tools

The caller must be signed in — a Yatta session, or an OAuth access token from this same server. Without that, the answer is 401, and the caller learns nothing, not even the tool names.

Ask for the tool listts
curl http://localhost:4000/mcp \  -H "Authorization: Bearer ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' # Answer (shortened){  "jsonrpc": "2.0", "id": 1,  "result": { "tools": [    { "name": "list_posts", "description": "List rows in the posts collection." },    { "name": "get_posts", "description": "Get one row from the posts collection by id." }  ] }}

The five collection tools

Every collection becomes five tools, named after the collection itself. A collection called posts becomes list_posts, get_posts, and so on.

list_<name>
List rows. Arguments: limit (default 50, at most 100), offset (default 0).
get_<name>
One row by id. Argument: id (string or number).
create_<name>
Create a row. Argument: fields, the row to create.
update_<name>
Change a row by id. Arguments: id, fields. Answers { updated: true/false }.
delete_<name>
Delete a row by id. Argument: id. Answers { deleted: true/false }.

Call a tool

Send tools/call with the tool name and its arguments. The answer comes back as text. A tool that fails answers with isError: true instead of crashing the server.

Create a post, then read it backts
curl http://localhost:4000/mcp \  -H "Authorization: Bearer ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",       "params":{"name":"create_posts",       "arguments":{"fields":{"title":"Hello","body":"First post"}}}}' # Answer (shortened){  "jsonrpc": "2.0", "id": 2,  "result": { "content": [{ "type": "text",    "text": "{\"id\":1,\"title\":\"Hello\",\"body\":\"First post\"}" }] }}

Your own functions

A function you register becomes one tool under its own name, and it receives the caller’s user id — so it can enforce its own rules, like “only touch your own rows”. A function whose name collides with a collection tool is skipped, so every tool name stays unique.

Call a function toolts
curl http://localhost:4000/mcp \  -H "Authorization: Bearer ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call",       "params":{"name":"my_function","arguments":{"text":"hello"}}}'

Start a session

There is no session id to keep. Every request stands on its own. Say hello with initialize, and check the connection with ping.

Initialize and pingts
curl http://localhost:4000/mcp \  -H "Authorization: Bearer ACCESS_TOKEN" \  -H "Content-Type: application/json" \  -d '{"jsonrpc":"2.0","id":4,"method":"initialize",       "params":{"protocolVersion":"2025-06-18"}}' # Answer{  "jsonrpc": "2.0", "id": 4,  "result": {    "protocolVersion": "2025-06-18",    "capabilities": { "tools": {} },    "serverInfo": { "name": "yatta", "version": "1.0.5" }  }} # Ping — answers an empty result# {"jsonrpc":"2.0","id":5,"method":"ping"} -> {"jsonrpc":"2.0","id":5,"result":{}}

A request without an id is a notification: it answers 202 with an empty body. You can also send several requests at once, as a JSON array, and get several answers back.

Errors

-32700 (HTTP 400)
The body is not JSON.
-32600
Not a JSON-RPC 2.0 request, or no method.
-32601
Unknown method.
-32602
Unknown tool, or no tool name given.
isError: true
The tool itself failed. The message is in the answer text; the server stays up.
HTTP 405
Anything that is not a POST.

Turn it off

The endpoint is on by default. One line turns it off.

Disable the MCP endpointts
MCP_ENABLED=0
Warning
Auth tables are never turned into tools: users, sessions, identities, verificationTokens, passkeys, and apiKeys are always left out, even if you ask for them. One honest limit: any signed-in user can use the exposed collection tools. If a row should only be touched by its owner, put that rule in a registered function — it gets the user id and can say no.