← API reference

MCP

The Model Context Protocol surface — one endpoint that lets an AI assistant read this user's listening and hand playlists back. Runs for every user, reaches nobody's data until they authorise a connection.

Dual-era. Revision 2026-07-28 is stateless with per-request _meta and mirrored headers; 2025-11-25 and earlier open with an initialize handshake. Both are served on the same endpoint.

Credentials live in mcp_connections, not in tokens — an MCP grant is a standing permission given to somebody else's service, tokens.scopes treats all as a wildcard, and an OAuth credential expires and refreshes.

7 of 7 · v0.120.0
GET /mcp #
Public

Not allowed.

The standalone SSE stream belonged to the 2025 revisions and is not part of this one. 405 is what the spec tells a modern transport to answer, and it is what a dual-era client reads as "use POST".

Responses

405

Method not allowed.

application/json

object

A JSON-RPC 2.0 response: result or error, never both.

jsonrpc string required
id object | null
result object | null
error object | null
code integer · int64 required
message string required
data object | null
POST /mcp #
MCP bearer

The MCP endpoint (JSON-RPC over Streamable HTTP).

One endpoint carrying the whole protocol. Not a REST resource — the body is a JSON-RPC 2.0 request and the method decides what happens, so this entry documents the transport rather than a payload.

Authentication is Authorization: Bearer <credential>, where the credential comes from Settings → AI Connections or from the OAuth flow. A 401 carries WWW-Authenticate with a resource_metadata pointer, and that pointer is how a client discovers the authorization server.

Dual-era. A request carrying params._meta["io.modelcontextprotocol/protocolVersion"] of 2026-07-28 is served statelessly and must mirror Mcp-Method, Mcp-Name and MCP-Protocol-Version into headers matching the body — a mismatch is 400 with JSON-RPC -32020. A request opening with initialize selects legacy semantics (2025-11-25 and earlier) and is not subject to header validation. No session id is ever minted: this server is stateless on both eras.

