@civic/mcp-client
v0.1.1
Published
TypeScript client library for connecting to Civic Hub and exposing MCP tools to AI SDKs
Downloads
317
Readme
@civic/mcp-client
TypeScript client library for connecting to Civic MCP Hub and exposing MCP (Model Context Protocol) tools to AI SDKs like Vercel AI, OpenAI, and Anthropic.
Installation
pnpm add @civic/mcp-clientDepending on which AI SDK you're using, also install the peer dependency:
# For Vercel AI SDK
pnpm add ai
# For OpenAI
pnpm add openai
# For Anthropic
pnpm add @anthropic-ai/sdkQuick Start
With Vercel AI SDK
import { CivicMcpClient } from "@civic/mcp-client";
import { vercelAIAdapter } from "@civic/mcp-client/adapters/vercel-ai";
import { streamText } from "ai";
const client = new CivicMcpClient({
auth: {
token: "your-access-token",
},
});
const tools = await client.getTools(vercelAIAdapter());
const result = streamText({
model: yourModel,
messages: [{ role: "user", content: "Search GitHub for civic repos" }],
tools,
});
// Clean up when done
await client.close();With OpenAI SDK
import { CivicMcpClient } from "@civic/mcp-client";
import { openAIAdapter } from "@civic/mcp-client/adapters/openai";
import OpenAI from "openai";
const client = new CivicMcpClient({
auth: { token: "your-access-token" },
});
const tools = await client.getTools(openAIAdapter());
const openai = new OpenAI();
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: "Search GitHub for civic repos" }],
tools,
});With Anthropic SDK
import { CivicMcpClient } from "@civic/mcp-client";
import { anthropicAdapter } from "@civic/mcp-client/adapters/anthropic";
import Anthropic from "@anthropic-ai/sdk";
const client = new CivicMcpClient({
auth: { token: "your-access-token" },
});
const tools = await client.getTools(anthropicAdapter());
const anthropic = new Anthropic();
const message = await anthropic.messages.create({
model: "claude-3-5-sonnet-20241022",
messages: [{ role: "user", content: "Search GitHub for civic repos" }],
tools,
});Features
- Session Management: Maintains persistent MCP sessions with automatic reconnection
- Framework Agnostic: Works with any AI SDK through adapters
- TypeScript First: Full type safety and IntelliSense support
- Flexible Authentication: Support for static tokens, async token providers, or automatic token exchange
- Lightweight: No unnecessary dependencies, minimal bundle size
API Reference
CivicMcpClient
Constructor Options
interface CivicMcpClientConfig {
// Hub URL (defaults to "https://app.civic.com/hub/mcp")
url?: string;
// Authentication — provide either a token directly or use token exchange
auth:
| { token: string | (() => Promise<string>) }
| { tokenExchange: TokenExchangeConfig };
// Scope requests to a specific profile (UUID)
civicProfile?: string; // Sent as x-civic-profile-id header
// Optional custom headers
headers?: Record<string, string>;
// Optional connection options
reconnection?: {
maxRetries?: number;
initialDelay?: number;
maxDelay?: number;
delayGrowFactor?: number;
};
}
interface TokenExchangeConfig {
clientId: string; // Civic Auth Client ID
clientSecret: string; // Civic Auth Client Secret
subjectToken: string | (() => Promise<string>); // External IdP token
authUrl?: string; // Token endpoint (default: "https://auth.civic.com/oauth/token")
expiresIn?: number; // Requested token lifetime in seconds (server default if omitted)
lockToProfile?: boolean; // Lock exchanged token to civicProfile (default: true)
}Methods
getTools<T>(adapter?: ToolAdapter<T>): Promise<T>- Get tools, optionally adapted for specific AI SDKgetServerInstructions(): Promise<string>- Get server instructions for system promptcallTool(name: string, args: unknown): Promise<CallToolResult>- Call a tool directlygetAccessToken(): string | undefined- Get the currently cached Civic Auth access token (token exchange only)close(): Promise<void>- Close the connection
Advanced Usage
Caching Client Instances
The library doesn't include caching logic - you manage CivicMcpClient instances yourself:
// Simple in-memory cache
const clientCache = new Map<string, CivicMcpClient>();
function getCivicMcpClient(userId: string): CivicMcpClient {
let client = clientCache.get(userId);
if (!client) {
client = new CivicMcpClient({ /* config */ });
clientCache.set(userId, client);
}
return client;
}Profile Scoping
const client = new CivicMcpClient({
auth: { token: accessToken },
civicProfile: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
});Dynamic Token Refresh
const client = new CivicMcpClient({
auth: {
token: async () => {
const session = await getSession();
return session.accessToken;
},
},
});Token Exchange (RFC 8693)
Server-side only. Token exchange requires a
clientSecret, which must never be exposed in browser/client-side code. Only usetokenExchangein server-side environments (Node.js, serverless functions, etc.).
If your app uses its own identity provider (Google, Auth0, etc.), the client can automatically exchange external tokens for Civic Auth access tokens. Exchanged tokens are cached and re-exchanged on expiry or when the external token changes.
const client = new CivicMcpClient({
auth: {
tokenExchange: {
clientId: process.env.CIVIC_CLIENT_ID,
clientSecret: process.env.CIVIC_CLIENT_SECRET,
subjectToken: () => getGoogleAccessToken(user),
},
},
civicProfile: "550e8400-e29b-41d4-a716-446655440000", // optional: scope to a specific profile
});
// Token exchange happens automatically on first use.
// The exchanged token is locked to the civicProfile by default.
const tools = await client.getTools(vercelAIAdapter());For setup instructions (creating an organization, linking your Client ID, and configuring token exchange), see the Civic Auth token exchange guide.
License
MIT
