Skip to content

Commit c76b6be

Browse files
committed
docs: add bearer auth server example
1 parent 161834d commit c76b6be

3 files changed

Lines changed: 172 additions & 0 deletions

File tree

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,67 @@ For a complete example with separate Authorization Server and Resource Server im
10671067

10681068
See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation.
10691069

1070+
For a minimal bearer-token server, validate the `Authorization` header in a
1071+
`TokenVerifier`. The SDK applies the verifier to both SSE and Streamable HTTP
1072+
requests, and tools can read the accepted token from `get_access_token()`:
1073+
1074+
<!-- snippet-source examples/snippets/servers/bearer_auth_server.py -->
1075+
```python
1076+
"""Run from the repository root:
1077+
uv run examples/snippets/servers/bearer_auth_server.py
1078+
"""
1079+
1080+
from pydantic import AnyHttpUrl
1081+
1082+
from mcp.server.auth.middleware.auth_context import get_access_token
1083+
from mcp.server.auth.provider import AccessToken, TokenVerifier
1084+
from mcp.server.auth.settings import AuthSettings
1085+
from mcp.server.mcpserver import MCPServer
1086+
1087+
1088+
class StaticTokenVerifier(TokenVerifier):
1089+
"""Accept a single bearer token for demonstration purposes."""
1090+
1091+
async def verify_token(self, token: str) -> AccessToken | None:
1092+
if token != "secret-token":
1093+
return None
1094+
1095+
return AccessToken(
1096+
token=token,
1097+
client_id="demo-client",
1098+
scopes=["user"],
1099+
)
1100+
1101+
1102+
mcp = MCPServer(
1103+
"Bearer auth demo",
1104+
token_verifier=StaticTokenVerifier(),
1105+
auth=AuthSettings(
1106+
issuer_url=AnyHttpUrl("https://auth.example.com"),
1107+
resource_server_url=AnyHttpUrl("http://localhost:8000"),
1108+
required_scopes=["user"],
1109+
),
1110+
)
1111+
1112+
1113+
@mcp.tool()
1114+
async def whoami() -> str:
1115+
"""Return the authenticated client id."""
1116+
access_token = get_access_token()
1117+
if access_token is None:
1118+
# The auth middleware rejects unauthenticated requests before tools run.
1119+
raise ValueError("No access token found")
1120+
1121+
return f"Authenticated as {access_token.client_id}"
1122+
1123+
1124+
if __name__ == "__main__":
1125+
mcp.run(transport="streamable-http")
1126+
```
1127+
1128+
_Full example: [examples/snippets/servers/bearer_auth_server.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/bearer_auth_server.py)_
1129+
<!-- /snippet-source -->
1130+
10701131
### FastMCP Properties
10711132

10721133
The FastMCP server instance accessible via `ctx.fastmcp` provides access to server configuration and metadata:

README.v2.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,67 @@ For a complete example with separate Authorization Server and Resource Server im
10581058

10591059
See [TokenVerifier](src/mcp/server/auth/provider.py) for more details on implementing token validation.
10601060

1061+
For a minimal bearer-token server, validate the `Authorization` header in a
1062+
`TokenVerifier`. The SDK applies the verifier to both SSE and Streamable HTTP
1063+
requests, and tools can read the accepted token from `get_access_token()`:
1064+
1065+
<!-- snippet-source examples/snippets/servers/bearer_auth_server.py -->
1066+
```python
1067+
"""Run from the repository root:
1068+
uv run examples/snippets/servers/bearer_auth_server.py
1069+
"""
1070+
1071+
from pydantic import AnyHttpUrl
1072+
1073+
from mcp.server.auth.middleware.auth_context import get_access_token
1074+
from mcp.server.auth.provider import AccessToken, TokenVerifier
1075+
from mcp.server.auth.settings import AuthSettings
1076+
from mcp.server.mcpserver import MCPServer
1077+
1078+
1079+
class StaticTokenVerifier(TokenVerifier):
1080+
"""Accept a single bearer token for demonstration purposes."""
1081+
1082+
async def verify_token(self, token: str) -> AccessToken | None:
1083+
if token != "secret-token":
1084+
return None
1085+
1086+
return AccessToken(
1087+
token=token,
1088+
client_id="demo-client",
1089+
scopes=["user"],
1090+
)
1091+
1092+
1093+
mcp = MCPServer(
1094+
"Bearer auth demo",
1095+
token_verifier=StaticTokenVerifier(),
1096+
auth=AuthSettings(
1097+
issuer_url=AnyHttpUrl("https://auth.example.com"),
1098+
resource_server_url=AnyHttpUrl("http://localhost:8000"),
1099+
required_scopes=["user"],
1100+
),
1101+
)
1102+
1103+
1104+
@mcp.tool()
1105+
async def whoami() -> str:
1106+
"""Return the authenticated client id."""
1107+
access_token = get_access_token()
1108+
if access_token is None:
1109+
# The auth middleware rejects unauthenticated requests before tools run.
1110+
raise ValueError("No access token found")
1111+
1112+
return f"Authenticated as {access_token.client_id}"
1113+
1114+
1115+
if __name__ == "__main__":
1116+
mcp.run(transport="streamable-http")
1117+
```
1118+
1119+
_Full example: [examples/snippets/servers/bearer_auth_server.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/bearer_auth_server.py)_
1120+
<!-- /snippet-source -->
1121+
10611122
### MCPServer Properties
10621123

10631124
The MCPServer server instance accessible via `ctx.mcp_server` provides access to server configuration and metadata:
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Run from the repository root:
2+
uv run examples/snippets/servers/bearer_auth_server.py
3+
"""
4+
5+
from pydantic import AnyHttpUrl
6+
7+
from mcp.server.auth.middleware.auth_context import get_access_token
8+
from mcp.server.auth.provider import AccessToken, TokenVerifier
9+
from mcp.server.auth.settings import AuthSettings
10+
from mcp.server.mcpserver import MCPServer
11+
12+
13+
class StaticTokenVerifier(TokenVerifier):
14+
"""Accept a single bearer token for demonstration purposes."""
15+
16+
async def verify_token(self, token: str) -> AccessToken | None:
17+
if token != "secret-token":
18+
return None
19+
20+
return AccessToken(
21+
token=token,
22+
client_id="demo-client",
23+
scopes=["user"],
24+
)
25+
26+
27+
mcp = MCPServer(
28+
"Bearer auth demo",
29+
token_verifier=StaticTokenVerifier(),
30+
auth=AuthSettings(
31+
issuer_url=AnyHttpUrl("https://auth.example.com"),
32+
resource_server_url=AnyHttpUrl("http://localhost:8000"),
33+
required_scopes=["user"],
34+
),
35+
)
36+
37+
38+
@mcp.tool()
39+
async def whoami() -> str:
40+
"""Return the authenticated client id."""
41+
access_token = get_access_token()
42+
if access_token is None:
43+
# The auth middleware rejects unauthenticated requests before tools run.
44+
raise ValueError("No access token found")
45+
46+
return f"Authenticated as {access_token.client_id}"
47+
48+
49+
if __name__ == "__main__":
50+
mcp.run(transport="streamable-http")

0 commit comments

Comments
 (0)