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 tools return responses in TOON (Token-Oriented Object Notation) format, optimized for LLM consumption.
Overview
Section titled “Overview”EvidentSource implements MCP using a proxy pattern: MCP tools proxy requests to the REST API and return TOON-formatted responses that are efficient for LLM token consumption.
AI Agent → MCP Server (port 3001) → REST API (Accept: text/plain) → TOON ResponseKey Benefits
Section titled “Key Benefits”- Token-efficient responses: TOON format uses natural language summaries with CSV-like tabular data
- Full API access: All event store operations available through MCP tools
- Direct REST fallback: Sophisticated agents can bypass MCP using HTTP hints in tool descriptions
- 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.
Available Tools
Section titled “Available Tools”list_databases
Section titled “list_databases”List all databases accessible to the authenticated user.
Parameters: None
HTTP Equivalent: GET / with Accept: text/plain
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": "list_databases", "arguments": {}}, "id": 3 }'Example Response (TOON format):
You have access to 2 databases.
databases[2]{name,revision,created_at,updated_at}:orders,1247,2024-01-15T10:30:00Z,2024-01-20T14:22:00Zinventory,892,2024-01-10T08:00:00Z,2024-01-19T16:45:00Z
To get details for a database: GET /db/{name}/latestTo query events: GET /db/{name}/latest/events?q=...To create a database: POST /api/v1/dbget_database
Section titled “get_database”Get metadata for a specific database including current revision.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
database | string | Yes | Database name |
HTTP Equivalent: GET /db/{database}/latest with Accept: text/plain
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": "get_database", "arguments": {"database": "orders"}}, "id": 3 }'Example Response (TOON format):
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
To query events: GET /db/orders/latest/events?q=...To commit events: POST /api/v1/db/ordersTo list state views: GET /db/orders/latest/state-viewsquery_events
Section titled “query_events”Query events from a database using bi-temporal selectors.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
database | string | Yes | Database name |
revision | integer | No | Database revision (default: latest) |
selector | object | No | Event filter criteria |
selector.stream_equals | string | No | Filter by event stream (source) |
selector.subject_equals | string | No | Filter by event subject |
selector.event_type_equals | string | No | Filter by event type |
limit | integer | No | Maximum events to return |
HTTP Equivalent: GET /db/{database}/{revision}/events?q={base64_query} with Accept: text/plain
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": "query_events", "arguments": { "database": "orders", "selector": {"event_type_equals": "OrderCreated"}, "limit": 10 } }, "id": 3 }'Example Response (TOON format):
Found 3 events in database "orders" (revisions 42-44).
events[3]{revision,id,type,source,subject,time}:42,evt-001,OrderCreated,order-service,order-123,2024-01-15T10:30:00Z43,evt-002,OrderCreated,order-service,order-124,2024-01-15T11:15:00Z44,evt-003,OrderCreated,order-service,order-125,2024-01-15T12:00:00Z
event_data[3]{revision,data}:42,{"orderId":"order-123","total":99.99}43,{"orderId":"order-124","total":149.99}44,{"orderId":"order-125","total":79.99}
Database "orders" is currently at revision 1247.To query events after this: GET /db/orders/latest/events?q=... (with revision_from=45)transact_batch
Section titled “transact_batch”Submit a batch of CloudEvents with optional consistency constraints.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
database | string | Yes | Database name |
events | array | Yes | CloudEvents to commit |
conditions | array | No | Append conditions (DCB spec) |
transaction_id | string | No | UUID for idempotent retries |
HTTP Equivalent: POST /api/v1/db/{database} with Accept: text/plain
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": "transact_batch", "arguments": { "database": "orders", "events": [{ "specversion": "1.0", "type": "OrderCreated", "source": "order-service", "id": "evt-new-001", "subject": "order-999", "data": {"orderId": "order-999", "total": 199.99} }] } }, "id": 3 }'Example Response (TOON format):
Batch abc-123-def committed successfully to database "orders".
batch: id: abc-123-def database: orders revision_before: 1247 revision_after: 1248 transaction_time: 2024-01-20T15:30:00Z event_count: 1
committed_events[1]{index,assigned_id}:0,evt-new-001
All 1 events committed atomically.
To query these events: GET /db/orders/1248/eventsTo verify the batch: GET /api/v1/db/orders/batch/abc-123-defget_state_view
Section titled “get_state_view”Execute a registered State View query at a specific revision.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
database | string | Yes | Database name |
state_view | string | Yes | State view name |
version | integer | Yes | State view version |
revision | integer | No | Database revision (default: latest) |
effective_time_end_at | string | No | ISO 8601 timestamp for bi-temporal queries |
HTTP Equivalent: GET /db/{database}/{revision}/state-views/{state_view}/v{version} with Accept: text/plain
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": "get_state_view", "arguments": { "database": "orders", "state_view": "order-summary", "version": 1 } }, "id": 3 }'TOON Format
Section titled “TOON Format”TOON (Token-Oriented Object Notation) is a text format designed for LLM consumption:
[Natural language summary]
[TOON-formatted data - key-value pairs or CSV-like tables]
[Contextual guidance for next actions]Tabular Format
Section titled “Tabular Format”Arrays of uniform objects use CSV-like format:
events[3]{revision,id,type,source}:42,evt-001,OrderCreated,order-service43,evt-002,ItemAdded,order-service44,evt-003,OrderSubmitted,order-serviceRequest 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:
# Initialize with auth headerINIT=$(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.
Tool Annotations
Section titled “Tool Annotations”Each tool includes MCP standard annotations:
| Tool | readOnlyHint | idempotentHint | Description |
|---|---|---|---|
list_databases | true | - | Read-only operation |
get_database | true | - | Read-only operation |
query_events | true | - | Read-only operation |
transact_batch | false | true | Write operation, idempotent with transaction_id |
get_state_view | true | - | Read-only operation |
Direct REST API Access
Section titled “Direct REST API Access”Sophisticated agents can bypass MCP and call the REST API directly. Each tool description includes HTTP hints:
HTTP: GET /db/{database}/latest with Accept: text/plainUse Accept: text/plain for TOON format or Accept: application/json for JSON format.
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 batching
- Read about State Views for materialized views