Methods: initialize, server/discover, ping, tools/list, tools/call, resources/list, resources/templates/list, resources/read, prompts/list, prompts/get, logging/setLevel, and any notifications/* (accepted, 202, no body).

Unknown method is 404 and JSON-RPC -32601 together — the body is what distinguishes this from the 404 a legacy HTTP+SSE server returns for a path it does not host.

Rate limited to 600 calls an hour per connection. Every call is written to the audit log.

Request body required

application/json

object

A JSON-RPC 2.0 request. One message per POST; a batch is refused.

jsonrpc string required

2.0.

id object | null

A string or an integer. Absent for a notification, which is answered 202.

method string required
params object | null

Responses

200

A JSON-RPC response.

application/json

object

A JSON-RPC 2.0 response: result or error, never both.

jsonrpc string required
id object | null
result object | null
error object | null
code integer · int64 required
message string required
data object | null
202

A notification was accepted. No body.

400

Malformed, header/body mismatch (-32020), or unsupported protocol version (-32022).

application/json

object

A JSON-RPC 2.0 response: result or error, never both.

jsonrpc string required
id object | null
result object | null
error object | null
code integer · int64 required
message string required
data object | null
401

Missing or invalid credential. Carries WWW-Authenticate.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

403

Cross-origin request, or a tool the connection was not granted. Carries WWW-Authenticate with error="insufficient_scope".

application/json

object

A JSON-RPC 2.0 response: result or error, never both.

jsonrpc string required
id object | null
result object | null
error object | null
code integer · int64 required
message string required
data object | null
404

Unknown method (-32601) or unknown tool.

application/json

object

A JSON-RPC 2.0 response: result or error, never both.

jsonrpc string required
id object | null
result object | null
error object | null
code integer · int64 required
message string required
data object | null
429

The connection's hourly call budget is spent.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

DELETE /mcp #
Public

Not allowed.

Session termination belonged to the 2025 revisions; there is no session to delete.

Responses

405

Method not allowed.

application/json

object

A JSON-RPC 2.0 response: result or error, never both.

jsonrpc string required
id object | null
result object | null
error object | null
code integer · int64 required
message string required
data object | null
GET /api/v1/mcp/connections #
Session

Your AI connections.

Session-only. Also returns this instance's MCP endpoint address, derived from the request the way the pairing verification URI is, and the full grant catalogue with the sentence shown for each on the consent screen.

Responses

200

The connections.

application/json

object

Your assistant connections, and what one can be granted.

connections array required
each item
object

A standing grant to an AI assistant — deliberately not an API token, so it can be granted, read and revoked on its own.

id integer · int64 required
name string required
scopes string required

Space-separated. No wildcard — a consent screen names what it is granting, and "everything, including whatever is added later" is not something a person can consent to.

client_id string | null

Set when the grant came through OAuth.

created_at integer · int64 required
last_used_at integer | null · int64
expires_at integer | null · int64

Null for a connection created by hand — those do not expire.

calls integer · int64 required

How many calls it has made, ever.

endpoint string · uri required

The address to paste into a client.

scopes array required
each item
object
scope string required
description string required
401

No valid session cookie or token. Also returned when a token is presented to a session-only endpoint — the endpoint does not accept tokens at all, so the scope is irrelevant.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

{
  "code": 401,
  "error": "Authentication required. Log in to access this endpoint."
}
POST /api/v1/mcp/connections #
Session

Mint a connection by hand, for a local client.

Session-only. The credential is in this response and nowhere else, ever again — only its hash is stored.

A connection created here does not expire: it is something the user pasted into a client themselves, there is nothing for it to refresh against, and expiring it would break their setup for no gain they asked for. An OAuth grant is the one that expires.

Unrecognised grants are dropped rather than stored, so a client cannot invent one by asking for it. There is no wildcard.

Request body required

application/json

object
name string | null

Up to 60 characters. Defaults to "AI assistant".

scopes string | null

Space- or comma-separated grants. Anything unrecognised is dropped rather than stored. Omitted means the default read grants.

Responses

200

Created.

application/json

object

A new connection and its credential.

id integer · int64 required
name string required
scopes string required

What was actually granted, unrecognised grants dropped.

token string required

Shown once and never again — it is stored only as a hash.

400

No recognised grant was requested.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

401

No valid session cookie or token. Also returned when a token is presented to a session-only endpoint — the endpoint does not accept tokens at all, so the scope is irrelevant.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

{
  "code": 401,
  "error": "Authentication required. Log in to access this endpoint."
}
DELETE /api/v1/mcp/connections/{id} #
Session

Revoke a connection.

Session-only. Revoked rather than deleted, so the audit rows keep pointing at something with a name — "which assistant read my history in March" outlives the connection.

Parameters

id path required
integer · int64

Responses

200

Revoked.

application/json

object
revoked boolean required
401

No valid session cookie or token. Also returned when a token is presented to a session-only endpoint — the endpoint does not accept tokens at all, so the scope is irrelevant.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

{
  "code": 401,
  "error": "Authentication required. Log in to access this endpoint."
}
404

No such connection.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

GET /api/v1/mcp/audit #
Session

What the assistants have been doing.

Session-only. Every call: which connection, which tool, with what arguments (truncated), whether it worked, how many rows came back, and how long it took. The row count is the interesting one — a tool that answered with 12 rows and one that answered with 20,000 are different events however identical the call looks.

Responses

200

The most recent 200 calls.

application/json

object
calls array required

Newest first.

each item
object
id integer · int64 required
at integer · int64 required
method string required

The JSON-RPC method — tools/call, resources/read.

tool string | null

The tool, resource URI or prompt name.

args string | null

The arguments, truncated to 500 characters. A record of what was asked, not a copy of it.

ok boolean required
rows_returned integer | null · int64

How much data left the building. Null where the answer was not a list. A tool that answered with 12 rows and one that answered with 20,000 are different events however identical the call looks.

ms integer | null · int64
connection_name string | null
401

No valid session cookie or token. Also returned when a token is presented to a session-only endpoint — the endpoint does not accept tokens at all, so the scope is irrelevant.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

{
  "code": 401,
  "error": "Authentication required. Log in to access this endpoint."
}