Latchkey
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Latchkeyshow me active tokens for the recipes service"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-latchkey
Per-person, per-service sign-in for your self-hosted MCP servers, built for Claude.ai and any OIDC provider.
Remote MCP clients like Claude.ai (web, desktop and mobile) need every MCP server to be an OAuth 2.1 authorization server. Most self-hosted MCP servers aren't, and building that into each one means copying the same security-sensitive code again and again. Latchkey does it once, in front of all of them:
claude.ai ──▶ https://mcp.example/<service>/mcp ──▶ latchkey ──┬─ stdio ─▶ npx some-mcp-server
├─ http ──▶ 127.0.0.1:3101 (auth off)
└─ http ──▶ private-host:8000One hostname, one path per service. Add a service with a few lines of config; no new proxy, certificate or OAuth code.
Per-service tokens. Each connector gets a token for exactly one service. A token for
recipesis refused byfinances.Per-person access. People sign in with your OIDC provider (Google preset; Authentik, Pocket-ID, Keycloak, …) and each service lists who may use it. Removing someone takes effect on their next request.
Wraps stdio and HTTP servers. Runs stdio servers itself (one long-lived process each, with only the environment you give it, never latchkey's secrets) or forwards to Streamable HTTP servers. Hide tools that make no sense remotely, keep upstream sessions alive, and get an audit line per tool call.
Locked-down OAuth. Codes only go to allowlisted redirect URIs; client metadata documents are fetched only from allowlisted URLs; tokens are stored hashed in an encrypted file.
No database, no web UI. One YAML file and a small CLI.
Status: early, in daily use by the author with Claude.ai against four upstreams. See docs/design.md for the design and known limits.
Quick start
# latchkey.yaml
issuer: https://mcp.your-tailnet.ts.net
encryption_key: ${LATCHKEY_ENCRYPTION_KEY}
idp:
preset: google
client_id: ${GOOGLE_CLIENT_ID}
client_secret: ${GOOGLE_CLIENT_SECRET}
users:
alex:
email: alex@example.com
services:
whoami:
allow: [alex]
builtin: whoami
recipes:
allow: [alex]
http:
url: http://127.0.0.1:3101/mcpdocker run -d --name latchkey \
-v $PWD/latchkey.yaml:/config/latchkey.yaml:ro -v $PWD/data:/data \
-e LATCHKEY_ENCRYPTION_KEY -e GOOGLE_CLIENT_ID -e GOOGLE_CLIENT_SECRET \
ghcr.io/robot-apocalypse/mcp-latchkey:edgePut it behind HTTPS at issuer (e.g. Tailscale Funnel, see
examples/docker-compose.yml), register <issuer>/idp/callback
with your OIDC provider, then add a Claude.ai custom connector for <issuer>/whoami/mcp.
The full walkthrough is in docs/setup.md.
Related MCP server: mcp-auth-proxy
CLI
In the image the CLI is on the path as latchkey; from a checkout use node dist/cli.js.
latchkey serve run the gateway
latchkey check-config validate config, list services and who may use them
latchkey users users, their services and bound IdP accounts
latchkey users unbind <user> forget a bound account (next sign-in rebinds)
latchkey tokens list [--user U] [--service S] active grants
latchkey tokens revoke --user U | --service S | --id IDHow Claude.ai connects
Verified against claude.ai (docs/spike-results.md): Claude discovers
each service from the WWW-Authenticate header, registers with its client metadata document
(https://claude.ai/oauth/mcp-oauth-client-metadata), and sends resource on both the
authorization and token requests, so several services can share one hostname with separately
scoped tokens.
Development
npm ci
npm test # unit tests + an end-to-end OAuth flow against a mock OIDC provider
npm run buildSecurity
See SECURITY.md for what latchkey does and does not protect, and how to report a vulnerability.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
- StytchOAuthdev.stytch.mcp
The Stytch MCP server is a reference implementation that demonstrates remote MCP server authentication and authorization using Stytch Connected Apps. It provides OAuth 2.1-compliant authorization (including PKCE), Dynamic Client Registration, and validates Stytch-issued access tokens to enable AI agents to securely interact with external services through permissioned access, supporting scopes like openid, email, profile, and manage:project_data.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Zero-setup MCP gateway securely connecting AI to your tools with authentication and workflows
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables Claude.ai to connect to a Hermes MCP server via OAuth 2.1 authorization code flow with PKCE, acting as a reverse proxy and single-user authorization gateway.-
- FlicenseNot gradedqualityDmaintenanceBridges stdio-based LLM harnesses to OAuth-protected remote MCP servers via Streamable HTTP, handling PKCE browser login and token refresh automatically.9 npm-
- AlicenseNot gradedqualityDmaintenanceActs as a secure OAuth 2.0/2.1 proxy gateway for MCP servers, enabling integration with Claude and ChatGPT platforms.32 npmMIT
- AlicenseNot gradedqualityCmaintenanceA self-hosted bridge that exposes local stdio MCP servers to ChatGPT and Claude over HTTPS with OAuth, providing a secure passthrough for your own tools.1MIT