LangChain / LangGraph
Connect a LangGraph agent to Civic using the langchain-mcp-adapters package, which bridges LangGraph's tool interface with Civic's Streamable HTTP MCP transport.
Prerequisites
- Python 3.11+
- A Civic account at app.civic.com with a configured toolkit
- A Civic token (generate from Install → MCP URL)
- An LLM API key (e.g. Anthropic)
Installation
pip install langgraph langchain-anthropic langchain-mcp-adapters
Environment Variables
# Your full Civic toolkit URL (include profile param for production agents)
CIVIC_URL=https://app.civic.com/hub/mcp?profile=your-toolkit
# Civic token generated from app.civic.com → Install → MCP URL
CIVIC_TOKEN=your-civic-token
# Your LLM provider key
ANTHROPIC_API_KEY=your-anthropic-key
How to generate a Civic token and configure toolkit URL parameters
Connecting to Civic
Use MultiServerMCPClient to connect your agent to the Civic MCP Hub over Streamable HTTP, then pass the discovered tools to your LangGraph graph:
import os
from langchain_anthropic import ChatAnthropic
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import MessagesState, StateGraph, START
from langgraph.prebuilt import ToolNode, tools_condition
async def create_agent():
client = MultiServerMCPClient({
"civic-nexus": {
"transport": "streamable_http",
"url": os.environ["CIVIC_URL"],
"headers": {"Authorization": f"Bearer {os.environ['CIVIC_TOKEN']}"},
}
})
tools = await client.get_tools()
model = ChatAnthropic(model="claude-sonnet-4-5").bind_tools(tools)
def call_model(state: MessagesState):
return {"messages": [model.invoke(state["messages"])]}
graph = (
StateGraph(MessagesState)
.add_node("agent", call_model)
.add_node("tools", ToolNode(tools))
.add_edge(START, "agent")
.add_conditional_edges("agent", tools_condition)
.add_edge("tools", "agent")
.compile(checkpointer=MemorySaver())
)
return graph
Running the Agent
import asyncio
async def main():
agent = await create_agent()
config = {"configurable": {"thread_id": "session-1"}}
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "What events do I have today?"}]},
config=config
)
print(result["messages"][-1].content)
asyncio.run(main())
Production Configuration
Lock to a Toolkit
For production agents, always lock to a specific toolkit using the profile URL parameter:
CIVIC_URL=https://app.civic.com/hub/mcp?profile=your-production-toolkit
When a profile is specified, the session is locked by default — the agent cannot switch toolkits or modify its own guardrails. This prevents prompt injection attacks from escaping the defined tool scope.
Multi-Account Setup
For organization accounts, include the accountId parameter:
CIVIC_URL=https://app.civic.com/hub/mcp?profile=support&accountId=org_abc123
Pre-load Skills
Load specific Skills at session start using the skills parameter:
CIVIC_URL=https://app.civic.com/hub/mcp?profile=support&skills=escalation,canned-responses
Environment Variable Reference
| Variable | Description |
|---|---|
CIVIC_URL | Full Civic toolkit URL including profile and any URL parameters |
CIVIC_TOKEN | Civic token from app.civic.com → Install → MCP URL |
Reference Implementation
A complete reference implementation including a FastAPI chat UI, streaming responses, and production patterns is available at:
github.com/civicteam/langchain-nexus-reference-implementation