Skip to content

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.

Notifications You must be signed in to change notification settings

knaperek/mcp-echo-server

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

Watchers

Forks

Releases

No releases published

Packages

No packages published