@masterela/mcp-authkit
v0.1.2
Published
Pluggable OAuth 2.0 + credentials elicitation library for MCP servers (TypeScript/Bun port of mcp-authkit)
Readme
mcp-authkit-ts
TypeScript/Bun port of mcp-authkit — a pluggable authentication library for MCP servers built on the official @modelcontextprotocol/sdk.
It handles two independent authentication legs:
- Leg 1 — session auth — every MCP session is gated behind a standard OIDC provider (Keycloak, Okta, Entra ID, Auth0, …) using JWT bearer tokens.
Validatorvalidates tokens andhandleWellKnownRoutespublishes the RFC 8414 / MCP-spec well-known endpoints so the MCP client drives the PKCE flow automatically. - Leg 2 — tool-level credentials — individual tools can additionally require a third-party OAuth token (
OAuthProvider) or a PAT / API key (CredentialsProvider), collected on demand via MCP URL-mode elicitation.
This port targets the pre-2026-07-28 push-style elicitation model (server.elicitInput blocking in-process) — the same model the Python original, the Go port, and the official azure-devops-mcp use today. See ARCHITECTURE.md for the full design and known deviations from the Python original.
Built for Bun; the runtime surface used (Web-standard Request/Response, Web Crypto) is Node/Deno-portable but only Bun is tested in CI.
Installation
bun add @masterela/mcp-authkitQuick start
Step 1 — Add the JWT middleware (Leg 1)
import { Validator, withAuth, handleWellKnownRoutes } from '@masterela/mcp-authkit';
const validator = new Validator();
Bun.serve({
async fetch(req) {
const wellKnown = await handleWellKnownRoutes(req, {
serverBaseUrl,
issuerUrl,
clientId,
});
if (wellKnown) return wellKnown;
return withAuth(
{
validator,
issuerUrl,
serverBaseUrl,
openPaths: ['/.well-known', '/health', '/register'],
},
mcpHandler,
)(req);
},
});Step 2 — Gate a tool behind a third-party OAuth token (Leg 2a)
import { OAuthProvider } from '@masterela/mcp-authkit';
const provider = await OAuthProvider.fromStandardOAuth2({
name: 'github',
authorizationUrl: 'https://github.com/login/oauth/authorize',
tokenUrl: 'https://github.com/login/oauth/access_token',
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scope: 'read:user repo',
redirectUri: `${serverBaseUrl}/github/callback`,
});
// Register provider.callbackPath -> provider.handleCallback in your router.
// Inside a tool handler, with `server: Server` and the caller's `sub` on hand:
const token = await provider.requireToken(server, sub);
// use token against the GitHub APIStep 3 — Gate a tool behind a PAT / API key form (Leg 2b)
import { CredentialsProvider } from '@masterela/mcp-authkit';
const creds = await CredentialsProvider.create({
name: 'confluence',
variables: {
pat: { label: 'Personal Access Token', type: 'password' },
},
serverBaseUrl,
});
// Register creds.openPaths[0] (GET) -> creds.handleEntry,
// creds.openPaths[1] (POST) -> creds.handleSubmit in your router.
const values = await creds.requireCredentials(server, sub);
const pat = values?.pat;
// use pat against the Confluence APIStorage backends
| Mode | Notes |
|---|---|
| memory (default) | In-process. Tokens lost on restart. Good for development. |
| file | AES-256-GCM-encrypted JSON files. Single-instance deployments. |
| redis | Bun.RedisClient. Multi-replica deployments. |
Select via the TOKEN_STORAGE_MODE env var (memory / file / redis). See ARCHITECTURE.md for why this port uses AES-256-GCM rather than the Python original's Fernet.
Documentation
Architecture, the two-leg auth model, and known deviations from the Python original: ARCHITECTURE.md
Contributing
bun install
bun run lint
bun run typecheck
bun test
bun run buildRedis-backed store tests require a Redis instance reachable at TEST_REDIS_URL (default redis://localhost:16379):
docker run -d --rm --name mcp-authkit-ts-test-redis -p 16379:6379 redis:7-alpine
bun test
docker stop mcp-authkit-ts-test-redis