Skip to content

API Endpoints

Seed exposes multiple HTTP endpoints for MCP protocol handling, OAuth 2.1 authentication, and operational monitoring.

Endpoint Overview

Public Endpoints (No Authentication)

MethodPathPurpose
GET/healthHealth check and version info
GET/.well-known/oauth-protected-resourceOAuth Protected Resource metadata (RFC 9728)
GET/.well-known/oauth-authorization-serverOAuth Authorization Server metadata (RFC 8414)
GET/oauth/authorizeOAuth authorization endpoint (proxy)
POST/oauth/tokenOAuth token exchange endpoint (proxy)
POST/oauth/registerDynamic Client Registration (RFC 7591)
GET/metricsPrometheus metrics (if METRICS_ENABLED=true)
GET/Documentation homepage
GET/assets/**Documentation static assets

Protected Endpoints (JWT Required)

MethodPathPurpose
POST/mcpMCP JSON-RPC endpoint
DELETE/mcpTerminate MCP session

Rate Limited Endpoints

EndpointPer-IP LimitGlobal LimitWindow
POST /mcp100 req10,000 req1 minute
DELETE /mcp100 req10,000 req1 minute
POST /oauth/register10 reg1,000 reg1 hour

Health Check

Health check endpoint for load balancers and monitoring.

Endpoint: GET /health

Authentication: None

Request: No parameters

Response: 200 OK

json
{
  "status": "ok",
  "version": "0.1.3"
}

Response Fields:

  • status (string): Always "ok" if server is running
  • version (string): Server version from package.json

Use Cases:

  • Kubernetes/Docker liveness probes
  • Load balancer health checks
  • Monitoring and alerting

Example:

bash
curl http://localhost:3000/health

MCP JSON-RPC Endpoint

Main endpoint for MCP protocol communication.

Endpoint: POST /mcp

Authentication: Required (JWT Bearer token)

Rate Limiting: 100 requests/minute per IP

Content-Type: application/json

Headers:

Authorization: Bearer <jwt_token>
mcp-session-id: <session_id>  # Optional for existing sessions

Request Body (JSON-RPC 2.0):

json
{
  "jsonrpc": "2.0",
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {
      "name": "example-client",
      "version": "1.0.0"
    }
  },
  "id": 1
}

Initialize Request

Creates a new MCP session and returns session ID.

Method: initialize

Required Parameters:

  • protocolVersion (string): MCP protocol version (e.g., "2024-11-05")
  • capabilities (object): Client capabilities
  • clientInfo (object): Client name and version

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": {},
      "prompts": {},
      "resources": {}
    },
    "serverInfo": {
      "name": "seed",
      "version": "0.1.3"
    }
  },
  "id": 1
}

Response Headers:

mcp-session-id: 550e8400-e29b-41d4-a716-446655440000

Session Behavior:

  • Session ID must be included in all subsequent requests
  • Session expires after 24 hours of inactivity (configurable via MCP_SESSION_TTL_SECONDS)
  • Session TTL refreshes on each request (sliding window)

Tools List Request

List available MCP tools.

Method: tools/list

Headers:

mcp-session-id: 550e8400-e29b-41d4-a716-446655440000

Request:

json
{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "id": 2
}

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "tools": [
      {
        "name": "echo-message",
        "description": "Echo back a message",
        "inputSchema": {
          "type": "object",
          "properties": {
            "message": {
              "type": "string",
              "description": "Message to echo"
            }
          },
          "required": ["message"]
        }
      },
      {
        "name": "random-number",
        "description": "Generate a random number",
        "inputSchema": {
          "type": "object",
          "properties": {
            "min": { "type": "number" },
            "max": { "type": "number" }
          }
        }
      }
    ]
  },
  "id": 2
}

Tool Call Request

Invoke an MCP tool.

Method: tools/call

Request:

json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "random-number",
    "arguments": {
      "min": 1,
      "max": 100
    }
  },
  "id": 3
}

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "42"
      }
    ]
  },
  "id": 3
}

Prompts List Request

List available MCP prompts.

Method: prompts/list

Request:

json
{
  "jsonrpc": "2.0",
  "method": "prompts/list",
  "id": 4
}

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "prompts": [
      {
        "name": "greeting",
        "description": "Generate a friendly greeting message",
        "arguments": [
          {
            "name": "name",
            "description": "Name of the person to greet",
            "required": true
          },
          {
            "name": "style",
            "description": "Greeting style (formal, casual, enthusiastic)",
            "required": false
          }
        ]
      }
    ]
  },
  "id": 4
}

Prompt Get Request

Get a prompt with arguments.

Method: prompts/get

Request:

json
{
  "jsonrpc": "2.0",
  "method": "prompts/get",
  "params": {
    "name": "greeting",
    "arguments": {
      "name": "Alice",
      "style": "enthusiastic"
    }
  },
  "id": 5
}

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "description": "Generate a friendly greeting message",
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "Generate an enthusiastic greeting for Alice"
        }
      }
    ]
  },
  "id": 5
}

Resources List Request

List available MCP resources.

Method: resources/list

Request:

json
{
  "jsonrpc": "2.0",
  "method": "resources/list",
  "id": 6
}

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "resources": [
      {
        "uri": "config://server",
        "name": "Server Configuration",
        "description": "Current server configuration and environment",
        "mimeType": "application/json"
      }
    ]
  },
  "id": 6
}

Resource Read Request

Read an MCP resource.

Method: resources/read

Request:

json
{
  "jsonrpc": "2.0",
  "method": "resources/read",
  "params": {
    "uri": "config://server"
  },
  "id": 7
}

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "contents": [
      {
        "uri": "config://server",
        "mimeType": "application/json",
        "text": "{\"name\":\"seed\",\"version\":\"0.1.3\",\"environment\":\"production\"}"
      }
    ]
  },
  "id": 7
}

Error Responses

Invalid Session (session not found or expired):

http
HTTP/1.1 404 Not Found
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "Invalid or expired session",
    "data": {
      "reason": "session_not_found"
    }
  },
  "id": null
}

Rate Limit Exceeded:

http
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704643200
Retry-After: 45

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32000,
    "message": "Too Many Requests",
    "data": {
      "reason": "rate_limit_exceeded",
      "retryAfter": 45
    }
  },
  "id": null
}

Unauthorized (missing or invalid JWT):

http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Unauthorized",
    "data": {
      "reason": "invalid_token",
      "details": "JWT signature verification failed"
    }
  },
  "id": null
}

MCP Session Termination

Explicitly terminate an MCP session.

Endpoint: DELETE /mcp

Authentication: Required (JWT Bearer token)

Rate Limiting: 100 requests/minute per IP

Headers:

Authorization: Bearer <jwt_token>
mcp-session-id: <session_id>

Request: No body

Response: 200 OK

json
{
  "jsonrpc": "2.0",
  "result": {
    "status": "terminated"
  },
  "id": null
}

Response Behavior:

  • Session removed from in-memory transport registry
  • Session metadata deleted from Redis
  • Subsequent requests with this session ID will return 404

Use Cases:

  • Graceful client shutdown
  • Explicit session cleanup
  • Resource conservation

OAuth Authorization

Initiates OAuth 2.1 authorization flow with PKCE.

Endpoint: GET /oauth/authorize

Authentication: None (public endpoint)

Query Parameters:

client_id=<client_id>
redirect_uri=<redirect_uri>
response_type=code
scope=openid+profile+email
state=<state_value>
code_challenge=<code_challenge>
code_challenge_method=S256

Parameter Details:

  • client_id (required): OAuth client ID (static or registered via DCR)
  • redirect_uri (required): Callback URL (must be registered for DCR clients)
  • response_type (required): Must be "code" (authorization code flow)
  • scope (optional): Requested scopes (e.g., openid profile email)
  • state (required): CSRF protection token
  • code_challenge (required): PKCE code challenge (SHA256 hash of verifier)
  • code_challenge_method (required): Must be "S256"

Behavior:

  1. DCR Client Validation (for client_id starting with seed-):

    • Validates client exists in Redis
    • Validates redirect_uri matches registered URIs
    • Replaces dynamic client_id with static IdP client for upstream
  2. Redirect to IdP:

    • Proxies request to upstream authorization endpoint (OAUTH_AUTHORIZATION_URL)
    • Preserves all query parameters
    • Returns 302 Found redirect

Response: 302 Found

http
Location: https://idp.example.com/authorize?client_id=static-client&redirect_uri=...

Error Responses:

Invalid Client:

http
HTTP/1.1 400 Bad Request

{
  "error": "invalid_client",
  "error_description": "Unknown or expired client_id"
}

Invalid Redirect URI:

http
HTTP/1.1 400 Bad Request

{
  "error": "invalid_redirect_uri",
  "error_description": "redirect_uri does not match any registered URI"
}

Example Authorization URL:

https://seed.example.com/oauth/authorize?
  client_id=seed-abc123xyz&
  redirect_uri=http://localhost:3000/callback&
  response_type=code&
  scope=openid+profile+email&
  state=random-state-value&
  code_challenge=base64url-encoded-sha256-hash&
  code_challenge_method=S256

OAuth Token Exchange

Exchanges authorization code for access token (with PKCE verification).

Endpoint: POST /oauth/token

Authentication: None (public endpoint, PKCE provides security)

Content-Type: application/x-www-form-urlencoded or application/json

Request Body (authorization_code grant):

grant_type=authorization_code
code=<authorization_code>
redirect_uri=<redirect_uri>
client_id=<client_id>
code_verifier=<code_verifier>

Request Body (refresh_token grant):

grant_type=refresh_token
refresh_token=<refresh_token>
client_id=<client_id>

Parameter Details:

authorization_code Grant:

  • grant_type (required): Must be "authorization_code"
  • code (required): Authorization code from /oauth/authorize
  • redirect_uri (required): Same URI used in authorization request
  • client_id (required): OAuth client ID
  • code_verifier (required): PKCE code verifier (plaintext)

refresh_token Grant:

  • grant_type (required): Must be "refresh_token"
  • refresh_token (required): Refresh token from previous token response
  • client_id (required): OAuth client ID

Behavior:

  1. Validate Grant Type: Must be authorization_code or refresh_token
  2. Validate Required Parameters: Check all required fields present
  3. DCR Client Validation (for client_id starting with seed-):
    • Validate client exists in Redis
    • Validate redirect_uri matches registered URIs (for auth code grant)
    • Replace dynamic client_id with static IdP client
  4. Proxy to IdP: Forward request to upstream token endpoint (OAUTH_TOKEN_URL)
  5. Return Response: Proxy status code and body from IdP

Response: 200 OK

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def502003c2...",
  "scope": "openid profile email"
}

Response Fields:

  • access_token (string): JWT access token
  • token_type (string): Always "Bearer"
  • expires_in (number): Token lifetime in seconds
  • refresh_token (string, optional): Refresh token for getting new access tokens
  • scope (string, optional): Granted scopes

Error Responses:

Invalid Request:

http
HTTP/1.1 400 Bad Request

{
  "error": "invalid_request",
  "error_description": "Missing required parameter: code"
}

Invalid Grant:

http
HTTP/1.1 400 Bad Request

{
  "error": "invalid_grant",
  "error_description": "redirect_uri does not match registered URI"
}

Invalid Client:

http
HTTP/1.1 401 Unauthorized

{
  "error": "invalid_client",
  "error_description": "Unknown or expired client_id"
}

Unsupported Grant Type:

http
HTTP/1.1 400 Bad Request

{
  "error": "unsupported_grant_type",
  "error_description": "Unsupported grant type: implicit"
}

Dynamic Client Registration

Register OAuth client dynamically per RFC 7591.

Endpoint: POST /oauth/register

Authentication: None (public endpoint)

Rate Limiting: 10 registrations/hour per IP

Content-Type: application/json

Request Body:

json
{
  "client_name": "My OAuth Client",
  "redirect_uris": [
    "https://app.example.com/callback",
    "http://localhost:3000/callback"
  ],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "software_id": "my-app",
  "software_version": "1.0.0"
}

Required Parameters:

  • redirect_uris (array): At least one redirect URI (max 10)

Optional Parameters:

  • client_name (string): Human-readable client name
  • grant_types (array): Grant types (default: ["authorization_code"])
  • response_types (array): Response types (default: ["code"])
  • token_endpoint_auth_method (string): Auth method (default: "none")
  • software_id (string): Software identifier
  • software_version (string): Software version

Validation Rules:

Redirect URI:

  • Must be valid URL
  • Must use HTTPS (except localhost and 127.0.0.1)
  • Must not contain fragment (#)
  • Maximum 10 URIs per client

Grant Types:

  • Supported: authorization_code, refresh_token

Response Types:

  • Supported: code

Auth Methods:

  • Supported: none, client_secret_post, client_secret_basic

Response: 201 Created

json
{
  "client_id": "seed-abc123xyz789",
  "client_name": "My OAuth Client",
  "redirect_uris": [
    "https://app.example.com/callback",
    "http://localhost:3000/callback"
  ],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "client_id_issued_at": 1704643200,
  "client_secret_expires_at": 0,
  "software_id": "my-app",
  "software_version": "1.0.0"
}

Response Fields:

  • client_id (string): Generated client ID (format: seed-<12chars>)
  • client_id_issued_at (number): Unix timestamp of registration
  • client_secret_expires_at (number): Always 0 (public clients don't expire)
  • All other fields echo the request

Client Storage:

  • Stored in Redis with TTL (default: 30 days, configurable via DCR_CLIENT_TTL)
  • Key format: dcr:client:<client_id>
  • Automatic expiration handled by Redis

Error Responses:

Invalid Redirect URI:

http
HTTP/1.1 400 Bad Request

{
  "error": "invalid_redirect_uri",
  "error_description": "redirect_uri must use https"
}

Invalid Client Metadata:

http
HTTP/1.1 400 Bad Request

{
  "error": "invalid_client_metadata",
  "error_description": "redirect_uris exceeds maximum of 10"
}

OAuth Discovery Endpoints

Protected Resource Metadata

Endpoint: GET /.well-known/oauth-protected-resource

Authentication: None

Response: 200 OK

json
{
  "resource": "https://seed.example.com",
  "authorization_servers": [
    "https://seed.example.com"
  ],
  "scopes_supported": ["openid", "profile", "email"],
  "bearer_methods_supported": ["header"],
  "resource_documentation": "https://gitlab.byterecursion.com/mcp-servers/seed"
}

Fields:

  • resource (string): Protected resource identifier (Seed's base URL)
  • authorization_servers (array): Authorization servers (points to Seed as OAuth proxy)
  • scopes_supported (array): Supported OAuth scopes
  • bearer_methods_supported (array): Token transmission methods
  • resource_documentation (string): Documentation URL

RFC: RFC 9728 - OAuth 2.0 Protected Resource Metadata

Authorization Server Metadata

Endpoint: GET /.well-known/oauth-authorization-server

Authentication: None

Caching: 5-minute in-memory cache

Response: 200 OK

json
{
  "issuer": "https://idp.example.com/application/o/my-app/",
  "authorization_endpoint": "https://seed.example.com/oauth/authorize",
  "token_endpoint": "https://seed.example.com/oauth/token",
  "registration_endpoint": "https://seed.example.com/oauth/register",
  "jwks_uri": "https://idp.example.com/application/o/my-app/jwks/",
  "scopes_supported": ["openid", "profile", "email"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256"]
}

Fields:

  • issuer (string): OAuth issuer (upstream IdP)
  • authorization_endpoint (string): Seed's authorization proxy
  • token_endpoint (string): Seed's token proxy
  • registration_endpoint (string): Seed's DCR endpoint
  • jwks_uri (string): Public key endpoint (upstream IdP)
  • scopes_supported (array): Supported scopes
  • response_types_supported (array): Supported response types
  • grant_types_supported (array): Supported grant types
  • token_endpoint_auth_methods_supported (array): Auth methods
  • code_challenge_methods_supported (array): PKCE methods

Design Choice: Returns Seed's proxy endpoints (not direct IdP endpoints) to maintain control over OAuth flow.

RFC: RFC 8414 - OAuth 2.0 Authorization Server Metadata

Prometheus Metrics

Metrics endpoint for monitoring and alerting.

Endpoint: GET /metrics

Authentication: None (public if enabled)

Configuration: METRICS_ENABLED=true

Content-Type: application/openmetrics-text; version=1.0.0; charset=utf-8

Response: 200 OK

# HELP http_request_duration_seconds Duration of HTTP requests in seconds
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{method="POST",route="/mcp",status_code="200",le="0.01"} 45
http_request_duration_seconds_bucket{method="POST",route="/mcp",status_code="200",le="0.05"} 89
http_request_duration_seconds_sum{method="POST",route="/mcp",status_code="200"} 12.5
http_request_duration_seconds_count{method="POST",route="/mcp",status_code="200"} 100

# HELP mcp_sessions_active Number of active MCP sessions
# TYPE mcp_sessions_active gauge
mcp_sessions_active 42

Security Note: This endpoint is public when enabled. Recommended to use network-level access control (firewall rules, VPC restrictions).

Metrics Categories:

  • HTTP metrics (request duration, total requests)
  • MCP metrics (sessions, tool invocations)
  • Authentication metrics (attempts, token validation)
  • JWKS metrics (cache hits, refreshes)
  • Redis metrics (operations, latency)
  • Rate limiting metrics (violations, usage)

See Also: Observability for complete metrics reference.

Documentation Endpoints

Static documentation served by VitePress.

Endpoints:

  • GET / - Documentation homepage
  • GET /assets/** - Static assets (CSS, JS, images)
  • GET /mcp-server/** - MCP server documentation
  • GET /tools/** - Tools documentation
  • GET /prompts/** - Prompts documentation
  • GET /404.html - VitePress 404 page
  • GET /hashmap.json - VitePress route map

Authentication: None (public)

Content-Type: text/html, text/css, application/javascript, etc.

Implementation: See docs.ts

Common Response Headers

Rate Limiting Headers

Included in all responses to rate-limited endpoints:

http
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1704643200

Fields:

  • X-RateLimit-Limit: Maximum requests allowed in window
  • X-RateLimit-Remaining: Requests remaining in current window
  • X-RateLimit-Reset: Unix timestamp when limit resets

When rate limit is exceeded:

http
Retry-After: 45

CORS Headers

For cross-origin requests:

http
Access-Control-Allow-Origin: https://claude.ai
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, mcp-session-id
Access-Control-Expose-Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
Access-Control-Max-Age: 86400

Security Headers

Applied to all responses (via Helmet):

http
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-DNS-Prefetch-Control: off
Referrer-Policy: strict-origin-when-cross-origin
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: cross-origin

Production-only (HTTPS):

http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Error Response Format

All errors use JSON-RPC 2.0 format for consistency.

Standard Error Structure:

json
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Unauthorized",
    "data": {
      "reason": "invalid_token",
      "details": "JWT signature verification failed"
    }
  },
  "id": null
}

Error Codes:

  • -32001: Authentication error (401)
  • -32000: Server error (429, 403, 404, 500, etc.)

OAuth Error Format:

json
{
  "error": "invalid_client",
  "error_description": "Unknown or expired client_id"
}

See Also: Error Handling (planned) for comprehensive error documentation.

Configuration Reference

Environment Variables

bash
# Server
BASE_URL=https://seed.example.com
PORT=3000

# Authentication
AUTH_REQUIRED=true
OIDC_ISSUER=https://idp.example.com/application/o/my-app/
OIDC_AUDIENCE=my-client-id
OIDC_JWKS_URL=https://idp.example.com/application/o/my-app/jwks/

# OAuth
OAUTH_AUTHORIZATION_URL=https://idp.example.com/application/o/authorize/
OAUTH_TOKEN_URL=https://idp.example.com/application/o/token/

# DCR
DCR_CLIENT_TTL=2592000              # 30 days
DCR_RATE_LIMIT_WINDOW_MS=3600000    # 1 hour
DCR_RATE_LIMIT_MAX=10               # 10 registrations per IP per hour

# MCP
MCP_SESSION_TTL_SECONDS=86400       # 24 hours
MCP_RATE_LIMIT_WINDOW_MS=60000      # 1 minute
MCP_RATE_LIMIT_MAX=100              # 100 requests per IP per minute

# Rate Limiting
RATE_LIMIT_ENABLED=true

# Metrics
METRICS_ENABLED=true

# Redis
REDIS_URL=redis://redis:6379

Implementation Files

  • MCP Routes: src/routes/mcp.ts - MCP JSON-RPC and session management
  • OAuth Routes:
    • src/routes/oauth-authorize.ts - Authorization endpoint
    • src/routes/oauth-token.ts - Token exchange endpoint
    • src/routes/oauth-register.ts - Dynamic client registration
    • src/routes/oauth-protected-resource.ts - Protected resource metadata
    • src/routes/oauth-authorization-server.ts - Authorization server metadata
  • Operational Routes:
    • src/routes/health.ts - Health check
    • src/routes/metrics.ts - Prometheus metrics
    • src/routes/docs.ts - Documentation serving
  • Router: src/routes/index.ts - Route registration

Released under the MIT License.