Add Governance to a LangChain Agent
You’ve built a LangChain agent. It works. Now your security team wants to know: who’s using it, what tools are they calling, and what data is flowing through?
ACP answers all three without requiring you to rewrite your agent. Point your LangChain agent’s tool calls at ACP’s MCP endpoint, and every call gets identity verification, scope enforcement, PII scanning, rate limiting, and audit logging.
What you need
- A LangChain agent with tool calls (Python or JS/TS)
- An ACP Cloud workspace (sign up)
- An identity provider configured in ACP (Auth0, Okta, or Entra ID)
How it works
LangChain agents call tools. Normally these tools call APIs directly. With ACP, you replace the direct API calls with calls to your ACP endpoint. ACP verifies the user’s identity, checks permissions, scans content, and then calls the backend tool.
LangChain Agent → ACP Gateway → Backend Tool (Salesforce, GitHub, etc.)
↓
Identity verified
Scopes checked
PII scanned
Action logged
Your LangChain code stays the same. You just change where tool calls go.
Option 1: Use ACP’s MCP client SDK
The simplest integration. ACP exposes tools via MCP (Model Context Protocol). LangChain supports MCP tool providers.
Python
from langchain_mcp import MCPToolkit
# Connect to your ACP workspace
toolkit = MCPToolkit(
server_url="https://api.makeagents.run/your-slug",
transport="streamable-http",
headers={
"Authorization": f"Bearer {user_jwt}"
}
)
# Get all tools available to this user
tools = toolkit.get_tools()
# Use with any LangChain agent
from langchain.agents import create_tool_calling_agent
agent = create_tool_calling_agent(llm, tools, prompt)
ACP returns only the tools the authenticated user has access to. A user with salesforce:read sees query tools but not write tools.
JavaScript/TypeScript
import { MCPToolkit } from "@langchain/mcp";
const toolkit = new MCPToolkit({
serverUrl: "https://api.makeagents.run/your-slug",
transport: "streamable-http",
headers: {
Authorization: `Bearer ${userJwt}`,
},
});
const tools = await toolkit.getTools();
Option 2: Use ACP’s OpenAI-compatible proxy
If your LangChain agent uses OpenAI function calling, you can route tool execution through ACP’s proxy endpoint without changing your tool definitions.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="gpt-4o",
openai_api_base="https://api.makeagents.run/your-slug/v1",
default_headers={
"Authorization": f"Bearer {user_jwt}"
}
)
ACP proxies the LLM request to OpenAI, but intercepts and governs every tool call. The LLM response is passed through — only tool execution is governed.
Option 3: Wrap individual tools
For maximum control, wrap specific LangChain tools to route through ACP:
import httpx
from langchain.tools import tool
ACP_URL = "https://api.makeagents.run/your-slug"
@tool
def salesforce_query(query: str, user_token: str) -> str:
"""Query Salesforce records using SOQL."""
response = httpx.post(
f"{ACP_URL}/tools/salesforce.query",
json={"query": query},
headers={"Authorization": f"Bearer {user_token}"}
)
if response.status_code == 403:
return "Access denied: insufficient permissions"
return response.json()
This gives you tool-level control over which calls go through ACP and which don’t.
What happens on every tool call
When your LangChain agent invokes a tool through ACP:
- Identity verification — ACP verifies the JWT (RS256, JWKS-cached). Invalid or expired tokens are rejected.
- Scope enforcement — ACP checks the token’s scopes/permissions against the tool’s required scopes. Missing scopes return 403.
- Immutable rules — ACP scans for SSNs, credit card numbers, and SSRF patterns. These checks cannot be disabled.
- Content scanning — Configurable PII detection scans tool inputs and outputs.
- Rate limiting — Per-user rate limits prevent any single user from overwhelming backend services.
- Execution — ACP calls the backend tool with the user’s own credentials (OAuth per-user tokens).
- Audit logging — Every action is logged with full identity attribution.
Per-user identity in multi-user apps
If your LangChain agent serves multiple users (e.g., a web app), pass each user’s JWT with their tool calls:
async def handle_user_request(user_jwt: str, question: str):
toolkit = MCPToolkit(
server_url="https://api.makeagents.run/your-slug",
headers={"Authorization": f"Bearer {user_jwt}"}
)
tools = toolkit.get_tools()
agent = create_tool_calling_agent(llm, tools, prompt)
return await agent.ainvoke({"input": question})
Each user gets their own tools (scoped to their permissions), their own rate limits, and their own audit trail. No shared credentials.
Verify in the audit log
Check Logs in the ACP dashboard. Every LangChain tool call shows:
{
"sub": "auth0|8f3a2b1c9d4e5f6a",
"tool": "salesforce.query",
"scopes": ["salesforce:read"],
"ok": true,
"latencyMs": 142
}
Export logs to your SIEM via webhook for compliance reporting.
Back to guides · Govern a Vercel AI SDK app → · SOC 2 audit trails →