arcade-mcp/examples/mcp_servers/authorization/docker/README.md
Eric Gustin 98fd13c4ed
Front-Door Auth (#696)
# Valuable references for the reviewer:
- Docs PR: https://github.com/ArcadeAI/docs/pull/583
- Implements Phase 1 of the following planning doc:
https://linear.app/arcadedev/project/arcade-mcp-supports-mcp-auth-front-door-auth-7cbaa20cb054/overview


https://github.com/user-attachments/assets/79ad43fd-f5e8-4793-a1dd-18b35acefdc3

# PR Description
Adds OAuth 2.1 Resource Server authentication to arcade-mcp-server,
enabling HTTP MCP servers to validate Bearer tokens on every request.
This unlocks tool-level authorization and secrets support for HTTP
servers.

- Multiple authorization server support
- Granular token validation options (verify_exp, verify_iat, verify_iss)
- Environment variable configuration
- OAuth discovery metadata endpoint
(/.well-known/oauth-protected-resource)
- Extracts sub claim from token as context.user_id
- Lifts transport restrictions for tools requiring auth/secrets on HTTP
when protected

```python
from arcade_mcp_server import MCPApp
from arcade_mcp_server.resource_server import ResourceServerAuth, AuthorizationServerEntry

resource_server_auth = ResourceServerAuth(
    canonical_url="http://127.0.0.1:8000/mcp",
    authorization_servers=[
        AuthorizationServerEntry(
            authorization_server_url="https://auth.example.com",
            issuer="https://auth.example.com",
            jwks_uri="https://auth.example.com/jwks",
        )
    ],
)

app = MCPApp(name="my_server", version="1.0.0", auth=resource_server_auth)
```

# Testing
Beyond the comprehensive unit tests, I also manually tested end-to-end
with WorkOS Authkit (DCR) and KeyCloak (non-DCR).

# Future Work
- CIMD support
- An `ArcadeResourceServer` to make adding front-door auth super easy
when using Arcade's Auth Server



<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Adds OAuth 2.1 front-door auth (JWKS validation + OAuth discovery) and
propagates user identity to tools, enabling auth/secret-requiring tools
over HTTP.
> 
> - **Authentication (Front-Door OAuth 2.1)**
> - New `resource_server` module with `ResourceServerAuth`
(multi-authorization-server, metadata) and `JWKSTokenValidator`
(JWKS-based JWT validation) plus granular validation options.
> - ASGI `ResourceServerMiddleware` validates Bearer tokens on every
HTTP request and injects `resource_owner`.
> - OAuth discovery endpoint via FastAPI router at
`/.well-known/oauth-protected-resource[/<path>]`.
> - **Integration**
> - `MCPApp`/`worker` accept `auth`/`resource_server_validator`, mount
middleware, expose discovery; logs accepted auth servers.
> - HTTP transport (`http_streamable`) carries `SessionMessage` with
`resource_owner` from request → session.
> - `Context`/`Session`/`Server` plumb `resource_owner`; `Server`
selects `user_id` preferring token `sub`.
> - **Behavior Changes**
> - HTTP transport restriction lifted for tools requiring
`authorization`/`secrets` when request is authenticated; otherwise
blocked with actionable error.
> - **Configuration**
> - Env-var based auth config via `MCP_RESOURCE_SERVER_*` in
`MCPSettings.ResourceServerSettings`; `.env` auto-load.
> - **Telemetry**
>   - Usage tracking records `resource_server_type` on server start.
> - **Examples**
> - New `examples/mcp_servers/authorization` sample server (HTTP auth,
secrets, Reddit tool) with Docker setup.
> - **Tests**
> - Extensive unit tests for validators, middleware, env config,
multi-AS, transport rules, and app integration.
> - **Version**
> - Bump `arcade-mcp-server` to `1.12.0`; minor docstring tweak in
`__init__.py`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
d1116cdcafb0c7cb8f91e66682eb1fbae380da31. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->





Resolves TOO-152
2025-12-11 12:51:20 -08:00

2.8 KiB

Docker Setup for MCP Servers

This directory contains a generalized Docker configuration template that can be used with any MCP server in this repository.

Quick Start

  1. Copy the Docker files to your MCP server directory:

    cp -r examples/docker-template/docker your-mcp-server/
    cp examples/docker-template/.dockerignore your-mcp-server/
    
  2. Build and run:

    cd your-mcp-server
    docker-compose -f docker/docker-compose.yml up --build
    

Configuration

Package Detection

The Dockerfile uses the package name from pyproject.toml by reading the [project] name field. It expects your server file at src/<package_name>/server.py (where <package_name> is from pyproject.toml).

If the server file is not found at this location, then the build will fail with an error message showing the detected package name and available directories in src/.

Environment Variables

  • ARCADE_SERVER_TRANSPORT: The transport protocol to use
    • Default: http
    • Options: http, stdio
  • ARCADE_SERVER_PORT: The port to run the server on (internal)
    • Default: 8001
  • ARCADE_SERVER_HOST: The host to bind to
    • Default: 0.0.0.0

Example: Simple MCP Server

# From examples/mcp_servers/simple/
docker-compose -f docker/docker-compose.yml up --build

The server will run internally on port 8001 but be accessible externally on port 8080 (http://localhost:8080). This demonstrates front-door auth working when the canonical URL differs from the internal bind address.

You can customize the ports by editing docker/docker-compose.yml and changing:

  • The port mapping (e.g., "8080:8001")
  • The ARCADE_SERVER_PORT environment variable (internal port)
  • The MCP_RESOURCE_SERVER_CANONICAL_URL (external URL)

Building the Image

docker build \
  -f docker/Dockerfile \
  -t your-mcp-server \
  .

Running with Docker

docker run -p 8080:8001 \
  -e ARCADE_SERVER_TRANSPORT=http \
  -e ARCADE_SERVER_HOST=0.0.0.0 \
  -e ARCADE_SERVER_PORT=8001 \
  your-mcp-server

Features

  • Automatic package detection: Reads package name from pyproject.toml
  • Standard server location: Expects server file at src/<package>/server.py
  • Secure by default: Runs as non-root user
  • Arcade environment variable support: Uses ARCADE_SERVER_* environment variables
  • Environment-based config: Easy customization via environment variables
  • uv integration: Uses uv for fast dependency management
  • Lightweight: Based on Python 3.11 Bookworm slim image with uv

Connecting from Cursor

Add to your ~/.cursor/mcp.json:

"your-server-name": {
  "name": "your-server-name",
  "type": "stream",
  "url": "http://localhost:8080/mcp"
}

Then restart Cursor to connect to the server.