Govern LangGraph's MCP tool calls with DVARA
langchain-mcp-adapters loads MCP tools into LangChain and LangGraph agents.
Load them from DVARA instead of from each MCP server. Your agent gets the same
tools, and DVARA applies your workspace's policy to every call, redacts personal
data in results and records each one.
What you need
- A DVARA MCP Gateway with at least one MCP server behind it. The
MCP quickstart gives you one in
about five minutes. It runs at
http://localhost:8080/mcp, and its API key isDEMO_API_KEYin the quickstart's.env. - A DVARA workspace API key.
- Python 3.11 or later.
pip install langchain-mcp-adapters langchain
Load the tools from DVARA
import os
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({
"dvara": {
"transport": "streamable_http",
"url": "http://localhost:8080/mcp",
"headers": {"Authorization": f"Bearer {os.environ['DVARA_API_KEY']}"},
}
})
tools = await client.get_tools()
DVARA lists each server's tools with the server's id in front, so the quickstart's
tools load as demo-tools__get_order_status, demo-tools__lookup_customer and
demo-tools__delete_customer.
Give the tools to your agent
from langchain.agents import create_agent
agent = create_agent("anthropic:<model-id>", tools)
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Look up customer C-42, then delete them."}]}
)
Any LangChain chat model works; the agent only sees DVARA.
What happens to each call
You can see this without a model by calling the tools yourself:
for tool in tools:
args = {"orderId": "ORD-1001"} if tool.name.endswith("get_order_status") else {"customerId": "C-42"}
try:
print(tool.name, "->", await tool.ainvoke(args))
except Exception as e:
print(tool.name, "-> refused:", e)
Against the quickstart:
demo-tools__delete_customer -> refused: delete_customer is irreversible and not allowed in this workspace
demo-tools__lookup_customer -> [{'type': 'text', 'text': '"Customer C-42: Jane Doe, [REDACTED_EMAIL], [REDACTED_PHONE_NUMBER],\\ncard on file [REDACTED_CREDIT_CARD], plan Enterprise."', ...}]
demo-tools__get_order_status -> [{'type': 'text', 'text': '"Order ORD-1001: shipped, arriving Thursday."', ...}]
- The delete never reaches the MCP server. The quickstart's policy denies
it, and the adapter raises the refusal as an
McpErrorwith the policy's message. - The customer record comes back redacted. The email, phone number and card number are replaced before your agent or the model sees them.
- Every call is recorded. Find them in Flightdeck under MCP → Tool Calls.
Govern the model calls too
The steps above govern your agent's tool calls. Its model calls are a separate stream: point the agent's model at DVARA's LLM Gateway as well, and the same workspace governs both.
pip install langchain-openai
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="<model-id>",
base_url="http://localhost:8080/v1",
api_key=os.environ["DVARA_API_KEY"],
)
agent = create_agent(model, tools)
The Gateway needs a model provider for this. The MCP quickstart doesn't set one up. See Connect LangChain for the full setup, and Agent sessions to tie an agent's model and tool calls into one session in the audit trail.
Where to go next
- Policy as Code to write your own rules.
- MCP Gateway for approvals, loop detection and the audit trail.