Skip to content

Commit a3935c6

Browse files
authored
examples/server: add example MCP server with JWT and API key (#339)
This commit introduces a new example demonstrating the integration of authentication middleware with an MCP server. The server supports both JWT token and API key authentication, along with scope-based access control for various MCP tools. Key features include token generation endpoints, in-memory API key storage, and a health check endpoint. New files added: - `main.go`: Implements the MCP server and authentication logic. - `go.mod` and `go.sum`: Manage dependencies for the project. - `README.md`: Provides setup instructions, available endpoints, and example usage. This example serves as a reference for implementing secure access to MCP tools. Fixes #330
1 parent be0a00c commit a3935c6

File tree

4 files changed

+664
-0
lines changed

4 files changed

+664
-0
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# MCP Server with Auth Middleware
2+
3+
This example demonstrates how to integrate the Go MCP SDK's `auth.RequireBearerToken` middleware with an MCP server to provide authenticated access to MCP tools and resources.
4+
5+
## Features
6+
7+
The server provides authentication and authorization capabilities for MCP tools:
8+
9+
### 1. Authentication Methods
10+
11+
- **JWT Token Authentication**: JSON Web Token-based authentication
12+
- **API Key Authentication**: API key-based authentication
13+
- **Scope-based Access Control**: Permission-based access to MCP tools
14+
15+
### 2. MCP Integration
16+
17+
- **Authenticated MCP Tools**: Tools that require authentication and check permissions
18+
- **Token Generation**: Utility endpoints for generating test tokens
19+
- **Middleware Integration**: Seamless integration with MCP server handlers
20+
21+
## Setup
22+
23+
```bash
24+
cd examples/server/auth-middleware
25+
go mod tidy
26+
go run main.go
27+
```
28+
29+
## Testing
30+
31+
```bash
32+
# Run all tests
33+
go test -v
34+
35+
# Run benchmark tests
36+
go test -bench=.
37+
38+
# Generate coverage report
39+
go test -cover
40+
```
41+
42+
## Endpoints
43+
44+
### Public Endpoints (No Authentication Required)
45+
46+
- `GET /health` - Health check
47+
48+
### MCP Endpoints (Authentication Required)
49+
50+
- `POST /mcp/jwt` - MCP server with JWT authentication
51+
- `POST /mcp/apikey` - MCP server with API key authentication
52+
53+
### Utility Endpoints
54+
55+
- `GET /generate-token` - Generate JWT token
56+
- `POST /generate-api-key` - Generate API key
57+
58+
## Available MCP Tools
59+
60+
The server provides three authenticated MCP tools:
61+
62+
### 1. Say Hi (`say_hi`)
63+
64+
A simple greeting tool that requires authentication.
65+
66+
**Parameters:**
67+
- None required
68+
69+
**Required Scopes:**
70+
- Any authenticated user
71+
72+
### 2. Get User Info (`get_user_info`)
73+
74+
Retrieves user information based on the provided user ID.
75+
76+
**Parameters:**
77+
- `user_id` (string): The user ID to get information for
78+
79+
**Required Scopes:**
80+
- `read` permission
81+
82+
### 3. Create Resource (`create_resource`)
83+
84+
Creates a new resource with the provided details.
85+
86+
**Parameters:**
87+
- `name` (string): The name of the resource
88+
- `description` (string): The description of the resource
89+
- `content` (string): The content of the resource
90+
91+
**Required Scopes:**
92+
- `write` permission
93+
94+
## Example Usage
95+
96+
### 1. Generating JWT Token and Using MCP Tools
97+
98+
```bash
99+
# Generate a token
100+
curl 'http://localhost:8080/generate-token?user_id=alice&scopes=read,write'
101+
102+
# Use MCP tool with JWT authentication
103+
curl -H 'Authorization: Bearer <generated_token>' \
104+
-H 'Content-Type: application/json' \
105+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"say_hi","arguments":{}}}' \
106+
http://localhost:8080/mcp/jwt
107+
```
108+
109+
### 2. Generating API Key and Using MCP Tools
110+
111+
```bash
112+
# Generate an API key
113+
curl -X POST 'http://localhost:8080/generate-api-key?user_id=bob&scopes=read'
114+
115+
# Use MCP tool with API key authentication
116+
curl -H 'Authorization: Bearer <generated_api_key>' \
117+
-H 'Content-Type: application/json' \
118+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_user_info","arguments":{"user_id":"test"}}}' \
119+
http://localhost:8080/mcp/apikey
120+
```
121+
122+
### 3. Testing Scope Restrictions
123+
124+
```bash
125+
# Access MCP tool requiring write scope
126+
curl -H 'Authorization: Bearer <token_with_write_scope>' \
127+
-H 'Content-Type: application/json' \
128+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_resource","arguments":{"name":"test","description":"test resource","content":"test content"}}}' \
129+
http://localhost:8080/mcp/jwt
130+
```
131+
132+
## Core Concepts
133+
134+
### Authentication Integration
135+
136+
This example demonstrates how to integrate `auth.RequireBearerToken` middleware with an MCP server to provide authenticated access. The MCP server operates as an HTTP handler protected by authentication middleware.
137+
138+
### Key Features
139+
140+
1. **MCP Server Integration**: Create MCP server using `mcp.NewServer`
141+
2. **Authentication Middleware**: Protect MCP handlers with `auth.RequireBearerToken`
142+
3. **Token Verification**: Validate tokens using provided `TokenVerifier` functions
143+
4. **Scope Checking**: Verify required permissions (scopes) are present
144+
5. **Expiration Validation**: Check that tokens haven't expired
145+
6. **Context Injection**: Add verified token information to request context
146+
7. **Authenticated MCP Tools**: Tools that operate based on authentication information
147+
8. **Error Handling**: Return appropriate HTTP status codes and error messages on authentication failure
148+
149+
### Implementation
150+
151+
```go
152+
// Create MCP server
153+
server := mcp.NewServer(&mcp.Implementation{Name: "authenticated-mcp-server"}, nil)
154+
155+
// Create authentication middleware
156+
authMiddleware := auth.RequireBearerToken(verifier, &auth.RequireBearerTokenOptions{
157+
Scopes: []string{"read", "write"},
158+
})
159+
160+
// Create MCP handler
161+
handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
162+
return server
163+
}, nil)
164+
165+
// Apply authentication middleware to MCP handler
166+
authenticatedHandler := authMiddleware(customMiddleware(handler))
167+
```
168+
169+
### Parameters
170+
171+
- **verifier**: Function to verify tokens (`TokenVerifier` type)
172+
- **opts**: Authentication options
173+
- `Scopes`: List of required permissions
174+
- `ResourceMetadataURL`: OAuth 2.0 resource metadata URL
175+
176+
### Error Responses
177+
178+
- **401 Unauthorized**: Token is invalid, expired, or missing
179+
- **403 Forbidden**: Required scopes are insufficient
180+
- **WWW-Authenticate Header**: Included when resource metadata URL is configured
181+
182+
## Implementation Details
183+
184+
### 1. TokenVerifier Implementation
185+
186+
```go
187+
func jwtVerifier(ctx context.Context, tokenString string) (*auth.TokenInfo, error) {
188+
// JWT token verification logic
189+
// On success: Return TokenInfo
190+
// On failure: Return auth.ErrInvalidToken
191+
}
192+
```
193+
194+
### 2. Using Authentication Information in MCP Tools
195+
196+
```go
197+
// Get authentication information in MCP tool
198+
func MyTool(ctx context.Context, req *mcp.CallToolRequest, args MyArgs) (*mcp.CallToolResult, any, error) {
199+
// Extract authentication info from request
200+
userInfo := req.Extra.TokenInfo
201+
202+
// Check scopes
203+
if !slices.Contains(userInfo.Scopes, "read") {
204+
return nil, nil, fmt.Errorf("insufficient permissions: read scope required")
205+
}
206+
207+
// Execute tool logic
208+
return &mcp.CallToolResult{
209+
Content: []mcp.Content{
210+
&mcp.TextContent{Text: "Tool executed successfully"},
211+
},
212+
}, nil, nil
213+
}
214+
```
215+
216+
### 3. Middleware Composition
217+
218+
```go
219+
// Combine authentication middleware with custom middleware
220+
authenticatedHandler := authMiddleware(customMiddleware(mcpHandler))
221+
```
222+
223+
## Security Best Practices
224+
225+
1. **Environment Variables**: Use environment variables for JWT secrets in production
226+
2. **Database Storage**: Store API keys in a database
227+
3. **HTTPS Usage**: Always use HTTPS in production environments
228+
4. **Token Expiration**: Set appropriate token expiration times
229+
5. **Principle of Least Privilege**: Grant only the minimum required scopes
230+
231+
## Use Cases
232+
233+
**Ideal for:**
234+
235+
- MCP servers requiring authentication and authorization
236+
- Applications needing scope-based access control
237+
- Systems requiring both JWT and API key authentication
238+
- Projects needing secure MCP tool access
239+
- Scenarios requiring audit trails and permission management
240+
241+
**Examples:**
242+
243+
- Enterprise MCP servers with user management
244+
- Multi-tenant MCP applications
245+
- Secure API gateways with MCP integration
246+
- Development environments with authentication requirements
247+
- Production systems requiring fine-grained access control
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module auth-middleware-example
2+
3+
go 1.23.0
4+
5+
require (
6+
github.com/golang-jwt/jwt/v5 v5.2.2
7+
github.com/modelcontextprotocol/go-sdk v0.3.0
8+
)
9+
10+
require (
11+
github.com/google/jsonschema-go v0.2.1-0.20250825175020-748c325cec76 // indirect
12+
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
13+
)
14+
15+
replace github.com/modelcontextprotocol/go-sdk => ../../../
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
2+
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
3+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
4+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
5+
github.com/google/jsonschema-go v0.2.1-0.20250825175020-748c325cec76 h1:mBlBwtDebdDYr+zdop8N62a44g+Nbv7o2KjWyS1deR4=
6+
github.com/google/jsonschema-go v0.2.1-0.20250825175020-748c325cec76/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
7+
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
8+
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
9+
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
10+
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=

0 commit comments

Comments
 (0)