Skip to content

feat(auth-middleware): add example MCP server with JWT and API key #339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 255 additions & 0 deletions examples/server/auth-middleware/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
# MCP Server with Auth Middleware

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.

## Features

The server provides authentication and authorization capabilities for MCP tools:

### 1. Authentication Methods

- **JWT Token Authentication**: JSON Web Token-based authentication
- **API Key Authentication**: API key-based authentication
- **Scope-based Access Control**: Permission-based access to MCP tools

### 2. MCP Integration

- **Authenticated MCP Tools**: Tools that require authentication and check permissions
- **Token Generation**: Utility endpoints for generating test tokens
- **Middleware Integration**: Seamless integration with MCP server handlers

## Setup

```bash
cd examples/server/auth-middleware
go mod tidy
go run main.go
```

## Testing

```bash
# Run all tests
go test -v

# Run benchmark tests
go test -bench=.

# Generate coverage report
go test -cover
```

## Endpoints

### Public Endpoints (No Authentication Required)

- `GET /health` - Health check

### MCP Endpoints (Authentication Required)

- `POST /mcp/jwt` - MCP server with JWT authentication
- `POST /mcp/apikey` - MCP server with API key authentication

### Utility Endpoints

- `GET /generate-token` - Generate JWT token
- `POST /generate-api-key` - Generate API key

## Available MCP Tools

The server provides three authenticated MCP tools:

### 1. Say Hi (`say_hi`)

A simple greeting tool that requires authentication.

**Parameters:**
- None required

**Required Scopes:**
- Any authenticated user

### 2. Get User Info (`get_user_info`)

Retrieves user information based on the provided user ID.

**Parameters:**
- `user_id` (string): The user ID to get information for

**Required Scopes:**
- `read` permission

### 3. Create Resource (`create_resource`)

Creates a new resource with the provided details.

**Parameters:**
- `name` (string): The name of the resource
- `description` (string): The description of the resource
- `content` (string): The content of the resource

**Required Scopes:**
- `write` permission

## Example Usage

### 1. Generating JWT Token and Using MCP Tools

```bash
# Generate a token
curl 'http://localhost:8080/generate-token?user_id=alice&scopes=read,write'

# Use MCP tool with JWT authentication
curl -H 'Authorization: Bearer <generated_token>' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"say_hi","arguments":{}}}' \
http://localhost:8080/mcp/jwt
```

### 2. Generating API Key and Using MCP Tools

```bash
# Generate an API key
curl -X POST 'http://localhost:8080/generate-api-key?user_id=bob&scopes=read'

# Use MCP tool with API key authentication
curl -H 'Authorization: Bearer <generated_api_key>' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_user_info","arguments":{"user_id":"test"}}}' \
http://localhost:8080/mcp/apikey
```

### 3. Testing Scope Restrictions

```bash
# Access MCP tool requiring write scope
curl -H 'Authorization: Bearer <token_with_write_scope>' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_resource","arguments":{"name":"test","description":"test resource","content":"test content"}}}' \
http://localhost:8080/mcp/jwt
```

## Core Concepts

### Authentication Integration

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.

### Key Features

1. **MCP Server Integration**: Create MCP server using `mcp.NewServer`
2. **Authentication Middleware**: Protect MCP handlers with `auth.RequireBearerToken`
3. **Token Verification**: Validate tokens using provided `TokenVerifier` functions
4. **Scope Checking**: Verify required permissions (scopes) are present
5. **Expiration Validation**: Check that tokens haven't expired
6. **Context Injection**: Add verified token information to request context
7. **Authenticated MCP Tools**: Tools that operate based on authentication information
8. **Error Handling**: Return appropriate HTTP status codes and error messages on authentication failure

### Implementation

```go
// Create MCP server
server := mcp.NewServer(&mcp.Implementation{Name: "authenticated-mcp-server"}, nil)

// Create authentication middleware
authMiddleware := auth.RequireBearerToken(verifier, &auth.RequireBearerTokenOptions{
Scopes: []string{"read", "write"},
})

// Create MCP handler
handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
return server
}, nil)

// Apply authentication middleware to MCP handler
authenticatedHandler := authMiddleware(customMiddleware(handler))
```

### Parameters

- **verifier**: Function to verify tokens (`TokenVerifier` type)
- **opts**: Authentication options
- `Scopes`: List of required permissions
- `ResourceMetadataURL`: OAuth 2.0 resource metadata URL

### Error Responses

- **401 Unauthorized**: Token is invalid, expired, or missing
- **403 Forbidden**: Required scopes are insufficient
- **WWW-Authenticate Header**: Included when resource metadata URL is configured

## Implementation Details

### 1. TokenVerifier Implementation

```go
func jwtVerifier(ctx context.Context, tokenString string) (*auth.TokenInfo, error) {
// JWT token verification logic
// On success: Return TokenInfo
// On failure: Return auth.ErrInvalidToken
}
```

### 2. Using Authentication Information in MCP Tools

```go
// Get authentication information in MCP tool
func MyTool(ctx context.Context, req *mcp.ServerRequest[*mcp.CallToolParamsFor[MyArgs]]) (*mcp.CallToolResultFor[struct{}], error) {
// Extract authentication info from context
userInfo := ctx.Value("user_info").(*auth.TokenInfo)

// Check scopes
hasReadScope := false
for _, scope := range userInfo.Scopes {
if scope == "read" {
hasReadScope = true
break
}
}

if !hasReadScope {
return nil, fmt.Errorf("insufficient permissions: read scope required")
}

// Execute tool logic
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Tool executed successfully"},
},
}, nil
}
```

### 3. Middleware Composition

```go
// Combine authentication middleware with custom middleware
authenticatedHandler := authMiddleware(customMiddleware(mcpHandler))
```

## Security Best Practices

1. **Environment Variables**: Use environment variables for JWT secrets in production
2. **Database Storage**: Store API keys in a database
3. **HTTPS Usage**: Always use HTTPS in production environments
4. **Token Expiration**: Set appropriate token expiration times
5. **Principle of Least Privilege**: Grant only the minimum required scopes

## Use Cases

**Ideal for:**

- MCP servers requiring authentication and authorization
- Applications needing scope-based access control
- Systems requiring both JWT and API key authentication
- Projects needing secure MCP tool access
- Scenarios requiring audit trails and permission management

**Examples:**

- Enterprise MCP servers with user management
- Multi-tenant MCP applications
- Secure API gateways with MCP integration
- Development environments with authentication requirements
- Production systems requiring fine-grained access control
15 changes: 15 additions & 0 deletions examples/server/auth-middleware/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module auth-middleware-example

go 1.23.0

require (
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/modelcontextprotocol/go-sdk v0.0.0
)

require (
github.com/google/jsonschema-go v0.2.0 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
)

replace github.com/modelcontextprotocol/go-sdk => ../../../
10 changes: 10 additions & 0 deletions examples/server/auth-middleware/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/jsonschema-go v0.2.0 h1:Uh19091iHC56//WOsAd1oRg6yy1P9BpSvpjOL6RcjLQ=
github.com/google/jsonschema-go v0.2.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
Loading