• MCP 2026-07-28
  • FAPI 2.0 · DPoP
  • Tested with Keycloak 26.8.0
  • MIT

Connect AI clients to protected MCP servers, without bearer tokens to steal.

mcp-airlock is a local stdio bridge to remote, OAuth-protected Model Context Protocol servers. It discovers the authorization server, logs you in through your browser, binds every token to a key that never leaves your machine, and keeps you logged in, all invisible to your AI client.

Why mcp-airlock

Remote MCP servers protect their tools with OAuth, but many clients only speak stdio, and few OAuth clients use sender-constrained tokens. mcp-airlock closes that gap.

No bearer tokens to steal

Access tokens are bound to a P-256 key generated for each login (DPoP). A leaked token is useless without the key.

Just the server URL

The authorization server is discovered from the MCP server's protected resource metadata, as the MCP specification describes.

Invisible re-authentication

Tokens are refreshed silently, even before they expire. When a login is needed, requests wait and then resume.

Features

Built against the specifications, and tested against a strict MCP 2026-07-28 server and a real Keycloak.

Dual-era MCP

Forwards MCP 2026-07-28 clients (per-request _meta) and legacy initialize clients, each with the HTTP rules of its revision.

Streamable HTTP, done right

JSON and SSE responses, Mcp-Method / Mcp-Name headers, x-mcp-header mirroring, subscriptions/listen and cancellation by closing the stream.

FAPI 2.0 login

Pushed Authorization Requests, PKCE S256, resource indicators, dpop_jkt, and RFC 9207 iss checks on the callback.

Refresh and step-up

Silent and proactive refresh with DPoP-bound refresh tokens. A 403 insufficient_scope triggers a step-up login that keeps earlier scopes.

OS keychain

macOS Keychain, Windows Credential Manager, or the Linux Secret Service. Credentials are scoped per MCP server and bound to their issuer.

Never leaves the client hanging

Network errors, HTTP errors and failed logins come back as JSON-RPC errors for the request. Authentication loops are detected, not repeated.

How it works

Your AI client starts mcp-airlock like any local MCP server. Everything else happens behind the stdio pipe.

mcp-airlock architecture The AI client talks JSON-RPC over stdio to mcp-airlock. mcp-airlock talks HTTPS with DPoP proofs to the MCP server, keeps credentials in the OS keychain, and uses the authorization server for discovery, login and refresh. The MCP server points to its authorization server through RFC 9728 metadata. stdio · JSON-RPC HTTPS + DPoP credentials discovery · login · refresh RFC 9728 AI client Claude, Gemini CLI, … mcp-airlock local bridge · airlock MCP server Streamable HTTP OS keychain token · DPoP key · refresh Authorization server PAR · PKCE · DPoP

Scroll the diagram sideways to see all of it.

  1. Your client sends a JSON-RPC request on stdio; mcp-airlock forwards it with a DPoP proof and the headers of its MCP revision.
  2. The server answers 401: requests pause in the airlock while mcp-airlock finds the authorization server through RFC 9728 and RFC 8414 metadata.
  3. If a refresh token exists, it is used silently. Otherwise your browser opens a PAR-based login with PKCE, and the callback is checked (state, iss).
  4. The code is exchanged for a DPoP-bound token, which is stored in your keychain for this server and this issuer only.
  5. The paused requests resume; responses, including streamed ones, flow back to your client.

Quick start

Three steps, assuming an MCP server protected by an OAuth provider such as Keycloak.

1Install

Download a binary from the releases page, or build it with Rust 1.88+:

cargo install --git https://github.com/ffalcinelli/mcp-airlock

2Register a client

Create a public client (e.g. mcp-airlock) with PAR, PKCE S256 and DPoP, and the redirect URI http://127.0.0.1:8082/callback. Client ID Metadata Documents work too.

3Add it to your AI client

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "my-secure-server": {
      "command": "/path/to/mcp-airlock",
      "args": ["--remote-mcp-url", "https://mcp.example.com/mcp",
               "--oidc-client-id", "mcp-airlock"]
    }
  }
}

Claude Code:

claude mcp add my-secure-server -- /path/to/mcp-airlock \
  --remote-mcp-url https://mcp.example.com/mcp --oidc-client-id mcp-airlock

On the first request your browser opens the provider's login page. More clients, every option, and provider setup: setup guide.

Compatibility

MCP

  • 2026-07-28 (stateless, per-request _meta)
  • 2025-11-25 and earlier (initialize sessions)
  • Streamable HTTP transport

OAuth and OIDC

  • RFC 9728, RFC 8414, OIDC Discovery
  • RFC 9126 (PAR), RFC 7636 (PKCE)
  • RFC 9449 (DPoP), RFC 9207 (iss)
  • RFC 8707, Client ID Metadata Documents

Providers

  • Keycloak 26.8.0 (tested end to end)
  • Any provider with PAR, PKCE S256 and DPoP

Platforms

  • macOS (Keychain)
  • Windows (Credential Manager)
  • Linux (Secret Service)

Security model

mcp-airlock protects your credentials in transit and at rest, and only sends them where they belong.

  • Tokens are bound to a per-login key (DPoP); proofs carry the method, URL, token hash and server nonces.
  • Authorization servers without PKCE S256 are refused; the callback checks state and iss.
  • Credentials are scoped per MCP server and bound to their issuer; refresh tokens never reach another authorization server.
  • Remote endpoints must use HTTPS; the login callback listens on loopback only; challenge metadata is followed on the server's own origin only.
  • Logs live in a private directory, and tokens, keys and codes are never logged.

The local machine is trusted. Read the threat model, and report vulnerabilities privately.