---
title: "Unified MCP Server with LangChain and LangGraph"
img: https://unified.to/images/logo.svg
date: 2026-09-25T00:00:00.000Z
updated: 2026-09-27T00:41:33.663Z
tag: Product, Guides
description: "LangChain and LangGraph give you a framework for building AI agents with tools, memory and multi-step workflows. With the official LangChain MCP adapter, every..."
url: "https://unified.to/blog/unified_mcp_server_with_langchain_and_langgraph"
---

# Unified MCP Server with LangChain and LangGraph
------
_September 25, 2026_

# Use Unified's MCP server 67k+ tools as LangChain tools in LangChain and LangGraph agents


LangChain and LangGraph give you a framework for building AI agents with tools, memory and multi-step workflows. With the official LangChain [MCP](/mcp) adapter, every tool exposed by the [Unified MCP](/mcp) server becomes a native LangChain tool. Your agents get real-time read and write access to your customers' CRM, ATS, HRIS, accounting, ticketing, file storage, messaging and commerce data, across all of Unified.to's integrations. You don't need custom integration code.


The adapter converts each Unified MCP tool into a LangChain `BaseTool`. You can pass the tools to `create_agent`, bind them to any chat model, or run them in a LangGraph `ToolNode`.


## Prerequisites

- A Unified.to workspace and your [workspace API key](https://app.unified.to/settings/api)
- At least one end-customer connection (for example, a HubSpot or Greenhouse connection), and its connection ID from [Connections](https://app.unified.to/connections)
- Python 3.10+ or Node.js 18+

## Install


**Python**


```bash
pip install langchain langchain-mcp-adapters langchain-anthropic
```


**TypeScript**


```bash
npm install langchain @langchain/mcp-adapters @langchain/anthropic
```


You can use any LangChain-supported model provider. The examples below use Anthropic, but OpenAI, Google, Mistral and others work the same way.


## Quickstart (Python)


Connect LangChain to the Unified MCP server with a few lines of code:


```python
import asyncio
import os

from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

UNIFIED_API_KEY = os.environ["UNIFIED_API_KEY"]
CONNECTION_ID = os.environ["UNIFIED_CONNECTION_ID"]  # end-customer connection


async def main():
    client = MultiServerMCPClient(
        {
            "unified": {
                "transport": "streamable_http",
                "url": f"https://mcp-api.unified.to/mcp?connection={CONNECTION_ID}",
                "headers": {"Authorization": f"bearer {UNIFIED_API_KEY}"},
            }
        }
    )

    tools = await client.get_tools()
    print(f"Loaded {len(tools)} Unified tools")

    agent = create_agent("anthropic:claude-sonnet-4-5", tools)

    result = await agent.ainvoke(
        {"messages": [{"role": "user", "content": "List the 10 most recently updated deals and summarize their stages."}]}
    )
    print(result["messages"][-1].content)


asyncio.run(main())
```


The tools available depend on the connection's integration and the permissions granted. A CRM connection exposes tools for contacts, companies, deals, and so on. An ATS connection exposes tools for candidates, applications, jobs, and so on.


## Quickstart (TypeScript)


```typescript
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
import { createAgent } from "langchain";

const client = new MultiServerMCPClient({
  mcpServers: {
    unified: {
      transport: "http",
      url: `https://mcp-api.unified.to/mcp?connection=${process.env.UNIFIED_CONNECTION_ID}`,
      headers: { Authorization: `bearer ${process.env.UNIFIED_API_KEY}` },
    },
  },
});

const tools = await client.getTools();

const agent = createAgent({ model: "anthropic:claude-sonnet-4-5", tools });

const result = await agent.invoke({
  messages: [{ role: "user", content: "Find open jobs and count applications per job." }],
});
console.log(result.messages.at(-1)?.content);

