Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MCP Echo Server

A debugging/testing server for MCP (Model Context Protocol) clients. Implements a full OAuth 2.0 Authorization Server with PKCE support. Logs and echoes all incoming HTTP requests, including headers, body, and connection metadata.

Features

  • Complete OAuth 2.0 Implementation: Authorization Code flow with PKCE
  • Dynamic Client Registration: RFC 7591 support for automatic client registration
  • Complete Request & Response Logging: Logs all HTTP headers, body, status codes
  • Real-time Log Streaming: View logs instantly in terminal
  • MCP Protocol Support: Handles standard MCP JSON-RPC methods with mock responses
  • Custom Token Support: Enter your own tokens for end-to-end tracing
  • No Real Users Required: "Login" with a single click - no username/password needed
  • CORS Enabled: Works with browser-based MCP clients

Quick Start

# Install dependencies
npm install

# Start server (auth optional)
npm run local

# Start server with auth REQUIRED on MCP endpoints
npm run local:auth

# Expose via cloudflared tunnel
npm run tunnel:auth

OAuth 2.0 Authorization Code Flow

The server implements the full OAuth 2.0 Authorization Code flow with PKCE:

┌─────────────────────────────────────────────────────────────────────────────┐
│                    OAuth 2.0 Authorization Code Flow                        │
└─────────────────────────────────────────────────────────────────────────────┘

    MCP Client                          Echo Server                    User
        │                                    │                           │
        │ 1. GET /authorize                  │                           │
        │    ?client_id=...                  │                           │
        │    &redirect_uri=...               │                           │
        │    &code_challenge=...             │                           │
        │    &state=...                      │                           │
        │ ─────────────────────────────────► │                           │
        │                                    │                           │
        │                                    │  2. Show Authorization    │
        │                                    │     Page                  │
        │                                    │ ────────────────────────► │
        │                                    │                           │
        │                                    │  3. User clicks           │
        │                                    │     "Approve"             │
        │                                    │     (+ optional custom    │
        │                                    │      token)               │
        │                                    │ ◄──────────────────────── │
        │                                    │                           │
        │ 4. Redirect to redirect_uri       │                           │
        │    ?code=ABC123                    │                           │
        │    &state=...                      │                           │
        │ ◄───────────────────────────────── │                           │
        │                                    │                           │
        │ 5. POST /token                     │                           │
        │    grant_type=authorization_code   │                           │
        │    code=ABC123                     │                           │
        │    code_verifier=...               │                           │
        │ ─────────────────────────────────► │                           │
        │                                    │                           │
        │ 6. Token Response                  │                           │
        │    { access_token: "user-token" }  │                           │
        │ ◄───────────────────────────────── │                           │
        │                                    │                           │
        │ 7. MCP Request with Bearer token   │                           │
        │    Authorization: Bearer user-token│                           │
        │ ─────────────────────────────────► │                           │
        │                                    │                           │

Why Authorization Code + PKCE?

The token you choose is not returned directly from the authorization page. Instead:

  1. Authorization page returns an opaque code (e.g., ABC123)
  2. Client exchanges the code at /token endpoint
  3. Token endpoint returns your chosen access_token

Why this indirection?

  • The authorization code travels via browser redirect (URL) - URLs can leak via browser history, referrer headers, logs
  • The actual token is returned via a secure POST request (back-channel)
  • PKCE (code_verifier/code_challenge) proves the same client that started the flow is completing it
  • Authorization codes expire in 60 seconds - even if intercepted, useless without the code_verifier

OAuth 2.0 Endpoints

Endpoint Method Description
/.well-known/oauth-authorization-server GET OAuth 2.0 server metadata (RFC 8414)
/register POST Dynamic Client Registration (RFC 7591)
/authorize GET Authorization page - user approves/denies
/token POST Exchange authorization code for access token
/auth GET Token management page (bypass OAuth for testing)
/auth/register POST Register token directly (bypass OAuth)
/auth/tokens GET List all active tokens
/auth/revoke POST Revoke a token

Custom Tokens for End-to-End Tracing

The authorization page lets you enter a custom token instead of using a random one:

  1. MCP client redirects to /authorize?...
  2. Authorization page shows with a pre-filled random token
  3. Replace it with your own memorable token like my-test-abc
  4. Click "Approve"
  5. After code exchange, /token returns { access_token: "my-test-abc" }
  6. All subsequent MCP requests show Authorization: Bearer my-test-abc in logs

This makes it trivial to trace requests across the entire OAuth + MCP flow.

MCP Endpoints

