MCP (Model Context Protocol)
The Model Context Protocol (MCP) provides a standardized way for AI agents and LLM-powered applications to interact with EvidentSource. MCP uses a minimal tool surface design where agents navigate the API through TOON (Token-Oriented Object Notation) hypermedia responses.
Architecture
Section titled “Architecture”EvidentSource implements MCP using a “front door” pattern: A minimal set of tools provides entry points, and agents navigate via hypermedia links in TOON responses.
AI Agent → MCP Resources → Discovery (guides, database catalog) → MCP Tools → fetch → REST API (Accept: text/plain) ← TOON hypermedia responsesDesign Principles
Section titled “Design Principles”- Minimal Tool Surface: Only 3 tools instead of one tool per operation
- Hypermedia Navigation: TOON responses include links and actions for discovery
- Direct REST Fallback: Sophisticated agents can bypass MCP using HTTP directly
- Authentication Forwarding: Auth headers are forwarded to the REST API
Connecting to MCP
Section titled “Connecting to MCP”Endpoint
Section titled “Endpoint”The MCP server runs standalone on port 3001 (separate from the REST API on port 3000):
POST http://localhost:3001/The MCP server uses Streamable HTTP transport with Server-Sent Events (SSE) for responses.
Required Headers
Section titled “Required Headers”| Header | Value | Description |
|---|---|---|
Content-Type | application/json | Request body format |
Accept | application/json, text/event-stream | Both required for SSE |
mcp-session-id | <session-id> | Required after initialization |
Authorization | Bearer <token> | Optional, forwarded to REST API |
Protocol Handshake
Section titled “Protocol Handshake”MCP requires a specific initialization sequence:
# Step 1: Initialize sessionINIT=$(curl -s -i -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": {"name": "my-agent", "version": "1.0.0"} }, "id": 1 }')
# Extract session ID from response headersSESSION=$(echo "$INIT" | grep -i mcp-session-id | awk '{print $2}' | tr -d '\r')echo "Session: $SESSION"
# Step 2: Send initialized notification (REQUIRED)curl -s -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{"jsonrpc": "2.0", "method": "notifications/initialized"}'
# Step 3: Now you can use toolstimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{"jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": 2}'Response Format
Section titled “Response Format”Responses are SSE (Server-Sent Events) format:
data: {"jsonrpc":"2.0","id":2,"result":{...}}id: 0/0Use timeout with curl since SSE streams stay open.
Resources
Section titled “Resources”MCP resources provide discovery entry points. Agents read resources to understand what’s available, then use fetch to navigate.
Available Resources
Section titled “Available Resources”| URI | Name | Description |
|---|---|---|
evidentsource://guides/roles | AI Roles Guide | Start here - understand Author vs User roles |
evidentsource://databases | Database Catalog | List all databases with navigation guidance |
evidentsource://guides/authoring | Component Authoring Guide | How to build State Views and State Changes |
evidentsource://guides/api | API Reference Guide | REST endpoints, queries, and constraints |
Resource Templates
Section titled “Resource Templates”| URI Template | Description |
|---|---|
evidentsource://db/{name} | Per-database guidance with revision semantics |
Reading Resources
Section titled “Reading Resources”# Read the roles guide (start here)timeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "resources/read", "params": {"uri": "evidentsource://guides/roles"}, "id": 3 }'
# Read the database catalogtimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "resources/read", "params": {"uri": "evidentsource://databases"}, "id": 4 }'MCP provides exactly 3 tools. All other operations use the fetch tool with appropriate paths.
Generic HTTP tool for navigating TOON hypermedia responses. Use this for all read operations and JSON writes (including transactions).
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
method | string | No | HTTP method: GET or POST (default: GET) |
path | string | Yes | API path (e.g., /db/orders/latest) |
query_params | object | No | Query parameters (auto-encoded) |
body | object | No | Request body for POST requests |
Examples:
# List databasestimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "fetch", "arguments": {"path": "/"} }, "id": 3 }'
# Get database detailstimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "fetch", "arguments": {"path": "/db/orders/latest"} }, "id": 4 }'
# Query events (query object auto-encoded to base64)timeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "fetch", "arguments": { "path": "/db/orders/latest/events", "query_params": { "q": { "selector": {"equals": {"subject": {"hasValue": true, "value": "order-123"}}}, "range": {"revision": {}}, "direction": "FORWARD" } } } }, "id": 5 }'
# Create a databasetimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "fetch", "arguments": { "method": "POST", "path": "/api/v1/db", "body": {"database_name": "my-new-db"} } }, "id": 6 }'
# Transact events with DCB conditionstimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "fetch", "arguments": { "method": "POST", "path": "/api/v1/db/orders", "body": { "events": [{ "specversion": "1.0", "type": "OrderCreated", "source": "order-service", "id": "evt-001", "subject": "order-123", "data": {"orderId": "order-123", "total": 99.99} }], "conditions": [] } } }, "id": 7 }'
# Get a state viewtimeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "fetch", "arguments": {"path": "/api/v1/db/orders/latest/state-views/order-summary/v1"} }, "id": 8 }'execute_state_change
Section titled “execute_state_change”Execute a State Change command handler. Use this when content type varies (not just JSON).
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
database | string | Yes | Database name |
state_change | string | Yes | State change name |
version | integer | Yes | State change version |
command | object | Yes | Command payload (JSON) |
Example:
timeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "execute_state_change", "arguments": { "database": "orders", "state_change": "place-order", "version": 1, "command": {"order_id": "123", "items": [{"sku": "ABC", "qty": 2}]} } }, "id": 9 }'publish_component
Section titled “publish_component”Deploy a WASM State View or State Change component. Use this for binary uploads.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
database | string | Yes | Database name |
component_type | string | Yes | state_view or state_change |
name | string | Yes | Component name |
version | integer | Yes | Component version |
content_type | string | Yes | Content type (e.g., application/json) |
wasm_base64 | string | Yes | Base64-encoded WASM binary |
key_type | string | No | State view key type: String, Int64, or None |
reduce_fn | string | No | State view reduce function name (default: reduce) |
events | array | No | State view event type filter |
Example:
timeout 2s curl -s -N -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "publish_component", "arguments": { "database": "orders", "component_type": "state_view", "name": "order-summary", "version": 1, "content_type": "application/json", "key_type": "String", "wasm_base64": "AGFzbQEAAAA..." } }, "id": 10 }'TOON Format
Section titled “TOON Format”TOON (Token-Oriented Object Notation) is a text format designed for LLM consumption:
[Natural language summary]
[Structured data - key-value pairs or CSV-like tables]
---Links: [Discoverable URLs for navigation]
Actions: [Available operations]Example TOON Response
Section titled “Example TOON Response”Database "orders" is at revision 1247.
database: name: orders revision: 1247 created_at: 2024-01-15T10:30:00Z updated_at: 2024-01-20T14:22:00Z
---Links: events: GET /db/orders/1247/events streams: GET /db/orders/1247/streams subjects: GET /db/orders/1247/subjects event_types: GET /db/orders/1247/event-types
Actions: transact: POST /api/v1/db/ordersRequest TOON from REST API
Section titled “Request TOON from REST API”You can request TOON format directly from the REST API:
# List databases in TOON formatcurl -H "Accept: text/plain" http://localhost:3000/
# Get database details in TOON formatcurl -H "Accept: text/plain" http://localhost:3000/db/orders/latest
# Query events in TOON formatcurl -H "Accept: text/plain" http://localhost:3000/db/orders/latest/eventsAuthentication
Section titled “Authentication”When authentication is enabled, pass the auth header during initialization:
INIT=$(curl -s -i -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "jsonrpc": "2.0", "method": "initialize", "params": { "protocolVersion": "2025-03-26", "capabilities": {}, "clientInfo": {"name": "my-agent", "version": "1.0.0"} }, "id": 1 }')The MCP server extracts the Authorization header and forwards it to the REST API for each tool call.
Common Workflows
Section titled “Common Workflows”Exploring a Database
Section titled “Exploring a Database”- Read the database catalog resource
- Use
fetchto get database details - Follow links in TOON response to explore events, streams, etc.
read_resource("evidentsource://databases") → fetch(path="/db/orders/latest") → fetch(path="/db/orders/1247/events")Transacting Events
Section titled “Transacting Events”- Get current database state
- Build events with appropriate subjects and types
- Use
fetchwith POST to transact
fetch(path="/db/orders/latest") → fetch(method="POST", path="/api/v1/db/orders", body={events: [...], conditions: [...]})Deploying Components
Section titled “Deploying Components”- Read the authoring guide resource
- Build WASM component following the guide
- Use
publish_componentto deploy
read_resource("evidentsource://guides/authoring") → publish_component(database="orders", component_type="state_view", ...)Error Handling
Section titled “Error Handling”Errors are returned as MCP error responses:
{ "jsonrpc": "2.0", "id": 3, "error": { "code": -32603, "message": "Request failed with status 404: Database not found" }}Common Issues
Section titled “Common Issues””Failed to create service: expect initialized notification”
Section titled “”Failed to create service: expect initialized notification””You must send notifications/initialized after the initialize request:
curl -s -X POST http://localhost:3001/ \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "mcp-session-id: $SESSION" \ -d '{"jsonrpc": "2.0", "method": "notifications/initialized"}'Connection Timeout
Section titled “Connection Timeout”MCP uses SSE (Server-Sent Events) which keeps connections open. Use timeout with curl:
timeout 2s curl -s -N -X POST http://localhost:3001/ ...Missing Accept Header
Section titled “Missing Accept Header”Both application/json and text/event-stream are required:
-H "Accept: application/json, text/event-stream"Next Steps
Section titled “Next Steps”- Learn about Event Sourcing fundamentals
- Explore the REST API for direct HTTP access
- See Transactions for event transacting
- Read about State Views for materialized views