How do I connect my agent to Enforgate?
Claude Desktop routes its MCP tools through the gateway; the other snippets ask the gateway for a verdict on a single call. Replace the key with one you created in the dashboard.
The dashboard's Connect page generates these same snippets with your real key already injected. The gateway URL below is https://api.enforgate.com; set NEXT_PUBLIC_GATEWAY_URL to change it.
Claude Desktop
Add this to claude_desktop_config.json. Claude Desktop will reach your connected tools through the gateway, which guards every call.
{
"mcpServers": {
"enforgate": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.enforgate.com/mcp",
"--header",
"Authorization: Bearer enf_your_api_key"
]
}
}
}cURL
Ask the gateway for a verdict on a single tool call. Returns { decision, reason, policyId, latencyMs }. A decision of "pending" means a human needs to approve it — the gateway never holds this request open waiting on them; it returns immediately with a referenceId and notifies an approver. The only way to learn what they decided is the approval.resolved webhook (see Settings → Webhooks).
curl -s https://api.enforgate.com/v1/check \
-H "Authorization: Bearer enf_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"serverName": "demo",
"toolName": "send_email",
"args": { "to": "someone@example.com" }
}'TypeScript
Check a call from Node or the browser before you run it. "pending" is not a final outcome — it means a human was notified and the gateway returned immediately rather than holding this request open. Subscribe to the approval.resolved webhook to learn what they decided, and resume your workflow from there.
const res = await fetch("https://api.enforgate.com/v1/check", {
method: "POST",
headers: {
"Authorization": "Bearer enf_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
serverName: "demo",
toolName: "send_email",
args: { to: "someone@example.com" },
}),
});
const verdict = await res.json();
if (verdict.decision === "pending") {
// A human was notified — this is NOT the final outcome. Enforgate never
// holds the request open waiting for them. Stash verdict.referenceId and
// resume from the approval.resolved webhook once it fires.
throw new Error(`Awaiting approval (referenceId: ${verdict.referenceId}): ${verdict.reason}`);
}
if (verdict.decision !== "allow") {
throw new Error(`Blocked by policy: ${verdict.reason}`);
}Python
The same verdict check with httpx. "pending" means a human was notified — it is not a final outcome, and the gateway does not hold this request open. Track verdict["referenceId"] and resume from the approval.resolved webhook once it fires.
import httpx
resp = httpx.post(
"https://api.enforgate.com/v1/check",
headers={"Authorization": "Bearer enf_your_api_key"},
json={
"serverName": "demo",
"toolName": "send_email",
"args": {"to": "someone@example.com"},
},
)
verdict = resp.json()
if verdict["decision"] == "pending":
# A human was notified; this is NOT the final outcome. Listen for the
# approval.resolved webhook (keyed on verdict["referenceId"]) and resume there.
raise RuntimeError(f"Awaiting approval (referenceId: {verdict['referenceId']}): {verdict['reason']}")
if verdict["decision"] != "allow":
raise RuntimeError(f"Blocked by policy: {verdict['reason']}")Session tokens
Mint a short-lived ens_ token per agent run so your long-lived key is never exposed. The session inherits the key's policy and expires automatically.
// Mint a session before each agent run.
const mintRes = await fetch("https://api.enforgate.com/v1/sessions", {
method: "POST",
headers: {
"Authorization": "Bearer enf_your_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
label: "my-agent-run",
ttlMinutes: 60,
// scopeTools: ["demo__send_email"], // optional: limit which tools are allowed
}),
});
const { token } = await mintRes.json();
// Use the ens_ session token for all calls in this run.
const verdict = await fetch("https://api.enforgate.com/v1/check", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
serverName: "demo",
toolName: "send_email",
args: { to: "someone@example.com" },
}),
});
// Token expires automatically when ttlMinutes elapses.LangChain
Wrap a LangChain tool so every invocation is checked by the gateway first: a drop-in action boundary. "pending" means a human was notified and is not a final outcome — the gateway never holds this call open waiting on them, so the tool raises and your agent should stop, not retry in a loop.
import httpx
from langchain_core.tools import tool
GATEWAY = "https://api.enforgate.com"
API_KEY = "enf_your_api_key"
def _guard(server: str, name: str, args: dict) -> None:
r = httpx.post(
f"{GATEWAY}/v1/check",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"serverName": server, "toolName": name, "args": args},
)
verdict = r.json()
if verdict["decision"] == "pending":
# A human was notified; this is NOT a final outcome and the gateway
# does not hold the request open. Resume from the approval.resolved
# webhook (keyed on verdict["referenceId"]), don't retry this call.
raise PermissionError(
f"Enforgate: awaiting approval (referenceId: {verdict['referenceId']}): {verdict['reason']}"
)
if verdict["decision"] != "allow":
raise PermissionError(f"Enforgate blocked this call: {verdict['reason']}")
@tool
def send_email(to: str, subject: str) -> str:
"""Send an email (guarded by Enforgate)."""
_guard("demo", "send_email", {"to": to, "subject": subject})
# ... your real send logic here ...
return "sent"Next steps
For Claude Desktop and other MCP clients, register your tool servers as Connected Tools first so the gateway has something to proxy. The verdict-check snippets work as soon as you have a key and a policy.
