Skip to main content
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 nexus.civic.com with a configured toolkit
  • A Civic token (generate from Settings → Tokens)
  • 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://nexus.civic.com/hub/mcp?profile=your-toolkit

# Civic token generated from nexus.civic.com → Settings → Tokens
CIVIC_TOKEN=your-civic-token

# Your LLM provider key
ANTHROPIC_API_KEY=your-anthropic-key

Get Your Credentials

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://nexus.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://nexus.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://nexus.civic.com/hub/mcp?profile=support&skills=escalation,canned-responses

Environment Variable Reference

VariableDescription
CIVIC_URLFull Civic toolkit URL including profile and any URL parameters
CIVIC_TOKENCivic token from nexus.civic.com → Settings → Tokens

Reference Implementation

A complete reference implementation including a FastAPI chat UI, streaming responses, and production patterns is available at: github.com/titus-civic/langchain-nexus-reference-implementation

Next Steps

Agent Deployment

Production deployment guide: profile locking, URL params, authentication

Guardrails

Constrain what tools your LangGraph agent can use

Audit Trail

Query what your agent did via Civic Chat

Get Credentials

Token generation and URL parameter reference