Connect: LangGraph.js¶
Recipe · through our MCP server · their docs checked 2026-10-30
@langchain/mcp-adapters (2.0 or later) turns our MCP tools into LangChain.js tools. Use
them with createAgent, or in a LangGraph.js graph like any other LangChain tool.
Our side¶
def public_app(tools: Registry, host: str, agent_token: str, reviewer_token: str) -> Any:
"""REST at /, MCP at /mcp, one bearer check for both: for a host on the internet."""
from mcp.server.transport_security import TransportSecuritySettings
from agent_adapter.server import create_app
tokens = {
agent_token: Principal("hosted-agent"), # the token the remote framework sends
reviewer_token: Principal("reviewer@example.com", scopes=frozenset({APPROVER_SCOPE})),
}
security = TransportSecuritySettings(allowed_hosts=[host], allowed_origins=[f"https://{host}"])
return create_app(
tools,
authenticate=bearer_auth(tokens),
host="0.0.0.0", # a public bind: the public-host checks apply
allowed_hosts=[host], # REST: any other Host header gets 421
mcp_options={"transport_security": security}, # MCP: the same rule
)
In development on one machine, local_app() and http://127.0.0.1:8000/mcp are enough.
Their side¶
import { MCPAdapter } from "@langchain/mcp-adapters";
import { createAgent } from "langchain";
const adapter = new MCPAdapter({
servers: {
shopTools: {
url: "https://tools.example.com/mcp", // HTTP is the default for a URL
headers: { Authorization: `Bearer ${process.env.AA_AGENT_TOKEN}` },
},
},
});
try {
const tools = (await adapter.listTools()).filter((t) => t.name !== "send_invoice");
const agent = createAgent({ model, tools });
await agent.invoke({ messages: [{ role: "user", content: "Shipping rate to the US?" }] });
} finally {
await adapter.close();
}
| Key | Meaning |
|---|---|
servers.<name>.url |
our /mcp endpoint; leave transport and mode out for a new remote server |
servers.<name>.headers |
sent on every request: the bearer token |
listTools() |
LangChain.js tools, one per MCP tool |
close() |
ends the connections |
Older code uses MultiServerMCPClient({ mcpServers: { … } }) with getTools(); 2.0
documents MCPAdapter.
Approval¶
A LangGraph.js interrupt() before a tool node is a UI gate: it adds a step, it does not
approve our call. Confirm tools reached over MCP park as approval required: <id> and the
adapter does not send our resume keys back. Keep them out of tools (as above), or call
them through the TS client, where decide runs
the call.
Checklist¶
| Check | Why |
|---|---|
confirm tools filtered out, or hosted served |
the adapter cannot resume a parked call |
| one adapter per principal | the header is the identity |
| everything on the public-host checklist |
Sources¶
- LangChain.js MCP: https://docs.langchain.com/oss/javascript/langchain/mcp
- MCP auth (headers): https://docs.langchain.com/oss/javascript/langchain/mcp/auth.md
- MCP connections: https://docs.langchain.com/oss/javascript/langchain/mcp/connections.md