diff --git a/README.md b/README.md index 1b8335a2d..5b0b905cd 100644 --- a/README.md +++ b/README.md @@ -68,18 +68,24 @@ ContextForge MCP Gateway is a feature-rich gateway, proxy and MCP Registry that * 10. [Configuration (`.env` or env vars)](#configuration-env-or-env-vars) * 10.1. [Basic](#basic) * 10.2. [Authentication](#authentication) - * 10.3. [UI Features](#ui-features) - * 10.4. [Security](#security) - * 10.5. [Logging](#logging) - * 10.6. [Transport](#transport) - * 10.7. [Federation](#federation) - * 10.8. [Resources](#resources) - * 10.9. [Tools](#tools) - * 10.10. [Prompts](#prompts) - * 10.11. [Health Checks](#health-checks) - * 10.12. [Database](#database) - * 10.13. [Cache Backend](#cache-backend) - * 10.14. [Development](#development) + * 10.3. [A2A (Agent-to-Agent) Features](#a2a-agent-to-agent-features) + * 10.4. [Email-Based Authentication & User Management](#email-based-authentication--user-management) + * 10.5. [MCP Client Authentication](#mcp-client-authentication) + * 10.6. [SSO (Single Sign-On) Configuration](#sso-single-sign-on-configuration) + * 10.7. [Dynamic Client Registration & Virtual MCP Server Authentication](#dynamic-client-registration--virtual-mcp-server-authentication) + * 10.8. [UI Features](#ui-features) + * 10.9. [Security](#security) + * 10.10. [Logging](#logging) + * 10.11. [Transport](#transport) + * 10.12. [Federation](#federation) + * 10.13. [Resources](#resources) + * 10.14. [Tools](#tools) + * 10.15. [Prompts](#prompts) + * 10.16. [Health Checks](#health-checks) + * 10.17. [Database](#database) + * 10.18. [Cache Backend](#cache-backend) + * 10.19. [Plugin Configuration](#plugin-configuration) + * 10.20. [Development](#development) * 11. [Running](#running) * 11.1. [Makefile](#makefile) * 11.2. [Script helper](#script-helper) @@ -1209,11 +1215,26 @@ You can get started by copying the provided [.env.example](https://github.com/IB | ------------------------------ | ------------------------------------------------ | --------------------- | ------- | | `SSO_AUTO_ADMIN_DOMAINS` | Email domains that automatically get admin privileges | `[]` | JSON array | +### Dynamic Client Registration & Virtual MCP Server Authentication + +ContextForge supports OAuth2 with Dynamic Client Registration (DCR) +for streamable HTTP servers through integration with an upstream API gateway, +such as HyperMCP gateway, enabling automatic OAuth2 client provisioning for MCP servers +without manual configuration. + +| Setting | Description | Default | Options | +|-----------------------------|--------------------------------------------------------|---------|---------| +| `JWT_AUDIENCE_VERIFICATION` | JWT audience verification needs to be disabled for DCR | `true` | bool | + +You can find an example for using dynamic client registration (DCR) with [HyprMCP Gateway (`hyprmcp/mcp-gateway`)](https://github.com/hyprmcp/mcp-gateway). + +Follow the tutorial at https://ibm.github.io/mcp-context-forge/tutorials/dcr-hyprmcp/ to get started. + ### Personal Teams Configuration | Setting | Description | Default | Options | | ---------------------------------------- | ------------------------------------------------ | ---------- | ------- | -| `AUTO_CREATE_PERSONAL_TEAMS` | Enable automatic personal team creation for new users | `true` | bool | +| `AUTO_CREATE_PERSONAL_TEAMS` | Enable automatic personal team creation for new users | `true` | bool | | `PERSONAL_TEAM_PREFIX` | Personal team naming prefix | `personal` | string | | `MAX_TEAMS_PER_USER` | Maximum number of teams a user can belong to | `50` | int > 0 | | `MAX_MEMBERS_PER_TEAM` | Maximum number of members per team | `100` | int > 0 | diff --git a/docs/docs/deployment/proxy-auth.md b/docs/docs/deployment/proxy-auth.md index 1ec00c521..31d6a4770 100644 --- a/docs/docs/deployment/proxy-auth.md +++ b/docs/docs/deployment/proxy-auth.md @@ -43,6 +43,83 @@ AUTH_REQUIRED=true ## Common Proxy Configurations +### HyprMCP Gateway + +Find the completed guide on how to use the [HyprMCP Gateway](https://github.com/hyprmcp/mcp-gateway) to support DCR and OAuth2 here: +[Tutorial: Dynamic Client Registration with HyprMCP](/ibm/mcp-context-forge/tutorials/dcr-hyprmcp/) + + +```yaml +# docker-compose.yaml +services: + hyprmcp-dex: + image: ghcr.io/dexidp/dex:v2.43.1-alpine + command: ["dex", "serve", "/config.yaml"] + ports: + - 5556:5556 + - 5557:5557 + healthcheck: + test: wget http://localhost:5556/.well-known/openid-configuration -O - + interval: 5s + start_period: 10s + start_interval: 1s + volumes: + - type: bind + source: config/hyprmcp-dex.yaml + target: /config.yaml + read_only: true + - type: bind + source: ./data + target: /data + #env_file: + # - config/.dex.secret.env + + hyprmcp-gateway: + image: ghcr.io/hyprmcp/mcp-gateway:0.2.6 + command: ["serve", "--config", "/opt/config.yaml"] + ports: + - 9000:9000 + volumes: + - type: bind + source: config/hyprmcp-gateway.yaml + target: /opt/config.yaml + read_only: true + depends_on: + hyprmcp-dex: + condition: service_healthy + required: true + network_mode: host + + context-forge: + image: ghcr.io/ibm/mcp-context-forge:0.7.0 + ports: + - 4444:4444 + volumes: + - type: bind + source: ./data + target: /data + - ./config/public.pem:/opt/public.pem:ro + - ./config/private.pem:/opt/private.pem:ro + env_file: + - config/context-forge.env + environment: + JWT_ALGORITHM: RS256 + JWT_PUBLIC_KEY_PATH: /opt/public.pem + JWT_PRIVATE_KEY_PATH: /opt/private.pem + JWT_AUDIENCE_VERIFICATION: false + JWT_ISSUER: http://localhost:5556 + DATABASE_URL: sqlite:////data/context-forge.db + HOST: 0.0.0.0 + PORT: "4444" + MCPGATEWAY_UI_ENABLED: true + MCPGATEWAY_ADMIN_API_ENABLED: true + BASIC_AUTH_USER: admin + BASIC_AUTH_PASSWORD: changeme + AUTH_REQUIRED: false + MCP_CLIENT_AUTH_ENABLED: false + TRUST_PROXY_AUTH: true +``` + ### OAuth2 Proxy OAuth2 Proxy is a popular reverse proxy that provides authentication using OAuth2 providers. diff --git a/docs/docs/manage/.pages b/docs/docs/manage/.pages index 45683c6b1..f9bf97450 100644 --- a/docs/docs/manage/.pages +++ b/docs/docs/manage/.pages @@ -12,6 +12,7 @@ nav: - observability.md - observability - proxy.md + - dcr.md - oauth.md - securing.md - sso.md diff --git a/docs/docs/manage/dcr.md b/docs/docs/manage/dcr.md new file mode 100644 index 000000000..4a2d5ff71 --- /dev/null +++ b/docs/docs/manage/dcr.md @@ -0,0 +1,40 @@ +# Dynamic Client Registration (DCR) + +Dynamic Client Registration (DCR) is an OAuth 2.0 extension that enables automatic client registration without manual configuration. This guide explains how to configure and use DCR with MCP Gateway for streamable HTTP servers. + +## Overview + +DCR solves a common authentication challenge in distributed MCP deployments: +- **Traditional OAuth2** requires pre-configured client credentials for each MCP server +- **With DCR**, MCP clients can automatically register themselves as OAuth2 clients + +## What is Dynamic Client Registration? + +Dynamic Client Registration is defined in [RFC 7591](https://tools.ietf.org/html/rfc7591) as a protocol that allows OAuth 2.0 clients to register with authorization servers dynamically. In the context of MCP Gateway: + +## Architecture + +A simplified architecture. Please view the following guide for an in depth swimlane chart: + +📖 **[Dynamic Client Registration with HyperMCP Tutorial](../tutorials/dcr-hyprmcp.md)** + +```mermaid +graph LR + Client[MCP Client] + HyprMCP[HyprMCP Gateway] + Dex[Dex] + IdP[Federated IdP
GitHub/Google/LDAP] + ContextForge[ContextForge] + + Client --> HyprMCP + HyprMCP --> Dex + Dex -.-> IdP + HyprMCP --> ContextForge +``` + +## Related Documentation + +- [OAuth 2.0 Integration](oauth.md) - General OAuth2 configuration +- [Proxy Authentication](../deployment/proxy-auth.md) - Using authentication proxies +- [SSO Configuration](sso.md) - Single Sign-On setup +- [Security Best Practices](securing.md) - Security guidelines diff --git a/docs/docs/manage/index.md b/docs/docs/manage/index.md index e21862e6c..3fc890cae 100644 --- a/docs/docs/manage/index.md +++ b/docs/docs/manage/index.md @@ -20,6 +20,7 @@ Whether you're self-hosting, running in the cloud, or deploying to Kubernetes, t | Page | Description | |------|-------------| | [Configuration](configuration.md) | **Complete configuration reference** - databases, environment variables, and deployment settings | +| [Dynamic Client Registration](dcr.md) | 🔐 **OAuth2 DCR** - Automatic client provisioning for streamable HTTP servers | | [Backups](backup.md) | How to persist and restore your database, configs, and resource state | | [Export & Import](export-import.md) | Complete configuration management with CLI, API, and Admin UI | | [Export/Import Tutorial](export-import-tutorial.md) | Step-by-step tutorial for getting started with export/import | diff --git a/docs/docs/tutorials/.pages b/docs/docs/tutorials/.pages index b12a35936..70263489d 100644 --- a/docs/docs/tutorials/.pages +++ b/docs/docs/tutorials/.pages @@ -2,3 +2,4 @@ nav: - index.md - argocd-helm-deployment-ibm-cloud-iks.md - openwebui-tutorial.md + - dcr-hyprmcp.md diff --git a/docs/docs/tutorials/dcr-hyprmcp.md b/docs/docs/tutorials/dcr-hyprmcp.md new file mode 100644 index 000000000..5b64c1b6e --- /dev/null +++ b/docs/docs/tutorials/dcr-hyprmcp.md @@ -0,0 +1,374 @@ +# Dynamic Client Registration with HyprMCP + +To make it easier for users to install an MCP server, you can set up OAuth2 authentication to avoid manual API token configuration. +However, since the MCP specification requires OAuth2 dynamic client registration, which most SSO providers do not support, some additional setup is required. +The [HyprMCP Gateway](https://github.com/hyprmcp/mcp-gateway) is an open-source reverse proxy for MCP servers that allows you to add this capability. + +This tutorial will guide you through the process of setting up ContextForge with HyprMCP to enable OAuth2 support for your virtual MCP servers. + +## Architecture Overview + +### Components + +| Component | Description | +| --------------- | ------------------------------------------------------ | +| HyprMCP Gateway | Reverse proxy for MCP servers that adds OAuth2 support | +| Dex | OpenID Connect server provider | +| IdP | (optional) Upstream identity provider | +| ContextForge | MCP gateway | +| MCP | Upstream MCP server | + +### Flow + +```mermaid +sequenceDiagram + actor User + participant HyprMCP + participant Dex + participant IdP + participant ContextForge + participant MCP + + User->>+HyprMCP: Init MCP connection + HyprMCP-->>-User: 401 Unauthorized + User->>+HyprMCP: GET Protected Resource Metadata + HyprMCP-->>-User: Protected Resource Metadata + + User->>+HyprMCP: GET OAuth2 Server Metadata + HyprMCP->>+Dex: GET OIDC configuration + Dex-->>-HyprMCP: OIDC configuration + HyprMCP-->>-User: OAuth2 Server Metadata + + User->>+HyprMCP: DCR request + HyprMCP->>+Dex: Create OIDC Client + Dex-->>-HyprMCP: OIDC Client + HyprMCP-->>-User: DCR response + + User->>+Dex: Authorization Request + Dex-->>User: Redirect to Upstream IdP + User->>+IdP: Authorization Request + Note over User,IdP: Authorizaton Flow + IdP-->>-User: Redirect to Dex + User->>Dex: Authorization Callback + Dex-->>-User: Redirect to Client + + User->>+Dex: Get Access Token + Dex-->>-User: Access Token + + User->>+HyprMCP: Init MCP connection
(With Access Token in Authorization Header) + HyprMCP->>+ContextForge: Init MCP connection + note right of ContextForge: ContextForge verifies
Access Token + ContextForge-->>-HyprMCP: Init Response + HyprMCP-->>-User: Init Response + Note right of User: MCP connection initialized + + loop + User->>+HyprMCP: tools/call request + HyprMCP->>+ContextForge: tools/call request + ContextForge->>+MCP: tools/call request + MCP-->>-ContextForge: tools/call response + ContextForge-->>-HyprMCP: tools/call response + HyprMCP-->>-User: tools/call response + end +``` + +## Prerequisites + +- **Docker** +- **Docker Compose** +- **SQLite3** + +## Deployment Steps + +This section guides you through the deployment process of all the required services. +The final compose file will be at the end. + +### Step 1: Dex IdP + +Create a `docker-compose.yaml` file with the following content: + +```yaml +services: + hyprmcp-dex: + image: ghcr.io/dexidp/dex:v2.43.1-alpine + command: ["dex", "serve", "/config.yaml"] + ports: + - 5556:5556 + - 5557:5557 + healthcheck: + test: wget http://localhost:5556/.well-known/openid-configuration -O - + interval: 5s + start_period: 10s + start_interval: 1s + volumes: + - type: bind + source: config/hyprmcp-dex.yaml + target: /config.yaml + read_only: true + - type: bind + source: ./data + target: /data + #env_file: + # - config/.dex.secret.env +``` + +Then, create `config/hyprmcp-dex.yaml` with the following content: + +```yaml +issuer: http://localhost:5556 +web: + http: 0.0.0.0:5556 + allowedOrigins: ["*"] +grpc: + addr: 0.0.0.0:5557 +storage: + type: sqlite3 + config: + file: /data/dex.db +oauth2: + skipApprovalScreen: true +enablePasswordDB: true +expiry: + signingKeys: 8760h # 1 year +staticPasswords: + - email: "admin@example.com" + # bcrypt hash of the string "password" for user admin: $(echo password | htpasswd -BinC 10 admin | cut -d: -f2) + hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" + username: "admin" + userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" +#connectors: +# - type: github +# id: github +# name: GitHub +# config: +# clientID: {{ .Env.GITHUB_CLIENT_ID }} +# clientSecret: {{ .Env.GITHUB_CLIENT_SECRET }} +# redirectURI: http://localhost:5556/callback +``` + +Optionally, you can enable the GitHub upstream IdP by uncommenting the relevant sections and creating `config/.dex.github.env` with the following content: + +```env +GITHUB_CLIENT_ID=your_client_id +GITHUB_CLIENT_SECRET=your_client_secret +``` + +Refer to the GitHub documentation for more information on how to obtain the client ID and secret. + +Once you're done, you can start the Dex server: + +```shell +docker compose up -d +``` + +### Step 2: Extract signing keys + +To get the signing keys from the Dex database, you can use the following command: + +```shell +sqlite3 -readonly data/dex.db "select signing_key from keys" +``` + +This returns the signing key used by Dex in the JWK format. +You can convert it to PEM format using an [online tool](https://8gwifi.org/jwkconvertfunctions.jsp). +Write the public and private keys to files `config/public.pem` and `config/private.pem` respectively. + +This step is necessary to ensure that ContextForge can verify the JWTs issued by Dex. + +### Step 3: ContextForge + +Add the ContextForge service to your compose file (put it in the `services` section): + +```yaml +context-forge: + image: ghcr.io/ibm/mcp-context-forge:0.7.0 + ports: + - 4444:4444 + volumes: + - type: bind + source: ./data + target: /data + - ./config/public.pem:/opt/public.pem:ro + - ./config/private.pem:/opt/private.pem:ro + env_file: + - config/context-forge.env + environment: + JWT_ALGORITHM: RS256 + JWT_PUBLIC_KEY_PATH: /opt/public.pem + JWT_PRIVATE_KEY_PATH: /opt/private.pem + JWT_AUDIENCE_VERIFICATION: false + JWT_ISSUER: http://localhost:5556 + DATABASE_URL: sqlite:////data/context-forge.db + HOST: 0.0.0.0 + PORT: "4444" + MCPGATEWAY_UI_ENABLED: true + MCPGATEWAY_ADMIN_API_ENABLED: true + BASIC_AUTH_USER: admin + BASIC_AUTH_PASSWORD: changeme + AUTH_REQUIRED: false + MCP_CLIENT_AUTH_ENABLED: false + TRUST_PROXY_AUTH: true +``` + +Start the new services, again, using `docker compose up -d`. + +### Step 5: MCP Server Setup + +For this tutorial we will use the Context7 MCP server as an example, but you can use any MCP server you want. +To get started, go to https://context7.com/ and create an account. Note down your API key, as you will need it shortly. + +Next, open ContextForge by navigating to http://localhost:4444. +Log in with the credentials provided in the compose file and configure the Context7 MCP server. +To do so, go to "MCP Servers" and at the bottom of the page: + +- Enter `https://mcp.context7.com/mcp` as the MCP Server URL +- Select Transport Type "Streamable HTTP" +- Select Authentication Type "Bearer Token" +- Enter your Context7 API key as the Token + Then, go to "Virtual Servers" and add a new server with the Context7 tools associated. + Copy the UUID of this virtual MCP server and save it for the next step. + +### Step 6: HyprMCP Gateway + +Add the [HyprMCP Gateway](https://github.com/hyprmcp/mcp-gateway) service to your compose file: + +```yaml +hyprmcp-gateway: + image: ghcr.io/hyprmcp/mcp-gateway:0.2.6 + command: ["serve", "--config", "/opt/config.yaml"] + ports: + - 9000:9000 + volumes: + - type: bind + source: config/hyprmcp-gateway.yaml + target: /opt/config.yaml + read_only: true + depends_on: + hyprmcp-dex: + condition: service_healthy + required: true + network_mode: host +``` + +Also, create the config file `config/hyprmcp-gateway.yaml` with the following content: + +```yaml +host: http://localhost:9000/ +authorization: + server: http://localhost:5556/ + authorizationProxyEnabled: false + serverMetadataProxyEnabled: true + dynamicClientRegistration: + enabled: true + publicClient: true +dexGRPCClient: + addr: localhost:5557 +proxy: + - path: /context-forge/mcp + http: + # replace "SERVER_UUID" with your virtual MCP server UUID from the previous step + url: http://localhost:4444/servers/SERVER_UUID/mcp + authentication: + enabled: true +``` + +Start the service with `docker compose up -d`. + +## Testing + +To test the virtual MCP server with OAuth2, you can use the MCP inspector tool: + +``` +npx @modelcontextprotocol/inspector@latest +``` + +It should open a browser window with the MCP inspector tool. On the left side, enter the URL of the HyprMCP Gateway `http://localhost:9000/context-forge/mcp` and press "Connect". +You should get redirected to the login page of the Dex IdP service for authentication. +When authentication is successful, you should be redirected back to the MCP inspector tool where it should now display "connected". +In the top navigation bar, click on "Tools", then click "List Tools". +Choose a tool from the list and run it via the right side panel. + +## Complete Compose File + +```yaml +services: + hyprmcp-dex: + image: ghcr.io/dexidp/dex:v2.43.1-alpine + command: ["dex", "serve", "/config.yaml"] + ports: + - 5556:5556 + - 5557:5557 + healthcheck: + test: wget http://localhost:5556/.well-known/openid-configuration -O - + interval: 5s + start_period: 10s + start_interval: 1s + volumes: + - type: bind + source: config/hyprmcp-dex.yaml + target: /config.yaml + read_only: true + - type: bind + source: ./data + target: /data + #env_file: + # - config/.dex.secret.env + + hyprmcp-gateway: + image: ghcr.io/hyprmcp/mcp-gateway:0.2.6 + command: ["serve", "--config", "/opt/config.yaml"] + ports: + - 9000:9000 + volumes: + - type: bind + source: config/hyprmcp-gateway.yaml + target: /opt/config.yaml + read_only: true + depends_on: + hyprmcp-dex: + condition: service_healthy + required: true + network_mode: host + + context-forge: + image: ghcr.io/ibm/mcp-context-forge:0.7.0 + ports: + - 4444:4444 + volumes: + - type: bind + source: ./data + target: /data + - ./config/public.pem:/opt/public.pem:ro + - ./config/private.pem:/opt/private.pem:ro + env_file: + - config/context-forge.env + environment: + JWT_ALGORITHM: RS256 + JWT_PUBLIC_KEY_PATH: /opt/public.pem + JWT_PRIVATE_KEY_PATH: /opt/private.pem + JWT_AUDIENCE_VERIFICATION: false + JWT_ISSUER: http://localhost:5556 + DATABASE_URL: sqlite:////data/context-forge.db + HOST: 0.0.0.0 + PORT: "4444" + MCPGATEWAY_UI_ENABLED: true + MCPGATEWAY_ADMIN_API_ENABLED: true + BASIC_AUTH_USER: admin + BASIC_AUTH_PASSWORD: changeme + AUTH_REQUIRED: false + MCP_CLIENT_AUTH_ENABLED: false + TRUST_PROXY_AUTH: true +``` + +## Conclusion + +This tutorial demonstrates how to set up ContextForge together with [HyprMCP](https://hyprmcp.com/) to enable secure, efficient and easy-to-use OAuth2 authentication for virtual MCP servers. + +### Future Work + +Interoperability could be further improved by ContextForge adding support for SSO using generic OIDC providers, as well as the HyprMCP gateway adding other DCR compatibility modes on top of the existing one based on Dex IdP. + +### Further Links + +- Explore the [HyprMCP](https://hyprmcp.com/) documentation for more advanced features and integrations. +- Read our in-depth guide on [Building Supabase-like OAuth Authentication For MCP Servers](https://hyprmcp.com/blog/mcp-server-authentication/)