20 MCP
9 operations.
20.0.1 getMcpStream
GET /api/mcp
The server→client stream slot; not offered in this stateless phase (answers 405).
Why it exists. † Report what the assistant surface offers, so a connecting client can discover the available tools before calling any.
The Streamable-HTTP server→client channel. This stateless phase offers no server-initiated stream, so it always answers 405 (or 404 when disabled) and reads nothing — hence public.
Authentication: none
Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.
Responses
| Status | Meaning | Body |
|---|---|---|
404 |
The MCP server is disabled. | |
405 |
No server-initiated stream is offered. |
20.0.2 sendMcpMessage
POST /api/mcp
The remote Model Context Protocol endpoint for external AI clients (e.g. Claude). Speaks JSON-RPC (initialize, list/call tools) and exposes a curated tool surface plus three spec-driven ones (kap_api_list, kap_api_show, kap_api_call) that discover and call any read in this specification; writes stay curated, and each tool runs as the signed-in user under RLS. Every payload it forwards is scrubbed of secret-keyed values (credentials, account keys, tokens) on the way out, because a tool returns whatever shape its handler returns. Off unless MCP_SERVER_ENABLED. See docs/proposals/20260816_remote_mcp_server.md.
Why it exists. † Serve the platform’s read surface to an external AI assistant over the Model Context Protocol, as the signed-in engineer — one identity, the caller’s own permissions, and the same access rules the browser gets.
Model Context Protocol server exposing a curated read surface as tools (see docs/proposals/20260816_remote_mcp_server.md). Accepts JSON-RPC 2.0 messages — initialize, tools/list, tools/call, ping. Disabled unless MCP_SERVER_ENABLED. Auth reuses the Supabase bearer via requireUser; each tool forwards that token to the corresponding backend operation, so it runs as the caller under RLS.
Authentication: SupabaseAuth
Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
A JSON-RPC 2.0 response. | |
202 |
Accepted — a notification, no reply. | |
400 |
Malformed JSON. | |
401 |
Missing or invalid bearer token. | |
404 |
The MCP server is disabled. |
20.0.3 getMcpFindByContentEmbed
GET /api/mcp/embed/find-by-content
Renders a drop zone where the engineer can put a picture and see which of a dataset’s photographs it is (the target of the kap_find_image_by_content MCP UI resource). Gated by a short-lived sealed t token that carries the user’s bearer server-side. Off unless MCP_SERVER_ENABLED.
Renders a self-contained HTML page with a drop zone for one picture, for a host to load in a sandboxed iframe (the target of the kap_find_image_by_content tool’s UI resource when it is called without an image_url). The page posts the picture back to this same URL, which forwards it to searchImagesByContent as the user. Authenticated by a short-lived sealed t token that carries the user’s bearer server-side (never exposed to the page). Framing is allowed on purpose. Off unless MCP_SERVER_ENABLED.
Authentication: none
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
t |
query | yes | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
The drop-zone HTML page. | |
400 |
The embed token is invalid or expired. | |
404 |
The MCP server is disabled. |
20.0.4 searchImagesByContentEmbed
POST /api/mcp/embed/find-by-content
The drop-zone page’s search call: forwards the posted picture to searchImagesByContent for the token’s dataset, as the user the token was sealed for, and returns that operation’s answer.
Called by the find-by-content embed page. Opens the sealed t token server-side, forwards the body to POST /datasets/{slug}/find-by-content (searchImagesByContent) for the token’s dataset with the user’s bearer, and returns the answer and status as they are. Off unless MCP_SERVER_ENABLED.
Authentication: none
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
t |
query | yes | string |
Request body
| Field | Type | Required |
|---|---|---|
image |
string | yes |
max_distance |
integer, nullable | no |
limit |
integer | no |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
searchImagesByContent’s answer. | |
400 |
The embed token is invalid or expired, or the body is not a picture. | |
404 |
The MCP server is disabled. |
20.0.5 getMcpGalleryEmbed
GET /api/mcp/embed/gallery
Renders a dataset’s image gallery as a self-contained HTML page for a host to embed in a sandboxed iframe (the target of the kap_gallery_ui(hosted:true) MCP UI resource). Gated by a short-lived sealed t token that carries the user’s bearer server-side; images are fetched under RLS. Off unless MCP_SERVER_ENABLED.
Why it exists. † Render a gallery of images for an assistant that can display a web page, so photographs appear inline in the conversation rather than as links.
Renders a dataset’s image gallery as a self-contained HTML page for a host to load in a sandboxed iframe (the target of the kap_gallery_ui(hosted:true) mode’s UI resource). Authenticated by a short-lived sealed t token that carries the user’s bearer server-side (never exposed to the client); the images are fetched under RLS. Framing is allowed on purpose. Off unless MCP_SERVER_ENABLED.
Authentication: none
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
t |
query | yes | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
The gallery HTML page. | |
400 |
The embed token is invalid or expired. | |
404 |
The MCP server is disabled. | |
502 |
The dataset’s images could not be loaded. |
20.0.6 getMcpAuthorization
GET /api/mcp/oauth/authorize
OAuth 2.1 authorization (PKCE). Authenticates via the existing Supabase session; when signed in it renders a consent page naming the client (no code issued here), and bounces to /login when not.
Why it exists. † Show the engineer what an assistant is asking to reach and let them approve or refuse it, so access is granted by a person rather than assumed from a session.
Authenticates the user via their existing KAP Supabase session. When signed in, renders a consent page naming the client (no code is issued here); when not signed in, redirects to /login. Public entry point; it performs its own session handling and never 401s. Off unless MCP_SERVER_ENABLED.
Authentication: none
Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.
Parameters
| Name | In | Required | Type | Notes |
|---|---|---|---|---|
client_id |
query | no | string | |
redirect_uri |
query | no | string | |
state |
query | no | string | |
response_type |
query | no | string | |
code_challenge |
query | no | string | |
code_challenge_method |
query | no | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
The consent page (HTML) shown to a signed-in user. | |
303 |
Redirect to /login, or back to the client on a protocol error. | |
400 |
Invalid client_id or redirect_uri. | |
404 |
The MCP server is disabled. |
20.0.7 decideMcpAuthorization
POST /api/mcp/oauth/authorize
The consent decision. Only an explicit approval carrying a signed, session-bound consent token issues a code — this is what prevents silent code issuance to an arbitrary registered client. Denial/forgery returns access_denied or 400.
Why it exists. † Record the engineer’s explicit approval and issue the one-time code the assistant exchanges for access — the step that stops a registered client obtaining access silently.
Receives the consent form. On an explicit approval carrying a signed, session-bound consent token, seals the Supabase session into an authorization code and redirects back to the client; on denial or a missing/forged token, redirects back with access_denied (or 400). This step is what prevents silent code issuance to an arbitrary registered client. Off unless MCP_SERVER_ENABLED.
Authentication: none
Returns — not readable from the source: the handler returns a value assembled at run time rather than a literal.
Responses
| Status | Meaning | Body |
|---|---|---|
303 |
Redirect back to the client with a code or an error. 303 rather than 307, so the consent POST is never replayed onto the client callback. | |
400 |
Invalid request or consent token. | |
404 |
The MCP server is disabled. |
20.0.8 createMcpClient
POST /api/mcp/oauth/register
Dynamic client registration (RFC 7591). The client’s metadata is HMAC-signed into a stateless client_id; redirect_uris must be absolute http(s) URLs. Public clients (PKCE, no secret). Off unless MCP_SERVER_ENABLED.
Why it exists. † Let an assistant register itself as a client without an administrator provisioning it in advance, keeping its details signed into the identifier rather than stored.
Registers a public OAuth client for the MCP connector. The client’s metadata is HMAC-signed into the returned client_id (stateless, no store). Off unless MCP_SERVER_ENABLED.
Authentication: none
Returns — client_id · client_id_issued_at · redirect_uris · token_endpoint_auth_method · grant_types · response_types
Field names as the handler returns them; the source states no types for them.
Request body
| Field | Type | Required |
|---|---|---|
redirect_uris |
array of string | no |
client_name |
string, nullable | no |
Responses
| Status | Meaning | Body |
|---|---|---|
201 |
The registered client. | |
400 |
Invalid client metadata. | |
404 |
The MCP server is disabled. |
20.0.9 createMcpToken
POST /api/mcp/oauth/token
Exchanges a PKCE authorization code (or a refresh token) for the user’s Supabase access token, returned as the OAuth access token so the resource server verifies it with the existing requireUser.
Why it exists. † Exchange an approved code for the caller’s own access token, so the assistant acts as the engineer and never holds a credential of its own.
Exchanges an authorization code (PKCE) or a refresh token for the user’s Supabase access token, returned as the OAuth access token so the resource server verifies it with the existing requireUser. Off unless MCP_SERVER_ENABLED.
Authentication: none
Returns — access_token · token_type · expires_in · refresh_token · scope
Field names as the handler returns them; the source states no types for them.
Request body
| Field | Type | Required |
|---|---|---|
grant_type |
string | no |
code |
string | no |
code_verifier |
string | no |
client_id |
string | no |
redirect_uri |
string | no |
refresh_token |
string | no |
Responses
| Status | Meaning | Body |
|---|---|---|
200 |
The token response (access_token = Supabase JWT). | |
400 |
invalid_grant or unsupported_grant_type. | |
404 |
The MCP server is disabled. |