Where Agent Guard fits in the agent stack you already run
You do not need a new agent platform to limit what your agents can do. We tested the three places Agent Guard fits today, and listed what is still missing.
After we released Agent Guard 1.0, the most common question was not about features. It was about plumbing. Teams already have agents. They are built on a provider SDK, a framework, an MCP client or a cloud platform. Nobody wants to rebuild that to add a permission check.
So we tested where Agent Guard fits into an agent setup that already exists. There are three places today. Each one is shown below with what we ran and what came back. Everything used the published 1.1.0 packages, synthetic data and no API keys. Runnable script ยท Recorded results
How teams manage agents now
Most setups we see have some of these layers, rarely all of them.
The agent is built with a provider SDK or a framework, or on a cloud agent platform. It reaches tools through function calling, and more and more through MCP servers. Some teams put an MCP gateway in front of those servers. Identity providers are starting to give agents their own identities instead of borrowed user tokens. Prompt-injection filters and AI gateways watch the traffic.
Each layer answers a real question. Identity says who the agent is. Filters guess whether a message is hostile. A gateway decides which servers are reachable.
What usually stays open is a narrower question: may this task do this action, on this record, right now? An agent with a valid identity, talking to an approved server, can still delete the wrong ticket. That is the question Agent Guard answers, and it does not need to replace any of the layers above.
The one rule: one door between the model and the tools
Every integration below follows the same idea. The model never calls a tool directly, and it never holds the guard. Your code gives it one function. Every tool call goes through that function, and the function asks the guard first.
If a call can reach the tool some other way, through a shell, a second client with the same credentials or a raw database connection, the guard cannot see it. So the work of integrating is mostly finding the door and making sure there is only one.
Path 1: inside your own agent loop
This fits if you call a model provider directly or use a framework that lets you handle tool calls yourself.
async function dispatch(call) {
try {
const prepared = await guard.prepare(task, { tool: call.name, args: call.input });
if (prepared.requiresApproval) return { status: 'waiting_for_approval', actionId: prepared.id };
return { status: 'ok', result: await guard.execute(prepared) };
} catch (error) {
if (isGuardError(error)) return { status: 'denied', code: error.code };
throw error;
}
}That function is the door. It turns every tool call into an ordinary tool result, so a denial goes back to the model like any other answer instead of crashing the loop.
We fed it tool calls in the shapes the Anthropic Messages API and the OpenAI Responses API use. The task was allowed to read and delete ticket 42 only.
- Reading ticket 42 returned the ticket.
- Reading ticket 41 returned
GRANT_DENIED, and no handler ran. - Deleting ticket 42 came back as
waiting_for_approval. Nothing was deleted.
When a person approved that exact action in the host's own code, the delete ran once. Running it again with the same approval returned ACTION_USED.
The provider made no difference. The shapes differ, but each call comes down to a name and arguments. Frameworks that let you wrap or intercept tool execution work the same way: you put dispatch where the framework would call the tool.
Path 2: in front of an MCP server you already use
This fits if your agents get their tools from MCP servers.
@shieldaai/agent-guard-mcp owns the MCP client connection. It loads only the tools you bind by name and registers them with the guard unapproved. You approve each definition after a person has reviewed it, by comparing its digest with the one recorded at review.
We connected it to a small document server with one tool. The task could read the handbook only.
- Reading the handbook returned its contents.
- Reading
salariesreturnedGRANT_DENIED. The server never received the call.
Then we changed the server's tool, the way a compromised or careless server might. The description gained a sentence telling the model to send the full text to an upload_to address, and the schema gained an upload_to field.
The connection went into quarantine as soon as the server announced the change (REGISTRY_CHANGED). After the host refreshed it, the digest no longer matched the reviewed one, so nothing was approved. The same handbook call returned TOOL_UNAPPROVED, and the server received zero calls. The 1.1.0 diff showed the reviewer what changed: the description, marked INSTRUCTION_TEXT, and the new upload_to field, marked SCHEMA_FIELD_ADDED.
Those marks are hints for a person, not a verdict. The point is that a reviewer sees the new sentence before approving, instead of a bare "this tool changed".
One practical detail from the test: the MCP SDK announced the removal and the new registration as two separate notices. Our first refresh overlapped the second one and was rejected with CONNECTION_CHANGED, which is documented. The second refresh succeeded. A host should expect to refresh more than once.
Path 3: as a separate service
This fits if your agents run in another process, another container or another language, or if you want approvals and budgets to survive a restart.
@shieldaai/agent-guard-executor keeps tasks, budgets and used approvals in an encrypted SQLite ledger, and serves them over HTTP on 127.0.0.1. The agent gets two things: a URL and a task token. It never gets the executor, the admin token or the approval route.
We called it with plain fetch and JSON, so the same requests work from Python, Go or anything else that speaks HTTP. The task could refund order 17, up to 50.
- Asking to refund order 18 returned
403 GRANT_DENIED. - Preparing the refund for order 17 worked, and the action required approval.
- Executing before approval returned
403 APPROVAL_REQUIRED. - Calling the approval route with the task token returned
401 UNAUTHENTICATED. The agent cannot approve its own action.
After the operator approved through the admin route, the agent's execute call returned refunded 20 on 17. The task's snapshot showed one call and 20 spent from its budget.
The server listens on loopback only and has no login of its own. For a remote agent, you put your own authenticated transport in front of it.
Which one to start with
The deciding question is where your tool calls already pass through code you control.
If your agent loop is your own code, the dispatcher in path 1 is a small change and the fastest to try. If your agents mostly use MCP servers, path 2 also gives you the definition review, which is where tool poisoning shows up. If the agent and the tools are in different processes, or you need the record to outlive a crash, path 3 is the one that keeps state.
They combine. The MCP connection accepts the durable executor in place of the in-memory guard, so MCP tools can get the ledger too.
What it does not do yet
We would rather list the gaps than have you find them.
There is no gateway mode yet. Agent Guard wraps the MCP client connection inside your application. It is not a proxy that Claude, Cursor or another MCP client can point at without code changes. That is the integration we most want to build next.
There are no ready-made adapter packages for agent frameworks. The dispatcher works with any of them, but today you write those few lines yourself.
Approvals go through your own code or review screen. There is no Slack or Teams approval button, and no built-in export to a SIEM. The decision log and the executor's audit route have the data, but connecting them is up to you.
Identity comes from your host. If your company already gives agents identities through its identity provider, you map that identity to the task's principal yourself.
And the limits from our earlier posts still apply. Agent Guard does not detect prompt injection. It only sees calls that go through it. Once a call is allowed, the handler runs with whatever access its process has.
One last thing
None of the three paths asks you to replace the agent you have. They ask you to find the place where its tool calls leave the model and put one check there.
That place exists in every agent setup. In most of them, nothing is standing in it yet.