Skip to content

Commit 20484e3

Browse files
authored
Merge branch 'main' into patch-1
2 parents cf27594 + 05f2045 commit 20484e3

File tree

9 files changed

+324
-62
lines changed

9 files changed

+324
-62
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ The following software is required to work on the spec:
1919
2. Clone your fork:
2020

2121
```bash
22-
git clone https://github.com/YOUR-USERNAME/specification.git
23-
cd specification
22+
git clone https://github.com/YOUR-USERNAME/modelcontextprotocol.git
23+
cd modelcontextprotocol
2424
```
2525

2626
3. Install dependencies:

docs/clients.mdx

Lines changed: 147 additions & 1 deletion
Large diffs are not rendered by default.

docs/docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
"specification/draft/basic/lifecycle",
189189
"specification/draft/basic/transports",
190190
"specification/draft/basic/authorization",
191+
"specification/draft/basic/security_best_practices",
191192
{
192193
"group": "Utilities",
193194
"pages": [

docs/examples.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ These MCP servers are maintained by companies for their platforms:
4242

4343
- **[Axiom](https://github.com/axiomhq/mcp-server-axiom)** - Query and analyze logs, traces, and event data using natural language
4444
- **[Browserbase](https://github.com/browserbase/mcp-server-browserbase)** - Automate browser interactions in the cloud
45+
- **[BrowserStack](https://github.com/browserstack/mcp-server)** - Access BrowserStack's [Test Platform](https://www.browserstack.com/test-platform) to debug, write and fix tests, do accessibility testing and more.
4546
- **[Cloudflare](https://github.com/cloudflare/mcp-server-cloudflare)** - Deploy and manage resources on the Cloudflare developer platform
4647
- **[E2B](https://github.com/e2b-dev/mcp-server)** - Execute code in secure cloud sandboxes
4748
- **[Neon](https://github.com/neondatabase/mcp-server-neon)** - Interact with the Neon serverless Postgres platform

docs/quickstart/client.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ source .venv/bin/activate
3939
uv add mcp anthropic python-dotenv
4040

4141
# Remove boilerplate files
42+
# On Windows:
43+
del main.py
44+
# On Unix or MacOS:
4245
rm main.py
4346

4447
# Create our main file

docs/specification/draft/basic/authorization.mdx

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ as described in OAuth 2.0 Protected Resource Metadata ([RFC9728](https://datatra
9999

100100
MCP clients **MUST** be able to parse `WWW-Authenticate` headers and respond appropriately to `HTTP 401 Unauthorized` responses from the MCP server.
101101

102-
103102
#### 2.3.2 Server Metadata Discovery
104103

105104
MCP clients **MUST** follow the OAuth 2.0 Authorization Server Metadata protocol defined
@@ -247,17 +246,8 @@ own resources.
247246

248247
MCP servers **MUST NOT** accept or transit any other tokens.
249248

250-
### 2.8 Security Considerations
251-
252-
The following security requirements **MUST** be implemented:
253249

254-
1. Clients **MUST** securely store tokens following OAuth 2.0 best practices
255-
2. Servers **SHOULD** enforce token expiration and rotation
256-
3. All authorization endpoints **MUST** be served over HTTPS
257-
4. Servers **MUST** validate redirect URIs to prevent open redirect vulnerabilities
258-
5. Redirect URIs **MUST** be either localhost URLs or HTTPS URLs
259-
260-
### 2.9 Error Handling
250+
### 2.8 Error Handling
261251

262252
Servers **MUST** return appropriate HTTP status codes for authorization errors:
263253

@@ -267,35 +257,50 @@ Servers **MUST** return appropriate HTTP status codes for authorization errors:
267257
| 403 | Forbidden | Invalid scopes or insufficient permissions |
268258
| 400 | Bad Request | Malformed authorization request |
269259

270-
### 2.10 Implementation Requirements
260+
## 3. Security Considerations
261+
262+
Implementations **MUST** follow OAuth 2.1 security best practices as laid out in [Section 7. Security Considerations](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#name-security-considerations).
263+
264+
### 3.1 Token Theft
265+
Attackers who obtain tokens stored by the client, or tokens cached or logged on the server can access protected resources with
266+
requests that appear legitimate to resource servers.
267+
268+
Clients **MUST** implement secure token storage and follow OAuth best practices,
269+
as outlined in [OAuth 2.1, section 7.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-7.1).
270+
271+
MCP authorization servers SHOULD issue short-lived access tokens token to reduce the impact of leaked tokens. For public clients, MCP authorization servers MUST rotate refresh tokens as described in [Section 4.3.1 of OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-4.3.1).
272+
273+
### 3.2 Communication Security
274+
Implementations MUST follow [OAuth 2.1 section 1.5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-1.5).
275+
276+
Specifically:
277+
1. All authorization server endpoints **MUST** be served over HTTPS.
278+
1. All redirect URIs **MUST** be either `localhost` or use HTTPS.
279+
280+
### 3.3 Authorization Code Protection
281+
282+
An attacker who has gained access to an authorization code contained in an authorization response can try to redeem the authorization code for an access token or otherwise make use of the authorization code. (Further described in [OAuth 2.1, section 7.5](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-7.5))
283+
284+
MCP clients **MUST** implement PKCE according to [OAuth 2.1 section 7.5.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-7.5.2). PKCE helps prevent authorization code interception and injection attacks by requiring clients to create a secret verifier-challenge pair, ensuring that only the original requestor can exchange an authorization code for tokens.
285+
271286

272-
1. Implementations **MUST** follow OAuth 2.1 security best practices
273-
1. PKCE is **REQUIRED** for all MCP clients and authorization servers
274-
1. MCP servers that also act as an AS:
275-
1. **SHOULD** implement token rotation for enhanced security
276-
1. **SHOULD** restrict token lifetimes based on security requirements
287+
### 3.3 Open Redirection
288+
An attacker may craft malicious redirect URIs to direct users to phishing sites.
277289

278-
## 3. Best Practices
290+
MCP clients **MUST** have redirect URIs registered with the authorization server.
279291

280-
#### 3.1 Local clients as Public OAuth 2.1 Clients
292+
Authorization servers **MUST** validate exact redirect URIs against pre-registered values to prevent redirection attacks.
281293

282-
We strongly recommend that local clients implement OAuth 2.1 as a public client:
294+
MCP clients **SHOULD** use and verify state parameters in the authorization code flow
295+
and discard any results that do not include or have a mis-match with the original state.
283296

284-
1. Utilizing code challenges (PKCE) for authorization requests to prevent interception
285-
attacks
286-
2. Implementing secure token storage appropriate for the local system
287-
3. Following token refresh best practices to maintain sessions
288-
4. Properly handling token expiration and renewal
297+
Authorization servers **MUST** take precautions to prevent redirecting user agents to untrusted URI's, following suggestions laid out in [OAuth 2.1, Section 7.12.2](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-7.12.2)
289298

290-
#### 3.2 Authorization Metadata Discovery
299+
Authorization servers **SHOULD** only automatically redirect the user agent if it trusts the redirection URI. If the URI is not trusted, the authorization server MAY inform the user and rely on the user to make the correct decision.
291300

292-
We strongly recommend that all clients implement metadata discovery. This reduces the
293-
need for users to provide endpoints manually or clients to fallback to the defined
294-
defaults.
301+
### 3.4 Confused Deputy Problem
295302

296-
#### 3.3 Dynamic Client Registration
303+
Attackers can exploit MCP servers acting as intermediaries to third-party APIs, leading to confused deputy vulnerabilities. By using stolen authorization codes, they can obtain access tokens without user consent. See [Security Best Practices 2.1](/specification/draft/basic/security_best_practices) for details.
297304

298-
Since clients do not know the set of MCP servers in advance, we strongly recommend the
299-
implementation of dynamic client registration. This allows applications to automatically
300-
register with the MCP server, and removes the need for users to obtain client ids
301-
manually.
305+
MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically
306+
registered client before forwarding to third-party authorization servers (which may require additional consent).
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: Security Best Practices
3+
---
4+
5+
## 1. Introduction
6+
7+
### 1.1 Purpose and Scope
8+
9+
This document provides security considerations for the Model Context Protocol (MCP), complementing the MCP Authorization specification. This document identifies security risks, attack vectors, and best practices specific to MCP implementations.
10+
11+
The primary audience for this document includes developers implementing MCP authorization flows, MCP server operators, and security professionals evaluating MCP-based systems. This document should be read alongside the MCP Authorization specification and [OAuth 2.0 security best practices](https://datatracker.ietf.org/doc/html/rfc9700).
12+
13+
## 2. Attacks and Mitigations
14+
15+
This section gives a detailed description of attacks on MCP implementations, along with potential countermeasures.
16+
17+
### 2.1 Confused Deputy Problem
18+
19+
Attackers can exploit MCP servers proxying other resource servers, creating "[confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)" vulnerabilities.
20+
21+
#### 2.1.1 Terminology
22+
23+
**MCP Proxy Server**
24+
: An MCP server that connects MCP clients to third-party APIs, offering MCP features while delegating operations and acting as a single OAuth client to the third-party API server.
25+
26+
**Third-Party Authorization Server**
27+
: Authorization server that protects the third-party API. It may lack dynamic client registration support, requiring MCP proxy to use a static client ID for all requests.
28+
29+
**Third-Party API**
30+
: The protected resource server that provides the actual API functionality. Access to this
31+
API requires tokens issued by the third-party authorization server.
32+
33+
**Static Client ID**
34+
: A fixed OAuth 2.0 client identifier used by the MCP proxy server when communicating with
35+
the third-party authorization server. This Client ID refers to the MCP server acting as a client
36+
to the Third-Party API. It is the same value for all MCP server to Third-Party API interactions regardless of
37+
which MCP client initiated the request.
38+
39+
#### 2.1.2 Architecture and Attack Flows
40+
41+
##### 2.1.2.1 Normal OAuth proxy usage (preserves user consent)
42+
43+
```mermaid
44+
sequenceDiagram
45+
participant UA as User-Agent (Browser)
46+
participant MC as MCP Client
47+
participant M as MCP Proxy Server
48+
participant TAS as Third-Party Authorization Server
49+
50+
Note over UA,M: Initial Auth flow completed
51+
52+
Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server
53+
54+
M->>UA: Redirect to third party authorization server
55+
UA->>TAS: Authorization request (client_id: mcp-proxy)
56+
TAS->>UA: Authorization consent screen
57+
Note over UA: Review consent screen
58+
UA->>TAS: Approve
59+
TAS->>UA: Set consent cookie for client ID: mcp-proxy
60+
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
61+
UA->>M: 3P Authorization code
62+
Note over M,TAS: Exchange 3P code for 3P token
63+
Note over M: Generate MCP authorization code
64+
M->>UA: Redirect to MCP Client with MCP authorization code
65+
66+
Note over M,UA: Exchange code for token, etc.
67+
```
68+
69+
##### 2.1.2.3 Malicious OAuth proxy usage (skips user consent)
70+
71+
```mermaid
72+
sequenceDiagram
73+
participant UA as User-Agent (Browser)
74+
participant M as MCP Proxy Server
75+
participant TAS as Third-Party Authorization Server
76+
participant A as Attacker
77+
78+
79+
Note over UA,A: Step 2: Attack (leveraging existing cookie, skipping consent)
80+
A->>M: Dynamically register malicious client, redirect_uri: attacker.com
81+
A->>UA: Sends malicious link
82+
UA->>TAS: Authorization request (client_id: mcp-proxy) + consent cookie
83+
rect rgba(255, 17, 0, 0.67)
84+
TAS->>TAS: Cookie present, consent skipped
85+
end
86+
87+
TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
88+
UA->>M: 3P Authorization code
89+
Note over M,TAS: Exchange 3P code for 3P token
90+
Note over M: Generate MCP authorization code
91+
M->>UA: Redirect to attacker.com with MCP Authorization code
92+
UA->>A: MCP Authorization code delivered to attacker.com
93+
Note over M,A: Attacker exchanges MCP code for MCP token
94+
A->>M: Attacker impersonates user to MCP server
95+
```
96+
97+
#### 2.1.3 Attack Description
98+
99+
When an MCP proxy server uses a static client ID to authenticate with a third-party
100+
authorization server that does not support dynamic client registration, the following
101+
attack becomes possible:
102+
103+
1. A user authenticates normally through the MCP proxy server to access the third-party API
104+
2. During this flow, the third-party authorization server sets a cookie on the user agent
105+
indicating consent for the static client ID
106+
3. An attacker later sends the user a malicious link containing a crafted authorization request which contains a malicious redirect URI along with a new dynamically registered client ID
107+
4. When the user clicks the link, their browser still has the consent cookie from the previous legitimate request
108+
5. The third-party authorization server detects the cookie and skips the consent screen
109+
6. The MCP authorization code is redirected to the attacker's server (specified in the crafted redirect_uri during dynamic client registration)
110+
7. The attacker exchanges the stolen authorization code for access tokens for the MCP server without the user's explicit approval
111+
8. Attacker now has access to the third-party API as the compromised user
112+
113+
#### 2.1.4 Mitigation
114+
115+
MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically
116+
registered client before forwarding to third-party authorization servers (which may require additional consent).

docs/specification/draft/basic/transports.mdx

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ In the **stdio** transport:
2424
- The client launches the MCP server as a subprocess.
2525
- The server reads JSON-RPC messages from its standard input (`stdin`) and sends messages
2626
to its standard output (`stdout`).
27-
- Messages may be JSON-RPC requests, notifications, responses—or a JSON-RPC
28-
[batch](https://www.jsonrpc.org/specification#batch) containing one or more requests
29-
and/or notifications.
27+
- Messages are individual JSON-RPC requests, notifications, or responses.
3028
- Messages are delimited by newlines, and **MUST NOT** contain embedded newlines.
3129
- The server **MAY** write UTF-8 strings to its standard error (`stderr`) for logging
3230
purposes. Clients **MAY** capture, forward, or ignore this logging.
@@ -85,35 +83,27 @@ MCP endpoint.
8583
1. The client **MUST** use HTTP POST to send JSON-RPC messages to the MCP endpoint.
8684
2. The client **MUST** include an `Accept` header, listing both `application/json` and
8785
`text/event-stream` as supported content types.
88-
3. The body of the POST request **MUST** be one of the following:
89-
- A single JSON-RPC _request_, _notification_, or _response_
90-
- An array [batching](https://www.jsonrpc.org/specification#batch) one or more
91-
_requests and/or notifications_
92-
- An array [batching](https://www.jsonrpc.org/specification#batch) one or more
93-
_responses_
94-
4. If the input consists solely of (any number of) JSON-RPC _responses_ or
95-
_notifications_:
86+
3. The body of the POST request **MUST** be a single JSON-RPC _request_, _notification_, or _response_.
87+
4. If the input is a JSON-RPC _response_ or _notification_:
9688
- If the server accepts the input, the server **MUST** return HTTP status code 202
9789
Accepted with no body.
9890
- If the server cannot accept the input, it **MUST** return an HTTP error status code
9991
(e.g., 400 Bad Request). The HTTP response body **MAY** comprise a JSON-RPC _error
10092
response_ that has no `id`.
101-
5. If the input contains any number of JSON-RPC _requests_, the server **MUST** either
93+
5. If the input is a JSON-RPC _request_, the server **MUST** either
10294
return `Content-Type: text/event-stream`, to initiate an SSE stream, or
10395
`Content-Type: application/json`, to return one JSON object. The client **MUST**
10496
support both these cases.
10597
6. If the server initiates an SSE stream:
106-
- The SSE stream **SHOULD** eventually include one JSON-RPC _response_ per each
107-
JSON-RPC _request_ sent in the POST body. These _responses_ **MAY** be
108-
[batched](https://www.jsonrpc.org/specification#batch).
109-
- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending a
98+
- The SSE stream **SHOULD** eventually include JSON-RPC _response_ for the
99+
JSON-RPC _request_ sent in the POST body.
100+
- The server **MAY** send JSON-RPC _requests_ and _notifications_ before sending the
110101
JSON-RPC _response_. These messages **SHOULD** relate to the originating client
111-
_request_. These _requests_ and _notifications_ **MAY** be
112-
[batched](https://www.jsonrpc.org/specification#batch).
113-
- The server **SHOULD NOT** close the SSE stream before sending a JSON-RPC _response_
114-
per each received JSON-RPC _request_, unless the [session](#session-management)
102+
_request_.
103+
- The server **SHOULD NOT** close the SSE stream before sending the JSON-RPC _response_
104+
for the received JSON-RPC _request_, unless the [session](#session-management)
115105
expires.
116-
- After all JSON-RPC _responses_ have been sent, the server **SHOULD** close the SSE
106+
- After the JSON-RPC _response_ has been sent, the server **SHOULD** close the SSE
117107
stream.
118108
- Disconnection **MAY** occur at any time (e.g., due to network conditions).
119109
Therefore:
@@ -133,9 +123,7 @@ MCP endpoint.
133123
this HTTP GET, or else return HTTP 405 Method Not Allowed, indicating that the server
134124
does not offer an SSE stream at this endpoint.
135125
4. If the server initiates an SSE stream:
136-
- The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream. These
137-
_requests_ and _notifications_ **MAY** be
138-
[batched](https://www.jsonrpc.org/specification#batch).
126+
- The server **MAY** send JSON-RPC _requests_ and _notifications_ on the stream.
139127
- These messages **SHOULD** be unrelated to any concurrently-running JSON-RPC
140128
_request_ from the client.
141129
- The server **MUST NOT** send a JSON-RPC _response_ on the stream **unless**

docs/specification/draft/changelog.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ the previous revision, [2025-03-26](/specification/2025-03-26).
77

88
## Major changes
99

10-
1. TODO
10+
1. Removed support for JSON-RPC **[batching](https://www.jsonrpc.org/specification#batch)**
11+
(PR [#416](https://github.com/modelcontextprotocol/specification/pull/416))
12+
2. TODO
1113

1214
## Other schema changes
1315

0 commit comments

Comments
 (0)