Skip to main content

Overview

MCP messages define the communication format between clients and servers. This reference documents all message types, their structure, and usage patterns.
MCP follows a request-response pattern similar to JSON-RPC 2.0 with extensions for streaming and bidirectional communication.

Protocol Transport Architecture

The MCP protocol supports multiple transport layers for communication between clients and servers. The diagram below illustrates how messages flow through the system:

Transport Options

Process-based communication using stdin/stdout. Best for local CLI integrations.
# Server reads from stdin, writes to stdout
echo '{"jsonrpc":"2.0","id":1,"method":"initialize"}' | mcp-server
HTTP-based one-way streaming from server to client. Ideal for web applications.
const eventSource = new EventSource('/mcp/stream');
eventSource.onmessage = (event) => {
  const message = JSON.parse(event.data);
  // Handle message
};
Full-duplex bidirectional communication. Best for real-time interactive applications.
import websockets
async with websockets.connect('ws://localhost:8000/mcp/ws') as ws:
    await ws.send(json.dumps(request))
    response = await ws.recv()

Message Structure

All MCP messages follow this base structure:
{
  "jsonrpc": "2.0",
  "id": "request-123",
  "method": "tools/call",
  "params": {
    "name": "chat",
    "arguments": {"query": "Hello"}
  }
}
jsonrpc
string
required
Protocol version, always "2.0"
id
string | number
required
Unique request identifier for matching responses
method
string
required
The method to invoke (e.g., "tools/call", "resources/read")
params
object
Method-specific parameters

Response Messages

Success Response

{
  "jsonrpc": "2.0",
  "id": "request-123",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Hello! How can I help you?"
      }
    ]
  }
}

Error Response

{
  "jsonrpc": "2.0",
  "id": "request-123",
  "error": {
    "code": -32602,
    "message": "Invalid params",
    "data": {
      "field": "query",
      "reason": "Required field missing"
    }
  }
}

Error Codes

CodeMessageDescription
-32700Parse errorInvalid JSON
-32600Invalid RequestInvalid request object
-32601Method not foundMethod does not exist
-32602Invalid paramsInvalid method parameters
-32603Internal errorInternal server error
-32000Server errorGeneric server error

Message Types

Initialize

Request:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {}
    },
    "clientInfo": {
      "name": "example-client",
      "version": "1.0.0"
    }
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "logging": {},
      "prompts": {
        "listChanged": true
      },
      "resources": {
        "subscribe": true,
        "listChanged": true
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "mcp-server-langgraph",
      "version": "2.8.0"
    }
  }
}

Tools/List

Request:
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}
Response:
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "chat",
        "description": "Chat with the AI agent",
        "inputSchema": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "User query"
            }
          },
          "required": ["query"]
        }
      }
    ]
  }
}

Tools/Call

Request:
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "chat",
    "arguments": {
      "query": "What is the weather?",
      "conversation_id": "conv_123"
    }
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "I don't have access to real-time weather data. Please check a weather service."
      }
    ],
    "isError": false
  }
}

Resources/List

Request:
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/list",
  "params": {}
}
Response:
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "resources": [
      {
        "uri": "conversation://conv_123",
        "name": "Conversation History",
        "description": "Chat conversation context",
        "mimeType": "application/json"
      }
    ]
  }
}

Resources/Read

Request:
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "resources/read",
  "params": {
    "uri": "conversation://conv_123"
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "contents": [
      {
        "uri": "conversation://conv_123",
        "mimeType": "application/json",
        "text": "{\"messages\": [...]}"
      }
    ]
  }
}

Prompts/List

Request:
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "prompts/list",
  "params": {}
}
Response:
{
  "jsonrpc": "2.0",
  "id": 6,
  "result": {
    "prompts": [
      {
        "name": "code_review",
        "description": "Review code for quality and best practices",
        "arguments": [
          {
            "name": "code",
            "description": "Code to review",
            "required": true
          }
        ]
      }
    ]
  }
}

Prompts/Get

Request:
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "prompts/get",
  "params": {
    "name": "code_review",
    "arguments": {
      "code": "def add(a, b): return a + b"
    }
  }
}
Response:
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "description": "Review code for quality and best practices",
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "Please review this code:\n\ndef add(a, b): return a + b"
        }
      }
    ]
  }
}

Content Types

Text Content

