Skip to main content

Civic MCP Client (Python)

Overview

civic-mcp-client is a Python MCP client for connecting to the Civic MCP Hub and exposing MCP tools to AI frameworks.

It supports:

  • Direct Civic token authentication
  • RFC 8693 token exchange (server-side only)
  • Adapter-based integration with frameworks like LangChain, Pydantic AI, and FastMCP

Start with the reference implementation:

That demo uses Civic Auth in the frontend and forwards the authenticated Civic access token to a Python backend, which then passes it into CivicMCPClient(auth={"token": ...}).

Install

pip install civic-mcp-client

Optional framework integrations:

pip install "civic-mcp-client[langchain,pydanticai,fastmcp]"

Authentication

Civic Token (direct; manual token for local testing)

Generate a Civic Token

  1. Log in to app.civic.com
  2. Click your account name in the bottom left
  3. Go to Install → MCP URL
  4. Click Generate Token and copy it immediately — it won't be shown again
warning

Never commit your token to source control. Store it in environment variables or a secrets manager. Tokens expire after 30 days.

Set Environment Variables

CIVIC_TOKEN=your-civic-token-here
CIVIC_URL=https://app.civic.com/hub/mcp

For production agents, lock to a specific toolkit by appending a profile parameter:

CIVIC_URL=https://app.civic.com/hub/mcp?profile=your-toolkit-alias

Use the Token

Pass the token as a Bearer token in the Authorization header:

headers = {"Authorization": f"Bearer {os.environ['CIVIC_TOKEN']}"}
headers: { Authorization: `Bearer ${process.env.CIVIC_TOKEN}` }
Full credentials guide

Token generation, URL parameters, OAuth vs token comparison

CIVIC_TOKEN (docs) is the same value as access_token. In the python-civic-mcp-client repo, this value is named CIVIC_ACCESS_TOKEN.

# .env
CIVIC_TOKEN=your-civic-token-here

# Optional: lock tool access to a specific profile id
CIVIC_PROFILE_ID=optional-profile-id
from civic_mcp_client import CivicMCPClient

client = CivicMCPClient(
# Use your token loading approach (env vars, secrets manager, etc.)
auth={"token": "your-civic-token-here"},
civic_profile="optional-profile-id",
)

Token Exchange (RFC 8693)

Token exchange is server-side only. Your Civic client secret must never be shipped to a browser or untrusted environment.

# .env (server)
CIVIC_CLIENT_ID=your_client_id
CIVIC_CLIENT_SECRET=your_client_secret

# Optional: lock tool access to a specific profile id
CIVIC_PROFILE_ID=optional-profile-id
from civic_mcp_client import CivicMCPClient

def get_external_access_token() -> str:
# Replace this with your IdP / external provider token fetch.
return "external-token"

client = CivicMCPClient(
auth={
"token_exchange": {
# Token exchange credentials (server-side only)
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"subject_token": get_external_access_token,
"expires_in": 3600, # optional requested lifetime in seconds
}
},
civic_profile="optional-profile-id",
)

Quick Start (manual token)

This quick start uses a manually generated Civic access token for local testing. For a production-like frontend + Python backend setup, use the demo app above.

import asyncio
from civic_mcp_client import CivicMCPClient


async def main() -> None:
client = CivicMCPClient(
auth={"token": "your-civic-token-here"},
civic_profile="optional-profile-id",
)

# Discover available tools from your configured toolkit
tools = await client.get_tools()

# Call a specific tool directly
result = await client.call_tool(
name="tool-name",
args={"foo": "bar"},
)

print("Tools loaded:", len(tools))
print("Tool call result:", result)

await client.close()


asyncio.run(main())

Framework Adapters

Use await client.adapt_for(...) with the adapter for your framework.

Depending on the adapter, adapt_for(...) will either:

  • Return a new CivicMCPClient (backend adapters like FastMCP), or
  • Return adapter-native tool outputs (e.g. tool schemas / definitions)

LangChain / LangGraph

from civic_mcp_client import CivicMCPClient
from civic_mcp_client.adapters.langchain import execute_langchain_tool_call, langchain

client = CivicMCPClient(auth={"token": "your-civic-access-token"})

tool_schemas = await client.adapt_for(langchain())

Pydantic AI

from civic_mcp_client import CivicMCPClient
from civic_mcp_client.adapters.pydanticai import pydanticai

client = CivicMCPClient(auth={"token": "your-civic-access-token"})
tool_definitions = await client.adapt_for(pydanticai())

FastMCP

from civic_mcp_client import CivicMCPClient
from civic_mcp_client.adapters.fastmcp import fastmcp

client = CivicMCPClient(auth={"token": "your-civic-access-token"})

fastmcp_client = await client.adapt_for(fastmcp())
tools = await fastmcp_client.get_tools()

Next Steps

  • See the agent deployment guide: profile locking, URL parameters, and best practices

Get help

  • Ask questions in our developer Slack