How do you secure Eve MCP connections?
You secure Eve MCP and OpenAPI connections with guardApproval() on the connection's approval field.
guardTool() wraps a local execute function. MCP and OpenAPI connections have none. Eve calls the host. The only gate is guardApproval with no authored tools.
import { defineMcpClientConnection } from "eve/connections";import { tokenBucket } from "@arcjet/guard";import { guardApproval } from "@arcjet/guard/vercel-eve/v0";import { arcjet } from "../arcjet.js";
const mcpLimit = tokenBucket({ bucket: "mcp-access", refillRate: 30, intervalSeconds: 60, maxTokens: 30,});
export default defineMcpClientConnection({ url: "https://api.example.com/mcp", description: "Linear", approval: guardApproval(arcjet, { action: "linear.commented", rules: (ctx) => [mcpLimit({ key: ctx.session.id, requested: 1 })], }),});Inbound screening, a generic MCP handler wrap, and sandbox versus tool policy are different jobs.
Why doesn't an Eve sandbox or hook protect an MCP connection?
You mounted a Linear MCP. There is no function in your repository that posts the comment. The sandbox never sees the call. Confusing those facts is how a sandboxed agent still opens a GitHub pull request that pastes a private Linear issue.
An Eve connection is a remote MCP or OpenAPI server under agent/connections/. The server publishes tools. Eve surfaces them through connection_search and brokers auth. The call still runs in the app runtime: unrestricted network, full secrets, the token injected on the way out. Isolation is for generated bash. It isn't a policy on linear__create_comment.
Eve hooks are observe-only. They return void. For more information, see why Eve hooks can't enforce. An audit line written after the comment posts isn't a deny.
Where does the gate sit when there is no local execute function?
On the connection's approval field. Pass guardApproval() as the one function. Don't compose it with Eve's always(), once(), or never().
On DENY, Eve returns a denied status that the model can read. That differs from guardTool(), which throws. The model sees the refusal and can pick another tool. The host is never called.
A person in the path (onAllow: "user-approval") parks the call until someone clicks. That is a hold after allow. Use both when the send is irreversible.
protect() is HTTP. This is a connection gate. Eve helpers default to onGuardError: "deny". "allow" is legitimate if failing closed stops useful connections during an outage.
The last reversible point is a labeled allow or deny on this Linear comment or this GitHub issue, immediately before the send. For more information, see runtime security for LLM applications.
How is this different from securing an MCP server's tool handlers?
How to secure an MCP server or AI agent tool calls is handler-side: you own execute, so you enforce inside the tool. This document is about Eve connection approval. You didn't write the handler. Eve calls Linear from your Node.js process. There is no function to wrap and no middleware in front of the send.
Does inbound screening cover MCP connections?
No. Channel auth verifies that Slack or GitHub sent the webhook. Inbound screening can decline a turn before it starts. Neither decides whether this connection can fire. That decision sits on linear__create_comment.
Keep the sandbox and tighten its egress. Put guardApproval() on the Linear and GitHub connections. If a bash sandbox and an inbound screen are your only controls, then the send still fires.
Frequently asked questions
How do I secure Eve MCP connections?
Put guardApproval() on the connection's approval field. MCP and OpenAPI connections have no local execute to wrap.
Can I wrap an Eve MCP connection with guardTool?
No. guardTool() needs a local execute. Connections have none. The only enforcement point is guardApproval() on approval.
Does channel auth or inbound screening protect MCP calls?
No. Channel auth proves who sent the webhook. Inbound screening can decline a turn before it starts. Neither sits on the connection call.
What if Arcjet is down on the connection approval?
Eve helpers default onGuardError: "deny", so an unevaluated policy does not call the host. "allow" is legitimate if failing closed stops useful connections during an outage.
Does a denied connection throw like guardTool()?
No. Eve returns a denied status the model can read. The host is not called. That differs from guardTool(), which throws.
AI runtime security in your code
Protect your AI agent workflows with Arcjet
Get allow, deny, and redact on agent actions before the side effect.