The endpoint
The server is POST https://api.newtalaria.com/mcp. Locally that is the Serverpod web server at http://localhost:8082/mcp; dashboard RPC stays on port 8080.
The body is JSON-RPC 2.0. The route implements initialize, notifications/initialized, ping, tools/list and tools/call. initialize answers with protocolVersion 2025-03-26, a capabilities object whose only key is tools, and serverInfo.
// POST https://api.newtalaria.com/mcp
{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": { "protocolVersion": "2025-03-26" } }
// 200 OK · Mcp-Session-Id: 9f1c…
{ "jsonrpc": "2.0", "id": 1, "result": {
"protocolVersion": "2025-03-26",
"capabilities": { "tools": {} },
"serverInfo": { "name": "talaria", "instructions": "authenticate, get_connection, ask before creating a project, verify a test event" } } }Clients speak Streamable HTTP. Each POST gets one JSON body back. A notification with no id is HTTP 204 with an Mcp-Session-Id header and no body. A method the route does not implement comes back as JSON-RPC method not found.
One request
Every call takes the same path. The bearer is resolved before the JSON-RPC method runs, and every handshake, tools/list, tool call and auth failure is logged with secret-shaped fields and tal_live_ values redacted.
Client
POST /mcp with a JSON-RPC body
McpRoute
Reads or mints Mcp-Session-Id and returns it
RequestAuth
resolveMcpAuth resolves the bearer. tal_live_… is rejected
Transfer meter
assertCanRead for the org before the call
McpToolExecutor
Checks MCP scope and project access
Use case
The same one the dashboard runs, capability checked again
Result
One text block holding the tool’s JSON, metered as data transfer
Who can call what
| Call | Bearer |
|---|---|
initialize · notifications/initialized · ping | Not required |
tools/call on docs_list, docs_search, docs_get | Not required when the client names the tool |
tools/list | Always required — a 401 starts OAuth |
Every other tools/call | Grant with the tool’s scope |
The 401 body is invalid_token. The WWW-Authenticate header names realm Talaria and points resource_metadata at /.well-known/oauth-protected-resource.
OAuth and the personal token
Discovery is the protected-resource and authorization-server documents under /.well-known. The client registers with POST /oauth/register, then uses /oauth/authorize, /oauth/token and /oauth/revoke. Consent is the dashboard page at /settings/integrations/cursor. The grant is one user and one organisation.
A personal token is minted with client id tal_pat. It is an opaque single-segment bearer, not a three-part JWT. You choose 30 days, 90 days or until you revoke it.
A tool call
tools/call builds an McpToolContext from the grant: user, organisation, grant id and scope. McpToolExecutor checks the MCP scope for that tool, and when the arguments include a projectId it checks the user can reach that project. It then runs the same use case the dashboard uses — queryEventsIssues for a search, updateIssueStatus for a status change.
The result is one content block of type text holding the tool’s JSON. A domain error (a missing note, a status the issue cannot move to, a project the user cannot see) comes back as a JSON-RPC error on HTTP 200, with the use-case message.
Docs and organisation data
docs_list, docs_search and docs_get read a pinned Markdown snapshot shipped with the server, synced from the public docs. They return the same pages as /docs on this site. Every other tool reads or writes the organisation’s projects, issues, events, traces, sessions and feature flags — the records the dashboard shows for that grant.
Issue triage
search_errors lists grouped issues. get_error returns one issue, its latest event and its comments. search_events accepts issueId so the agent can see whether it is still firing. update_issue_status requires mcp:write; a note is required for resolved and ignored and is stored as a comment.
Where to connect a client
The URL is the same for every client. The config key is not.