@civic/auth-mcp

中文文档
1.0.0发布于 2026/10/08MITTypeScriptESM + CJS构建来源已验证近 30 天下载 5792

Civic Auth integration for MCP servers

原始使用文档
文档目录18 节
  1. 🚀 Why Choose Civic Auth?
  2. 📦 Features
  3. 🚀 Quick Start
  4. 🛠️ Usage Examples
  5. 🚀 Express Middleware (Recommended)
  6. ⚙️ Configuration Options
  7. ⚡ Framework-Agnostic Usage
  8. 💻 Client Integration
  9. 🖥️ CLI Client
  10. 🪪 Client ID Metadata Documents (CIMD)
  11. 💾 Token Persistence
  12. 🎫 Token Authentication
  13. 🛠️ Dynamic Client Registration
  14. 🔄 Legacy OAuth Support
  15. Configuration
  16. ✨ Why Choose @civic/auth-mcp?
  17. 🌟 What's Next?
  18. 📄 License

@civic/auth-mcp

CI npm version License: MIT codecov

🔐 The Fastest Way to Add Authorization to MCP Servers

This is the fastest way to add authorization to MCP servers, enabling secure tool use in LLMs and providing confidence and security for you and your customers.

It works with any compliant OAuth2/OIDC provider, while being optimized for Civic Auth's lightning-fast authentication experience.

🚀 Why Choose Civic Auth?

Civic Auth delivers the fastest, most flexible authentication experience for modern applications:

  • ⚡ Lightning Setup: Get users authenticated in under 60 seconds with familiar sign-in options (email, Google, passkeys, wallets)
  • 🔄 Adaptable Onboarding: Seamless experience for all users - supports existing wallets or creates embedded wallets automatically
  • 🌐 Web3 Support: Native support for Solana, Ethereum, Base, Polygon, BSC, Arbitrum, and other EVM chains
  • 📱 Universal Compatibility: Works everywhere - React, Next.js, Node.js, or any OIDC/OAuth 2.0 environment

Ready to experience the fastest auth? Get your Client ID at auth.civic.com and be up and running in minutes.

📦 Features

  • Compliant with the latest version of the Model Context Protocol (MCP) specification, particularly regarding Authorization Server discovers spec
  • Client and server SDKs for easy integration
  • Express middleware for quick setup
  • Framework-agnostic core for use with any nodejs framework
  • CLI authentication for integration with command-line tools

🚀 Quick Start

Install the dependencies:

npm install @civic/auth-mcp

The server-side middleware has no dependency on an MCP SDK: it works alongside the v2 SDK (@modelcontextprotocol/server), the v1 SDK (@modelcontextprotocol/sdk), or no SDK at all. The @civic/auth-mcp/client entry point (see Client Integration) is built on the v2 client package, @modelcontextprotocol/client.

Add the middleware to your express app:

app.use(await auth());

Out of the box, this uses Civic Auth as an authentication provider, which is the fastest way to get started.

Once you are ready to go live, obtain a client ID from auth.civic.com and replace the default client ID in the middleware:

app.use(await auth({
    clientId: "...", // Get your client ID from auth.civic.com
}));

That's it!

🛠️ Usage Examples

The fastest way to secure an MCP server. Works smoothly with the MCP TypeScript SDK v2 (@modelcontextprotocol/server with the @modelcontextprotocol/node transport) and with the v1 SDK (@modelcontextprotocol/sdk).

import express from "express";
import {auth} from "@civic/auth-mcp";
import {NodeStreamableHTTPServerTransport} from "@modelcontextprotocol/node";
import {McpServer} from "@modelcontextprotocol/server";

// Create your Express app
const app = express();

// Add auth middleware
app.use(await auth());

