Protocol & JSON-RPC
ONCE MCP uses JSON-RPC 2.0 over HTTP. All requests go to POST /api/mcp.
Server Card
The server card advertises transport and capability metadata:
GET /.well-known/mcp/server-card.jsonKey details:
transport.typeisstreamable-httpprotocolVersionis2024-11-05capabilities.tools.listChangedandcapabilities.resources.listChangedarefalse(lists are static during a session)
Authentication
ONCE MCP uses OAuth 2.1-style HTTP authorization challenges for remote clients.
When a request is unauthenticated, ONCE returns:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="once-mcp", resource_metadata="https://beta.once.app/.well-known/oauth-protected-resource/api/mcp", scope="once:mcp"Most MCP clients will show an Authenticate / Sign in button and open a browser flow automatically.
After sign-in, provide a token via:
Authorization: Bearer <token>header (preferred), oraccess_token/accessTokentool arguments only when you are using a direct compatibility client that cannot manage bearer headers
Token resolution order: Authorization header → access_token → accessToken.
Initialize
Optional handshake to get server info and protocol version:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize"
}Requires authentication.
List Tools
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}Requires authentication. Returns all available tools with their input schemas.
Lists are static during a session (listChanged: false), so clients typically cache and only refresh on reconnect.
Call a Tool
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_release_schema",
"arguments": {}
}
}Tools require authentication:
In native MCP clients, authenticated tool calls do not need an access_token argument:
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "list_releases",
"arguments": {}
}
}Response format:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "{\"release\":{\"required\":[\"title\"],\"notes\":[\"...\"]}}"
}
]
}
}The tool payload is serialized into content[].text. Parse it as JSON if you need structured data.
For large local files in Claude Code or Cursor, use prepare_local_file_upload first. It returns a scoped upload token plus raw HTTP endpoints so the file can be transferred outside the conversation.
List Resources
{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/list"
}Requires authentication. Lists are static during a session (listChanged: false).
Read a Resource
{
"jsonrpc": "2.0",
"id": 6,
"method": "resources/read",
"params": {
"uri": "mcp://schemas/release-required"
}
}Requires authentication.
Response format:
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"contents": [
{
"uri": "mcp://schemas/release-required",
"mimeType": "application/json",
"text": "{ ... }"
}
]
}
}The resource payload lives in contents[].text. For JSON resources, parse the string.
Batch Requests
The server accepts JSON-RPC batches:
[
{ "jsonrpc": "2.0", "id": 1, "method": "initialize" },
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" },
{ "jsonrpc": "2.0", "id": 3, "method": "resources/list" }
]Responses are returned as an array. Each item succeeds or fails independently.
Notifications
Omit id to send a notification. The server responds with HTTP 204 No Content.
Error Format
{
"jsonrpc": "2.0",
"id": 10,
"error": {
"code": -32602,
"message": "Missing uri",
"data": null
}
}Common error codes:
| Code | Meaning |
|---|---|
| -32700 | Parse error |
| -32601 | Method not found |
| -32602 | Invalid params |
| -32000 | Server error |
| 401 | Unauthorized |
| 403 | Forbidden |
Capabilities
| Feature | Status |
|---|---|
| Tools | List, call |
| Resources | List, read |
| Prompts | Not implemented |
| Subscriptions | Not implemented |