Skip to content

Mcp

Implements the Model Context Protocol over JSON-RPC 2.0. All operations go through a single POST /mcp endpoint, differentiated by the request body.

  • Spec: modelcontextprotocol.io
  • MIME: application/json
  • Serializer: McpSerializer
  • Routing: Body-matched (single endpoint)
import crate.protocols.mcp.policy;
auto crateRouter = router.crateSetup!Mcp;
crateRouter.add(userCrate);

All operations use POST /mcp with a JSON-RPC body. The params.name field determines the operation:

Tool NameOperation
list_usersList all
get_userGet one
create_userCreate
replace_userReplace
update_userUpdate fields
delete_userDelete
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_user",
"arguments": { "id": "abc123" }
},
"id": 1
}

MCP registers additional protocol-level operations via onRouterInit:

EndpointBody MatchPurpose
POST /mcpmethod: "initialize"Protocol handshake
POST /mcpmethod: "tools/list"List available tools
POST /mcpmethod: "notifications/initialized"Client notification (202 response)
POST /mcpmethod: "ping"Ping/pong health check
POST /mcpname: "get_upload_instructions"File upload endpoint discovery
POST /mcpname: "get_list_query_options"Query parameter discovery
GET /mcp/sseServer-Sent Events stream
GET /.well-known/oauth-protected-resourceOAuth 2.1 metadata
GET /.well-known/oauth-protected-resource/mcp301 redirect to MCP endpoint

The tools list is populated lazily via onRouterReady after all models are registered.

MCP provides two discovery tools that help AI clients understand what operations are available and how to use them.

Returns REST API upload instructions for a resource field, so AI clients can upload files directly via HTTP instead of passing base64 through tool arguments.

Request:

{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_upload_instructions",
"arguments": {
"model": "picture",
"id": "abc123",
"field": "picture"
}
},
"id": 1
}

Parameters:

NameRequiredDescription
modelyesModel name (singular, lowercase)
idyesResource ID to upload to
fieldyesField name (e.g. "picture", "image/value")

Response (upload available):

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "{\"url\":\"http://localhost/pictures/abc123/picture\",\"method\":\"POST\",\"contentType\":\"multipart/form-data\",\"example\":\"curl -X POST 'http://localhost/pictures/abc123/picture' -H 'Authorization: Bearer {token}' -F 'file=@/path/to/file'\"}"
}]
}
}

Response (no upload endpoint):

If no REST upload endpoint exists for the given model/field, the response suggests using the base64 tool instead:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "No REST upload endpoint available for picture/avatar. Use the `set_picture_avatar_value` tool with base64-encoded data instead."
}]
}
}

Returns the available query/filter parameters for a model’s list endpoint, so AI clients can discover filtering, pagination, and search options.

Request:

{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_list_query_options",
"arguments": {
"model": "team"
}
},
"id": 2
}

Parameters:

NameRequiredDescription
modelyesModel name (singular, lowercase)

Response:

Returns a JSON Schema object describing the available query parameters:

{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [{
"type": "text",
"text": "{\"type\":\"object\",\"properties\":{\"limit\":{\"type\":\"string\",\"description\":\"Max items to return\",\"in\":\"query\"},\"skip\":{\"type\":\"string\",\"description\":\"Items to skip\",\"in\":\"query\"}}}"
}]
}
}

The tool description in tools/list is automatically updated with the list of available models.

MCP supports client-initiated notifications and ping requests.

Sent by the client after the initialize handshake completes. This is a fire-and-forget message that returns 202 Accepted with no body.

{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}

Response: 202 Accepted (empty body)

Standard MCP ping request. Returns a JSON-RPC pong response.

{
"jsonrpc": "2.0",
"method": "ping",
"id": 3
}

Response:

{
"jsonrpc": "2.0",
"id": 3,
"result": {}
}

MCP uses two internal middleware layers to bridge JSON-RPC requests into the same format that REST operations expect. This makes all downstream middleware and operations protocol-agnostic.

The McpContextMarker middleware runs on every request (@any). It detects MCP requests by checking for the jsonrpc field in the request body and sets two context flags:

  • _isMcpRequesttrue for all MCP requests
  • _mcpMethod — the JSON-RPC method string (e.g. "tools/call", "tools/list")

Other middleware can check req.context["_isMcpRequest"] to branch on protocol.

The McpRequestNormalizer middleware transforms JSON-RPC tool calls into REST-compatible format:

  1. Extracts the RPC ID and stores it in req.params["_rpcId"]
  2. Extracts tool arguments from params.arguments
  3. Unwraps the data field for _record tools (e.g. create_record)
  4. Moves the item id from arguments to req.params["id"]
  5. Forwards non-reserved arguments to req.query as filter/pagination params
  6. Stores base64 data in req.params["_base64Data"] if present
  7. Rewrites the request body to model-specific format (e.g. { "icon": {...} })

Reserved keys (not forwarded to query): id, model, data, fullJsonExport

MCP responses are wrapped in the JSON-RPC tool result envelope. The serializer supports two output modes controlled by summary fields and the fullJsonExport argument.

When a model has fields marked @isId or @isSummary, read responses return a compact key-value text format:

_id: 000000000000000000000001
name: My Team
---
_id: 000000000000000000000002
name: Other Team

This reduces token usage for AI clients that only need an overview.

Pass fullJsonExport: true in the tool arguments to get the complete JSON representation:

{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_user",
"arguments": { "id": "abc123", "fullJsonExport": true }
},
"id": 1
}

If no summary fields are defined on the model, full JSON is always returned regardless of this flag.

Create, update, replace, and delete operations return a success confirmation instead of the full item:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{
"type": "text",
"text": "create_user succeeded. id: 000000000000000000000001"
}]
}
}

Context mappers registered on operations are applied to response data before serialization. This works for both toResponseItem and toResponseList, allowing field transformation, enrichment, or filtering at the protocol level.

  • Resources: Only tools are exposed; no MCP resource endpoints
  • Prompts: No prompt support
  • Sampling: No sampling / LLM model endpoints
  • Progress tracking: No progress reporting mechanism
  • Logging: No structured logging API
  • Cancellation: No request cancellation
auto crateRouter = router.crateSetup!(McpPolicy!(PolicyConfig("/tools")));
// Endpoint at POST /tools/mcp

The standard /.well-known/oauth-protected-resource endpoint returns OAuth 2.1 metadata. Additionally, /.well-known/oauth-protected-resource/mcp issues a 301 redirect to the MCP endpoint (respecting any configured base URL prefix).