await client.close();
```


## Authentication


The Unified MCP server accepts a token either as a `token` URL parameter or as an `Authorization: bearer {token}` header. All other options must be sent as URL parameters. See [Authentication](https://docs.unified.to/mcp/authentication) for full details.


| Use case                                              | Token                                                       | Other URL parameters        |
| ----------------------------------------------------- | ----------------------------------------------------------- | --------------------------- |
| Your backend runs the agent (recommended)             | Workspace API key, in the `Authorization` header            | `connection={connectionId}` |
| Token is handed to a less-trusted runtime or end user | Public end-user token: `{connectionId}-{nonce}-{signature}` | none                        |
**A workspace API key grants access to all of your connections and your Unified.to account.** Keep it server-side, prefer the `Authorization` header over the URL, and never log full MCP URLs that contain it.


## Controlling which tools the agent sees


Agents choose tools more reliably when they see a smaller, focused list, and a shorter list also saves context. Use these [server options](https://docs.unified.to/mcp/server-options) as URL parameters to shape the tools LangChain loads:


| Option           | What it does                                                                                                                                                                       |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tools`          | Comma-delimited list of tool IDs. Only these tools are exposed.                                                                                                                    |
| `permissions`    | Comma-delimited list of Unified.to permissions. Only tools allowed by these permissions are exposed. For example, pass read-only permissions for an agent that should never write. |
| `hide_sensitive` | Hides PII fields (names, emails, telephones, and so on) from results.                                                                                                              |
```python
url = (
    "https://mcp-api.unified.to/mcp"
    f"?connection={CONNECTION_ID}"
    "&permissions=crm_contact_read,crm_deal_read"
    "&hide_sensitive=true"
)
```


## Using Unified tools in a LangGraph workflow


For more control over agent flow, bind the Unified tools to a model and route tool calls through a LangGraph `ToolNode`:


```python
from langchain.chat_models import init_chat_model
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.graph import StateGraph, MessagesState, START
from langgraph.prebuilt import ToolNode, tools_condition

client = MultiServerMCPClient(
    {
        "unified": {
            "transport": "streamable_http",
            "url": f"https://mcp-api.unified.to/mcp?connection={CONNECTION_ID}",
            "headers": {"Authorization": f"bearer {UNIFIED_API_KEY}"},
        }
    }
)
tools = await client.get_tools()

model = init_chat_model("anthropic:claude-sonnet-4-5").bind_tools(tools)


async def call_model(state: MessagesState):
    return {"messages": [await model.ainvoke(state["messages"])]}


builder = StateGraph(MessagesState)
builder.add_node("call_model", call_model)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "call_model")
builder.add_conditional_edges("call_model", tools_condition)
builder.add_edge("tools", "call_model")
graph = builder.compile()

result = await graph.ainvoke(
    {"messages": [{"role": "user", "content": "Which candidates applied this week, and for which roles?"}]}
)
```


## Multi-tenant agents


If your product serves many end customers, each customer has their own Unified connection. Create the MCP client per request or session, scoped to the current customer's connection, so an agent can only reach that customer's data:


```python
def unified_client_for(connection_id: str) -> MultiServerMCPClient:
    return MultiServerMCPClient(
        {
            "unified": {
                "transport": "streamable_http",
                "url": f"https://mcp-api.unified.to/mcp?connection={connection_id}",
                "headers": {"Authorization": f"bearer {UNIFIED_API_KEY}"},
            }
        }
    )


async def handle_request(customer, prompt: str):
    tools = await unified_client_for(customer.unified_connection_id).get_tools()
    agent = create_agent("anthropic:claude-sonnet-4-5", tools)
    return await agent.ainvoke({"messages": [{"role": "user", "content": prompt}]})
```


Because Unified normalizes data across integrations, the same agent code works whether one customer uses Salesforce and another uses HubSpot.


## Calling tools directly


Unified tools are standard LangChain tools, so deterministic workflows can call them without an agent:


```python
tools_by_name = {t.name: t for t in await client.get_tools()}
contacts = await tools_by_name["list_crm_contacts"].ainvoke({"limit": 50})
```


Tool names vary by integration and category. Print `[t.name for t in tools]` to see what a connection exposes.


## Regions


| Region | Streamable HTTP endpoint            |
| ------ | ----------------------------------- |
| US     | `https://mcp-api.unified.to/mcp`    |
| EU     | `https://mcp-api-eu.unified.to/mcp` |
Use the endpoint for the region where your Unified.to workspace is hosted.


## Troubleshooting

- **401 / unauthorized:** Check that the token is a valid workspace API key or a correctly signed end-user token. When you use a workspace API key, make sure the `connection` parameter is also present.
- **Fewer tools than expected:** The tool list reflects the connection's integration, the scopes granted when the customer authorized it, and any `tools` or `permissions` filters in the URL.
- **Agent picks the wrong tool:** Narrow the tool list with `tools` or `permissions`, and describe the task more specifically in your system prompt.
- **Options not applied:** Only the token can go in a header. Every other option must be a URL parameter.

## Related

- [Unified MCP Server overview](https://docs.unified.to/mcp)
- [Authentication](https://docs.unified.to/mcp/authentication)
- [Server options](https://docs.unified.to/mcp/server-options)
- [LangChain MCP adapters (Python)](https://github.com/langchain-ai/langchain-mcp-adapters)