{
  "type": "text",
  "text": "This is plain text content"
}

Image Content

{
  "type": "image",
  "data": "base64-encoded-image-data",
  "mimeType": "image/png"
}

Resource Content

{
  "type": "resource",
  "resource": {
    "uri": "file:///path/to/file.txt",
    "mimeType": "text/plain",
    "text": "File contents"
  }
}

Notifications

Notifications are messages sent without expecting a response (no id field):

Tools/ListChanged

{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed"
}

Resources/ListChanged

{
  "jsonrpc": "2.0",
  "method": "notifications/resources/list_changed"
}

Resources/Updated

{
  "jsonrpc": "2.0",
  "method": "notifications/resources/updated",
  "params": {
    "uri": "conversation://conv_123"
  }
}

Streaming Messages

For streaming responses, the server sends multiple messages:

Content Start

{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "type": "content_start"
  }
}

Content Delta

{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "type": "content_delta",
    "delta": "Quantum computing"
  }
}

Content End

{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "type": "content_end",
    "conversation_id": "conv_123",
    "usage": {
      "prompt_tokens": 20,
      "completion_tokens": 150,
      "total_tokens": 170
    }
  }
}

Progress Updates

For long-running operations:
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "token-123",
    "progress": 50,
    "total": 100
  }
}

Example Flows

Complete Chat Interaction

import asyncio
import json
import httpx

async def chat_example():
    async with httpx.AsyncClient() as client:
        # 1. Initialize
        init_response = await client.post(
            "http://localhost:8000/mcp",
            json={
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "protocolVersion": "2024-11-05",
                    "clientInfo": {"name": "example", "version": "1.0"}
                }
            },
            headers={"Authorization": f"Bearer {token}"}
        )
        print("Initialized:", init_response.json())

        # 2. List tools
        tools_response = await client.post(
            "http://localhost:8000/mcp",
            json={
                "jsonrpc": "2.0",
                "id": 2,
                "method": "tools/list"
            },
            headers={"Authorization": f"Bearer {token}"}
        )
        print("Available tools:", tools_response.json())

        # 3. Call chat tool
        chat_response = await client.post(
            "http://localhost:8000/mcp",
            json={
                "jsonrpc": "2.0",
                "id": 3,
                "method": "tools/call",
                "params": {
                    "name": "chat",
                    "arguments": {"query": "Hello!"}
                }
            },
            headers={"Authorization": f"Bearer {token}"}
        )
        result = chat_response.json()
        print("Response:", result["result"]["content"][0]["text"])

asyncio.run(chat_example())

Batch Requests

Send multiple requests in a single HTTP call:
[
  {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
  },
  {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "resources/list"
  },
  {
    "jsonrpc": "2.0",
    "id": 3,
    "method": "prompts/list"
  }
]
Response:
[
  {
    "jsonrpc": "2.0",
    "id": 1,
    "result": {"tools": [...]}
  },
  {
    "jsonrpc": "2.0",
    "id": 2,
    "result": {"resources": [...]}
  },
  {
    "jsonrpc": "2.0",
    "id": 3,
    "result": {"prompts": [...]}
  }
]

Validation

Request Validation

All requests must:
  • Include jsonrpc: "2.0"
  • Have a unique id (except notifications)
  • Specify a valid method
  • Provide required params for the method

Response Validation

Clients should validate:
  • Response id matches request id
  • Either result or error is present (not both)
  • Content types match expected formats

Best Practices

Always use unique IDs for requests to prevent confusion:
import uuid

request_id = str(uuid.uuid4())

request = {
    "jsonrpc": "2.0",
    "id": request_id,
    "method": "tools/call",
    "params": {...}
}
Always check for error responses:
response = await client.post(url, json=request)
data = response.json()

if "error" in data:
    code = data["error"]["code"]
    message = data["error"]["message"]
    raise Exception(f"MCP Error {code}: {message}")

return data["result"]
Set reasonable timeouts for requests:
async with httpx.AsyncClient(timeout=30.0) as client:
    response = await client.post(url, json=request)
Combine multiple requests to reduce latency:
batch = [
    {"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
    {"jsonrpc": "2.0", "id": 2, "method": "resources/list"}
]

responses = await client.post(url, json=batch)

Next Steps


MCP Messages: Standardized communication protocol for AI applications!