Skip to main content

LangChain.js / LangGraph

Connect a LangGraph agent to Civic using @civic/mcp-client with the built-in LangChain adapter. The adapter returns DynamicStructuredTool instances ready to drop into createReactAgent, the legacy AgentExecutor, or any custom LangGraph graph.

Prerequisites

  • Node.js 20+
  • A Civic account at app.civic.com with a configured toolkit
  • A Civic token and an LLM API key (e.g. Anthropic or OpenAI)

Installation

pnpm add @civic/mcp-client @langchain/core @langchain/anthropic @langchain/langgraph

Environment Variables

CIVIC_TOKEN=your-civic-token
ANTHROPIC_API_KEY=your-anthropic-key
CIVIC_PROFILE_ID=your-profile-id # optional: lock agent to a specific toolkit
Get Your Credentials

How to generate a Civic token and configure toolkit URL parameters

Connecting to Civic

Use CivicMcpClient with langchainAdapter() to get tools, then pass them to createReactAgent:

import { CivicMcpClient } from "@civic/mcp-client";
import { langchainAdapter } from "@civic/mcp-client/adapters/langchain";
import { ChatAnthropic } from "@langchain/anthropic";
import { createReactAgent } from "@langchain/langgraph/prebuilt";

const client = new CivicMcpClient({
auth: { token: process.env.CIVIC_TOKEN! },
});

const tools = await client.getTools(langchainAdapter());

const agent = createReactAgent({
llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }),
tools,
});

Running the Agent

async function main() {
const result = await agent.invoke({
messages: [{ role: "user", content: "What events do I have today?" }],
});

console.log(result.messages.at(-1)?.content);
await client.close();
}

main().catch(console.error);

Production Configuration

Lock to a Toolkit

For production agents, scope to a specific profile:

const client = new CivicMcpClient({
auth: { token: process.env.CIVIC_TOKEN! },
civicProfile: process.env.CIVIC_PROFILE_ID,
});

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-Turn Conversations

Pass a checkpointer to createReactAgent to retain conversation history across invocations:

import { MemorySaver } from "@langchain/langgraph";

const agent = createReactAgent({
llm: new ChatAnthropic({ model: "claude-sonnet-4-6" }),
tools,
checkpointer: new MemorySaver(),
});

const config = { configurable: { thread_id: "session-1" } };

const result = await agent.invoke(
{ messages: [{ role: "user", content: "What events do I have today?" }] },
config
);

Next Steps