// Create your MCP server
async function getServer() {
    const server = new McpServer({
        name: "my-mcp-server",
        version: "0.0.1",
    });

    // Register your tools
    server.registerTool(
        "tool-name",
        { description: "Example tool" },
        async (ctx) => {
            // Access the authenticated user's information.
            // The middleware sets req.auth; the transport forwards it as ctx.http.authInfo.
            // (On the v1 SDK, use server.tool(...) and read extra.authInfo instead.)
            const user = ctx.http?.authInfo?.extra?.sub;
            return {
                content: [
                    {
                        type: "text",
                        text: `Hello ${user}!`,
                    },
                ],
            };
        }
    );

    // Set up the transport layer
    // In production you may need session management
    const transport = new NodeStreamableHTTPServerTransport({
        sessionIdGenerator: undefined,
    });

    await server.connect(transport);

    return { transport, server };
}

// Set up MCP endpoint
app.post("/mcp", async (req, res) => {
    const { transport, server } = await getServer();
    await transport.handleRequest(req, res, req.body);
    res.on('close', () => {
        transport.close();
        server.close()
    })
});

⚙️ Configuration Options

app.use(await auth({
  // Use a different auth server:
  wellKnownUrl: 'https://accounts.google.com/.well-known/openid-configuration',
    
  // Or specify additional options
  scopesSupported: ['openid', 'profile', 'email', 'custom:scope'],
  
  // Protect a different route (defaults to '/mcp')
  mcpRoute: '/api',
    
  // Enrich auth info with custom data from your database
  onLogin: async (authInfo, request) => {
    // Look up user data based on the JWT subject claim
    const userData = await db.users.findOne({ sub: authInfo?.extra?.sub });
    // Return enriched auth info
    return {
      ...authInfo,
      extra: { ...authInfo.extra, ...userData }
    };
  },
  
  // Legacy OAuth options
  enableLegacyOAuth: true, // Defaults to true
  stateStore: customStateStore, // Custom state store for OAuth flows
}));

⚡ Framework-Agnostic Usage

For non-Express frameworks, use the McpServerAuth class directly:

import { McpServerAuth } from "@civic/auth-mcp";

// Initialize the auth server
const mcpServerAuth = await McpServerAuth.init();

// Or with custom data enrichment
const mcpServerAuth = await McpServerAuth.init({
  onLogin: async (authInfo, request) => {
    const userData = await db.users.findOne({ sub: authInfo?.extra?.sub });
    return {
      ...authInfo,
      extra: { ...authInfo.extra, ...userData }
    };
  },
});

// In your framework's route handler:
// 1. Expose the protected resource metadata
if (path === '/.well-known/oauth-protected-resource') {
  const metadata = mcpServerAuth.getProtectedResourceMetadata('https://my-server.com/mcp');
  return json(metadata);
}

// 2. Validate bearer tokens
try {
  const authInfo = await mcpServerAuth.handleRequest(request);
  // User data will be in authInfo.extra
} catch (error) {
    return unauthorized('Authentication failed');
}

💻 Client Integration

This library includes a client SDK for easy integration with MCP servers, supporting various authentication methods.

The client is built on the v2 MCP client package, so install it alongside the library and import from the @civic/auth-mcp/client entry point:

npm install @civic/auth-mcp @modelcontextprotocol/client

CLIClient negotiates the protocol version automatically (versionNegotiation: { mode: "auto" }): it probes server/discover for servers on protocol revision 2026-07-28 and falls back to initialize for servers on earlier revisions. Pass your own versionNegotiation option to override this.

🖥️ CLI Client

The CLI client allows you to authenticate and connect to MCP servers directly from the command line. When authentication is required, it will automatically open a browser window for the user to complete the authentication flow.

import { CLIAuthProvider, RestartableStreamableHTTPClientTransport, CLIClient } from "@civic/auth-mcp/client";

// Create the auth provider
const authProvider = new CLIAuthProvider({
  clientId: "your-client-id", // Get your client ID from auth.civic.com
  // clientSecret: "your-secret", // Optional: only for non-PKCE auth servers
  // successHtml: "<html><body><h1>Success!</h1></body></html>", // Optional: custom success page
  // errorHtml: "<html><body><h1>Error: {{error}}</h1></body></html>", // Optional: custom error page
});