Endpoint Method Description
/ POST MCP JSON-RPC endpoint
/mcp POST Explicit MCP JSON-RPC endpoint
/sse GET Server-Sent Events stream
/* ANY Echo endpoint - returns request details

Testing the OAuth Flow

Manual Testing with curl

# 1. Generate PKCE values
CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=/+' | head -c 43)
CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '/+' '_-' | tr -d '=')

# 2. Open authorization URL in browser
echo "Open: http://localhost:8787/authorize?client_id=test&redirect_uri=http://localhost:9999/callback&response_type=code&code_challenge=$CODE_CHALLENGE&code_challenge_method=S256&state=xyz"

# 3. After approving, extract the 'code' from the redirect URL

# 4. Exchange code for token
curl -X POST http://localhost:8787/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code&code=YOUR_CODE_HERE&redirect_uri=http://localhost:9999/callback&code_verifier=$CODE_VERIFIER"

# 5. Use the token for MCP requests
curl -X POST http://localhost:8787/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'

Quick Testing (Bypass OAuth)

For quick testing without the full OAuth flow:

# Register a token directly
curl -X POST http://localhost:8787/auth/register \
  -H "Content-Type: application/json" \
  -d '{"token": "my-quick-token"}'

# Use it
curl -X POST http://localhost:8787/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer my-quick-token" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Server Output Example

The server logs both requests and responses with color-coded output:

╔════════════════════════════════════════════════════════════╗
║     MCP Echo Server - OAuth 2.0 Authorization Server       ║
╚════════════════════════════════════════════════════════════╝

  Server running at: http://localhost:8787

  🔒 Authentication: REQUIRED
     MCP endpoints require valid Bearer token

  OAuth 2.0 Endpoints:
    GET  /.well-known/oauth-authorization-server
    GET  /authorize      - Authorization page
    POST /token          - Token exchange

  Token Management:
    GET  /auth           - Token management page
    POST /auth/register  - Direct token registration

  MCP Endpoints:
    POST /mcp            - MCP JSON-RPC
    GET  /sse            - Server-Sent Events

════════════════════════════════════════════════════════════════════════════════
[2024-01-15T10:30:45.123Z] REQUEST a1b2c3d4...
════════════════════════════════════════════════════════════════════════════════
POST /token

─── Headers ───
  content-type: application/x-www-form-urlencoded

─── Body ───
  {
    "grant_type": "authorization_code",
    "code": "abc123...",
    "code_verifier": "..."
  }
════════════════════════════════════════════════════════════════════════════════
[a1b2c3d4...] Token request: grant_type=authorization_code
[a1b2c3d4...] PKCE verified successfully
[a1b2c3d4...] Token issued: my-custom-...

─── Response ───
  Status: 200
  Headers:
    Content-Type: application/json
    Cache-Control: no-store
  Body:
  {
    "access_token": "my-custom-token",
    "token_type": "Bearer",
    "expires_in": 86400,
    "scope": "mcp"
  }
────────────────────────────────────────────────────────────────────────────────

Project Structure

mcp-echo-server/
├── src/
│   └── index.js          # Cloudflare Worker code
├── scripts/
│   ├── local-server.js   # Standalone Node.js server (recommended)
│   ├── test-client.js    # Test client for MCP requests
│   └── setup.sh          # Interactive setup script
├── wrangler.toml         # Cloudflare Worker configuration
├── package.json
└── README.md

NPM Scripts

Script Description
npm run local Start local server (auth optional)
npm run local:auth Start local server with auth required
npm run tunnel Local server + cloudflared tunnel
npm run tunnel:auth Local server with auth + tunnel
npm run dev Wrangler local development
npm run deploy Deploy to Cloudflare Workers
npm run tail Stream Worker logs

local vs local:auth - What's the difference?

Command Auth Required? MCP Request Behavior
npm run local No MCP requests work without any token
npm run local:auth Yes MCP requests return 401 unless valid Bearer token provided

Use npm run local when you:

  • Just want to see what MCP clients are sending
  • Don't need to test authentication
  • Want quick debugging without token setup

Use npm run local:auth when you:

  • Testing the full OAuth 2.0 authorization flow
  • Verifying MCP clients handle authentication correctly
  • Want to trace tokens end-to-end through the system

Note: OAuth endpoints (/authorize, /token, etc.) work in both modes. The difference is only whether MCP endpoints (/mcp, /sse) enforce token validation.

Command Line Options

node scripts/local-server.js [options]

Options:
  --require-auth    Require valid Bearer token on MCP endpoints
  --port=XXXX       Custom port (default: 8787)

OAuth 2.0 Metadata

The server exposes standard OAuth 2.0 metadata at /.well-known/oauth-authorization-server:

{
  "issuer": "http://localhost:8787",
  "authorization_endpoint": "http://localhost:8787/authorize",
  "token_endpoint": "http://localhost:8787/token",
  "registration_endpoint": "http://localhost:8787/register",
  "token_endpoint_auth_methods_supported": ["none"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256", "plain"],
  "scopes_supported": ["mcp"]
}

Dynamic Client Registration

MCP clients can automatically register themselves using the /register endpoint (RFC 7591):

curl -X POST http://localhost:8787/register \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": ["http://localhost:9999/callback"],
    "client_name": "My MCP Client"
  }'

Response:

{
  "client_id": "client_abc123...",
  "client_secret": "...",
  "client_id_issued_at": 1234567890,
  "redirect_uris": ["http://localhost:9999/callback"],
  "client_name": "My MCP Client",
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Requirements

License

MIT

About

A debugging MCP server that echoes requests and implements full OAuth 2.0 with PKCE. Useful for testing MCP clients, inspecting protocol traffic, and debugging authentication flows.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages