Skip to content

Commit 7e2e517

Browse files
Merge pull request #228 from modelcontextprotocol/justin/jsonrpc-batch
[RFC] Explicitly require JSON-RPC batch support
2 parents ae138ba + ea21b21 commit 7e2e517

File tree

7 files changed

+227
-167
lines changed

7 files changed

+227
-167
lines changed

Diff for: docs/specification/draft/architecture/_index.md

-12
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,6 @@ implementation:
115115
- Protocol designed for future extensibility
116116
- Backwards compatibility is maintained
117117

118-
## Message Types
119-
120-
MCP defines three core message types based on
121-
[JSON-RPC 2.0](https://www.jsonrpc.org/specification):
122-
123-
- **Requests**: Bidirectional messages with method and parameters expecting a response
124-
- **Responses**: Successful results or errors matching specific request IDs
125-
- **Notifications**: One-way messages requiring no response
126-
127-
Each message type follows the JSON-RPC 2.0 specification for structure and delivery
128-
semantics.
129-
130118
## Capability Negotiation
131119

132120
The Model Context Protocol uses a capability-based negotiation system where clients and

Diff for: docs/specification/draft/basic/_index.md

+75-25
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,6 @@ weight: 2
77

88
{{< callout type="info" >}} **Protocol Revision**: draft {{< /callout >}}
99

10-
All messages between MCP clients and servers **MUST** follow the
11-
[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines
12-
three fundamental types of messages:
13-
14-
| Type | Description | Requirements |
15-
| --------------- | -------------------------------------- | -------------------------------------- |
16-
| `Requests` | Messages sent to initiate an operation | Must include unique ID and method name |
17-
| `Responses` | Messages sent in reply to requests | Must include same ID as request |
18-
| `Notifications` | One-way messages with no reply | Must not include an ID |
19-
20-
**Responses** are further sub-categorized as either **successful results** or **errors**.
21-
Results can follow any JSON object structure, while errors must include an error code and
22-
message at minimum.
23-
24-
## Protocol Layers
25-
2610
The Model Context Protocol consists of several key components that work together:
2711

2812
- **Base Protocol**: Core JSON-RPC message types
@@ -40,16 +24,82 @@ These protocol layers establish clear separation of concerns while enabling rich
4024
interactions between clients and servers. The modular design allows implementations to
4125
support exactly the features they need.
4226

43-
See the following pages for more details on the different components:
27+
## Messages
4428

45-
{{< cards >}}
46-
{{< card link="/specification/draft/basic/lifecycle" title="Lifecycle" icon="refresh" >}}
47-
{{< card link="/specification/draft/server/resources" title="Resources" icon="document" >}}
48-
{{< card link="/specification/draft/server/prompts" title="Prompts" icon="chat-alt-2" >}}
49-
{{< card link="/specification/draft/server/tools" title="Tools" icon="adjustments" >}}
50-
{{< card link="/specification/draft/server/utilities/logging" title="Logging" icon="annotation" >}}
51-
{{< card link="/specification/draft/client/sampling" title="Sampling" icon="code" >}}
52-
{{< /cards >}}
29+
All messages between MCP clients and servers **MUST** follow the
30+
[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines
31+
these types of messages:
32+
33+
### Requests
34+
35+
Requests are sent from the client to the server or vice versa, to initiate an operation.
36+
37+
```typescript
38+
{
39+
jsonrpc: "2.0";
40+
id: string | number;
41+
method: string;
42+
params?: {
43+
[key: string]: unknown;
44+
};
45+
}
46+
```
47+
48+
- Requests **MUST** include a string or integer ID.
49+
- Unlike base JSON-RPC, the ID **MUST NOT** be `null`.
50+
- The request ID **MUST NOT** have been previously used by the requestor within the same
51+
session.
52+
53+
### Responses
54+
55+
Responses are sent in reply to requests, containing the result or error of the operation.
56+
57+
```typescript
58+
{
59+
jsonrpc: "2.0";
60+
id: string | number;
61+
result?: {
62+
[key: string]: unknown;
63+
}
64+
error?: {
65+
code: number;
66+
message: string;
67+
data?: unknown;
68+
}
69+
}
70+
```
71+
72+
- Responses **MUST** include the same ID as the request they correspond to.
73+
- **Responses** are further sub-categorized as either **successful results** or
74+
**errors**. Either a `result` or an `error` **MUST** be set. A response **MUST NOT**
75+
set both.
76+
- Results **MAY** follow any JSON object structure, while errors **MUST** include an
77+
error code and message at minimum.
78+
- Error codes **MUST** be integers.
79+
80+
### Notifications
81+
82+
Notifications are sent from the client to the server or vice versa, as a one-way message.
83+
The receiver **MUST NOT** send a response.
84+
85+
```typescript
86+
{
87+
jsonrpc: "2.0";
88+
method: string;
89+
params?: {
90+
[key: string]: unknown;
91+
};
92+
}
93+
```
94+
95+
- Notifications **MUST NOT** include an ID.
96+
97+
### Batching
98+
99+
JSON-RPC also defines a means to
100+
[batch multiple requests and notifications](https://www.jsonrpc.org/specification#batch),
101+
by sending them in an array. MCP implementations **MAY** support sending JSON-RPC
102+
batches, but **MUST** support receiving JSON-RPC batches.
53103

54104
## Auth
55105

Diff for: docs/specification/draft/basic/lifecycle.md

+6
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ The client **MUST** initiate this phase by sending an `initialize` request conta
7373
}
7474
```
7575

76+
The initialize request **MUST NOT** be part of a JSON-RPC
77+
[batch](https://www.jsonrpc.org/specification#batch), as other requests and notifications
78+
are not possible until initialization has completed. This also permits backwards
79+
compatibility with prior protocol versions that do not explicitly support JSON-RPC
80+
batches.
81+
7682
The server **MUST** respond with its own capabilities and information:
7783

7884
```json

Diff for: docs/specification/draft/basic/messages.md

-71
This file was deleted.

Diff for: docs/specification/draft/basic/transports.md

+71-57
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ It is also possible for clients and servers to implement
2424
In the **stdio** transport:
2525

2626
- The client launches the MCP server as a subprocess.
27-
- The server receives JSON-RPC messages on its standard input (`stdin`) and writes
28-
responses to its standard output (`stdout`).
27+
- The server reads JSON-RPC messages from its standard input (`stdin`) and sends messages
28+
to its standard output (`stdout`).
29+
- Messages may be JSON-RPC requests, notifications, responses—or a JSON-RPC
30+
[batch](https://www.jsonrpc.org/specification#batch) containing one or more requests
31+
and/or notifications.
2932
- Messages are delimited by newlines, and **MUST NOT** contain embedded newlines.
3033
- The server **MAY** write UTF-8 strings to its standard error (`stderr`) for logging
3134
purposes. Clients **MAY** capture, forward, or ignore this logging.
@@ -66,61 +69,72 @@ The server **MUST** provide a single HTTP endpoint path (hereafter referred to a
6669
**MCP endpoint**) that supports both POST and GET methods. For example, this could be a
6770
URL like `https://example.com/mcp`.
6871

69-
### Message Exchange
70-
71-
1. Every JSON-RPC message sent from the client **MUST** be a new HTTP POST request to the
72-
MCP endpoint.
73-
74-
2. When the client sends a JSON-RPC _request_ to the MCP endpoint via POST:
75-
76-
- The client **MUST** include an `Accept` header, listing both `application/json` and
77-
`text/event-stream` as supported content types.
78-
- The server **MUST** either return `Content-Type: text/event-stream`, to initiate an
79-
SSE stream, or `Content-Type: application/json`, to return a single JSON-RPC
80-
_response_. The client **MUST** support both these cases.
81-
- If the server initiates an SSE stream:
82-
- The SSE stream **SHOULD** eventually include a JSON-RPC _response_ message.
83-
- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending a
84-
JSON-RPC _response_. These messages **SHOULD** relate to the originating client
85-
_request_.
86-
- The server **SHOULD NOT** close the SSE stream before sending the JSON-RPC
87-
_response_, unless the [session](#session-management) expires.
88-
- After the JSON-RPC _response_ has been sent, the server **MAY** close the SSE
89-
stream at any time.
90-
- Disconnection **MAY** occur at any time (e.g., due to network conditions).
91-
Therefore:
92-
- Disconnection **SHOULD NOT** be interpreted as the client cancelling its
93-
request.
94-
- To cancel, the client **SHOULD** explicitly send an MCP `CancelledNotification`.
95-
- To avoid message loss due to disconnection, the server **MAY** make the stream
96-
[resumable](#resumability-and-redelivery).
97-
98-
3. When the client sends a JSON-RPC _notification_ or _response_ to the MCP endpoint via
99-
POST:
100-
101-
- If the server accepts the message, it **MUST** return HTTP status code 202 Accepted
102-
with no body.
103-
- If the server cannot accept the message, it **MUST** return an HTTP error status
104-
code (e.g., 400 Bad Request). The HTTP response body **MAY** comprise a JSON-RPC
105-
_error response_ that has no `id`.
106-
107-
4. The client **MAY** also issue an HTTP GET to the MCP endpoint. This can be used to
108-
open an SSE stream, allowing the server to communicate to the client without the
109-
client first sending a JSON-RPC _request_.
110-
- The client **MUST** include an `Accept` header, listing `text/event-stream` as a
111-
supported content type.
112-
- The server **MUST** either return `Content-Type: text/event-stream` in response to
113-
this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the
114-
server does not offer an SSE stream at this endpoint.
115-
- If the server initiates an SSE stream:
116-
- The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream.
117-
These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC
118-
_request_ from the client.
119-
- The server **MUST NOT** send a JSON-RPC _response_ on the stream **unless**
120-
[resuming](#resumability-and-redelivery) a stream associated with a previous
121-
client request.
122-
- The server **MAY** close the SSE stream at any time.
123-
- The client **MAY** close the SSE stream at any time.
72+
### Sending Messages to the Server
73+
74+
Every JSON-RPC message sent from the client **MUST** be a new HTTP POST request to the
75+
MCP endpoint.
76+
77+
1. The client **MUST** use HTTP POST to send JSON-RPC messages to the MCP endpoint.
78+
2. The client **MUST** include an `Accept` header, listing both `application/json` and
79+
`text/event-stream` as supported content types.
80+
3. The body of the POST request **MUST** be one of the following:
81+
- A single JSON-RPC _request_, _notification_, or _response_
82+
- An array [batching](https://www.jsonrpc.org/specification#batch) one or more
83+
_requests and/or notifications_
84+
- An array [batching](https://www.jsonrpc.org/specification#batch) one or more
85+
_responses_
86+
4. If the input consists solely of (any number of) JSON-RPC _responses_ or
87+
_notifications_:
88+
- If the server accepts the input, the server **MUST** return HTTP status code 202
89+
Accepted with no body.
90+
- If the server cannot accept the input, it **MUST** return an HTTP error status code
91+
(e.g., 400 Bad Request). The HTTP response body **MAY** comprise a JSON-RPC _error
92+
response_ that has no `id`.
93+
5. If the input contains any number of JSON-RPC _requests_, the server **MUST** either
94+
return `Content-Type: text/event-stream`, to initiate an SSE stream, or
95+
`Content-Type: application/json`, to return one JSON object. The client **MUST**
96+
support both these cases.
97+
6. If the server initiates an SSE stream:
98+
- The SSE stream **SHOULD** eventually include one JSON-RPC _response_ per each
99+
JSON-RPC _request_ sent in the POST body. These _responses_ **MAY** be
100+
[batched](https://www.jsonrpc.org/specification#batch).
101+
- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending a
102+
JSON-RPC _response_. These messages **SHOULD** relate to the originating client
103+
_request_. These _requests_ and _notifications_ **MAY** be
104+
[batched](https://www.jsonrpc.org/specification#batch).
105+
- The server **SHOULD NOT** close the SSE stream before sending a JSON-RPC _response_
106+
per each received JSON-RPC _request_, unless the [session](#session-management)
107+
expires.
108+
- After all JSON-RPC _responses_ have been sent, the server **SHOULD** close the SSE
109+
stream.
110+
- Disconnection **MAY** occur at any time (e.g., due to network conditions).
111+
Therefore:
112+
- Disconnection **SHOULD NOT** be interpreted as the client cancelling its request.
113+
- To cancel, the client **SHOULD** explicitly send an MCP `CancelledNotification`.
114+
- To avoid message loss due to disconnection, the server **MAY** make the stream
115+
[resumable](#resumability-and-redelivery).
116+
117+
### Listening for Messages from the Server
118+
119+
1. The client **MAY** issue an HTTP GET to the MCP endpoint. This can be used to open an
120+
SSE stream, allowing the server to communicate to the client, without the client first
121+
sending data via HTTP POST.
122+
2. The client **MUST** include an `Accept` header, listing `text/event-stream` as a
123+
supported content type.
124+
3. The server **MUST** either return `Content-Type: text/event-stream` in response to
125+
this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server
126+
does not offer an SSE stream at this endpoint.
127+
4. If the server initiates an SSE stream:
128+
- The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream. These
129+
_requests_ and _notifications_ **MAY** be
130+
[batched](https://www.jsonrpc.org/specification#batch).
131+
- These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC
132+
_request_ from the client.
133+
- The server **MUST NOT** send a JSON-RPC _response_ on the stream **unless**
134+
[resuming](#resumability-and-redelivery) a stream associated with a previous client
135+
request.
136+
- The server **MAY** close the SSE stream at any time.
137+
- The client **MAY** close the SSE stream at any time.
124138

125139
### Multiple Connections
126140

0 commit comments

Comments
 (0)