// Create the restartable transport with auth provider
// This transport allows restarting the connection after authorisation is granted
const transport = new RestartableStreamableHTTPClientTransport(
  new URL("http://localhost:33006/mcp"),
  { authProvider }
);

// Create and connect client
const mcpClient = new CLIClient(
    { name: "cli-example", version: "0.0.1" }, 
    { capabilities: {} }
);

// Connect to the server
await mcpClient.connect(transport);

🪪 Client ID Metadata Documents (CIMD)

Instead of pre-registering a client ID, your client can identify itself with a Client ID Metadata Document (SEP-991) — a JSON document hosted at an HTTPS URL that doubles as the OAuth client_id. This is the preferred registration mechanism in the MCP spec since 2025-11-25 and is used by Claude, Claude Code, and VS Code.

If your metadata document is already hosted somewhere, pass its URL instead of a client ID:

const authProvider = new CLIAuthProvider({
  clientMetadataUrl: "https://myagent.example.com/oauth/client-metadata.json",
});

When the authorization server advertises client_id_metadata_document_supported, the URL is used directly as the client_id; otherwise the flow falls back to Dynamic Client Registration. A pre-registered clientId, if also provided, always takes precedence.

To host the document itself, build it with buildClientMetadataDocument or serve it with the Express handler — mounted at the exact path of its public URL:

import { clientMetadataHandler } from "@civic/auth-mcp";
import { LOOPBACK_REDIRECT_URIS } from "@civic/auth-mcp/client";

app.get(
  "/oauth/client-metadata.json",
  clientMetadataHandler({
    url: "https://myagent.example.com/oauth/client-metadata.json",
    clientName: "My Agent",
    // For CLI/native clients use portless loopback URIs (RFC 8252);
    // for web clients list your hosted callback URLs.
    redirectUris: LOOPBACK_REDIRECT_URIS,
  })
);

💾 Token Persistence

By default, tokens are stored in memory and lost when the process exits. You can configure persistent token storage by implementing the TokenPersistence interface.

In-Memory Token Persistence (Default)

Tokens are stored in memory and lost when the process exits:

import { CLIAuthProvider, InMemoryTokenPersistence } from "@civic/auth-mcp/client";

const authProvider = new CLIAuthProvider({
  clientId: "your-client-id",
  tokenPersistence: new InMemoryTokenPersistence(), // Explicit, but this is the default
});

Custom Token Persistence

Implement your own persistence strategy by implementing the TokenPersistence interface:

import { TokenPersistence } from "@civic/auth-mcp/client";
import type { OAuthTokens } from "@modelcontextprotocol/client";

class MyTokenPersistence implements TokenPersistence {
  async saveTokens(tokens: OAuthTokens): Promise<void> { ... }

  async loadTokens(): Promise<OAuthTokens | undefined> { ... }

  async clearTokens(): Promise<void> { ... }
}

const authProvider = new CLIAuthProvider({
  clientId: "your-client-id",
  tokenPersistence: new DatabaseTokenPersistence(),
});

🎫 Token Authentication

The TokenAuthProvider simplifies connecting to MCP servers with pre-obtained JWT tokens. Use this if you have an app that already handles authentication, e.g. via Civic Auth.

import { TokenAuthProvider, RestartableStreamableHTTPClientTransport } from "@civic/auth-mcp/client";
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";

// Create with pre-obtained token
const authProvider = new TokenAuthProvider("your-jwt-token");

// Create transport and client
const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:33006/mcp"),
  { authProvider }
);

const mcpClient = new Client(
  { name: "example-client", version: "0.0.1" },
  { capabilities: {} }
);

await mcpClient.connect(transport);

🛠️ Dynamic Client Registration

Some MCP Clients use OAuth 2.0 Dynamic Client Registration to automatically register with the auth server.

This means that the client ID sent to your MCP server will not match your client ID, as a new client ID is generated dynamically during the registration process for each client.

Civic-Auth, and the @civic/auth-mcp library support this. To enable it, set the allowDynamicClientRegistration option to true in the MCP server configuration:

app.use(await auth({
    allowDynamicClientRegistration: true,
}));

This will ensure that your client ID is passed to the auth server during client registration, and resultant tokens will be valid for your MCP server only.

Note - this feature is available for the Civic Auth provider only. Behaviour may differ for other providers.

Issuer acceptance

When using Civic Auth, discovery advertises the tenant-scoped issuer (e.g. https://auth.civic.com/oauth/<tenant>/), but token validation accepts both that tenant-scoped issuer and the base Civic issuer (https://auth.civic.com/oauth/). This lets base-issuer tokens — such as client_credentials tokens and token-exchange output — and tenant-scoped tokens both validate against the same server. All Civic issuers share a single signing key, so accepting both does not weaken signature verification.

To accept a wider or different set of issuers (for example, any Civic tenant in a multi-tenant deployment), provide an issuerValidator predicate, which fully overrides the default check:

app.use(await auth({
    allowDynamicClientRegistration: true,
    issuerValidator: (iss) => iss?.startsWith("https://auth.civic.com/oauth/") ?? false,
}));

🔄 Legacy OAuth Support

This library includes support for the legacy MCP OAuth specification to ensure compatibility with existing clients while they transition to the latest specification.

Important: This legacy support will be removed in a future version once all major clients have updated to the current MCP specification.

The legacy OAuth mode:

  • Is enabled by default to ensure maximum compatibility
  • Provides OAuth endpoints directly on the MCP server (e.g., /authorize, /token)
  • Transparently proxies OAuth flows to the configured authorization server

To disable legacy OAuth support (recommended once your clients are updated):

app.use(await auth({
    enableLegacyOAuth: false,
}));

We recommend monitoring your client usage and disabling legacy support once all clients have been updated to use the standard OAuth flow with authorization server discovery.

NOTE If testing with Claude (Web or Desktop), you will need to deploy your server to a remote environment using https first, as Claude does not support localhost MCP integrations.

NOTE When using the legacy OAuth flow behind a proxy, you can either enable Express's "trust proxy" setting (see Express behind proxies), or configure the protocolHeader and hostHeader options to read the correct values from proxy headers:

// Option 1: Trust proxy (Express handles X-Forwarded-* headers)
app.enable('trust proxy');

// Option 2: Explicit header configuration
app.use(await auth({
  protocolHeader: 'X-Forwarded-Proto',
  hostHeader: 'X-Forwarded-Host',
}));

// Option 3: Force HTTPS regardless
app.use(await auth({ forceHttps: true }));

Sub-Path Mounting and Well-Known Discovery

Per RFC 9728, the .well-known/oauth-protected-resource endpoint is constructed by inserting the well-known path between the host and the resource path. For example, if your resource is at https://example.com/hub/mcp, clients will discover metadata at:

GET /.well-known/oauth-protected-resource/hub/mcp

When mounting the middleware at a sub-path (e.g. app.use("/hub", await auth())), the well-known endpoint will be served at /hub/.well-known/oauth-protected-resource. You will need to configure a rewrite rule (e.g. in your reverse proxy or Express app) to route /.well-known/oauth-protected-resource/hub/mcp to /hub/.well-known/oauth-protected-resource.

Similarly, the legacy OAuth endpoints (/authorize, /token, /register, /oauth/callback, /.well-known/oauth-authorization-server) are always advertised at the root. When the middleware is mounted at a sub-path, these endpoints will be served under that path (e.g. /hub/authorize). You will need to configure rewrite rules to route the root paths to the mounted paths.

Custom State Store

By default, the legacy OAuth mode uses an in-memory state store for managing OAuth flow state between redirects. For production deployments with multiple servers or processes, you can provide a custom state store implementation:

// Implement a custom state store (e.g., using Redis)
class RedisStateStore implements StateStore {
  ...
}

// Use the custom state store
app.use(await auth({
  stateStore: new RedisStateStore(),
}));

Configuration

The auth() middleware accepts the following configuration options:

OptionTypeDefaultDescription
clientIdstringPublic Civic client IDOAuth client ID / Tenant ID for token validation
wellKnownUrlstringhttps://auth.civic.com/oauth/.well-known/openid-configurationURL to the auth server's OIDC configuration
scopesSupportedstring[]['openid', 'profile', 'email']OAuth scopes to support
protocolHeaderstring-Header name to read protocol from (e.g. X-Forwarded-Proto)
hostHeaderstringhostHeader name to read host from (e.g. X-Forwarded-Host)
basePathstring/Base path for auth endpoints
mcpRoutestring/mcpThe MCP route to protect with authentication
onLoginfunction-Optional callback to enrich auth info with custom data
allowDynamicClientRegistrationbooleanfalseEnable dynamic client registration
enableLegacyOAuthbooleantrueEnable legacy OAuth proxy mode (deprecated)
stateStoreStateStoreIn-memory storeCustom state store for OAuth flow (legacy mode only)
jwksobject-Local JWKS for testing (bypasses remote JWKS fetch)
disableClientIdVerificationbooleanfalseSkip the client_id/tid check
forceHttpsbooleanfalseForce metadata URLs to https (e.g. behind a TLS-terminating proxy)
issuerValidatorfunction-Predicate overriding the default issuer check; accept a token iff it returns true for the token's iss

✨ Why Choose @civic/auth-mcp?

🚀 Zero-Friction Setup

  • Drop-in Express middleware that works out of the box
  • One-line integration

🔒 Enterprise Security, Startup Speed

  • Works seamlessly with Civic Auth, a battle-tested and secure authentication provider
  • Automatic token refresh and session management
  • Privacy-first design with minimal data collection
  • PKCE-support

🎯 Developer Experience First

  • CLI authentication with automatic browser flow
  • Multiple auth patterns: tokens, OAuth flow, pre-authenticated
  • TypeScript-first with comprehensive type safety

🌐 Production Ready

  • Comprehensive error handling and retry logic
  • Built-in transport layer with connection recovery
  • Lightweight with minimal dependencies

🌟 What's Next?

  • 📚 Documentation: Comprehensive guides at docs.civic.com
  • 🐛 Issues: Report bugs or request features on GitHub
  • 💬 Community: Join our Discord for support and discussions
  • 🔄 Updates: Follow @civickey for the latest updates

📄 License

It is provided as-is, without warranty of any kind, express or implied. Civic makes no guarantees of fitness for a particular purpose or ongoing support. Use of this library is governed solely by the terms of the MIT License.

By using this software, you agree that Civic shall not be held liable for any damages arising from its use, performance, or integration.

Note: The @civic/auth-mcp library is released as an open-source project under the MIT License.

It is provided without warranty or support guarantees.

版本与发布记录

@civic/auth-mcp 共发布 44 个版本,首次发布于 2025/05/22;过去 12 个月发布了 9 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.0.0latest2026/10/081 个月前
1.0.0-beta.0beta2026/10/081 个月前
0.5.02026/06/294 个月前
0.4.02026/06/124 个月前
0.3.12026/03/277 个月前
0.3.02026/03/177 个月前

其他发布通道:alpha → 0.2.3-alpha.0,beta → 1.0.0-beta.0。

关于 @civic/auth-mcp 的常见问题

@civic/auth-mcp 是什么?

Civic Auth integration for MCP servers

如何安装 @civic/auth-mcp?

在项目目录运行 npm install @civic/auth-mcp,或使用 pnpm add / yarn add / bun add。

@civic/auth-mcp 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@civic/auth-mcp 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@civic/auth-mcp 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@civic/auth-mcp 可以商用吗?

@civic/auth-mcp 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@civic/auth-mcp 的下载量有多少?

根据 npm 官方下载统计,@civic/auth-mcp 在 2026/10/10 – 2026/11/08 这 30 天内被下载 5,792 次,日均约 193 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@civic/auth-mcp 还在维护吗?

最新版本 1.0.0 发布于 2026/10/08(1 个月前);至今共发布 44 个版本,其中过去 12 个月发布了 9 个;首次发布于 2025/05/22。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。