MCP security: permissions, tool gates, and validation

How to review MCP server permissions and gate tool calls with deterministic checks before side effects.

Last verified:

Model Context Protocol makes it easy to attach tools, resources, and prompts to an agent. That ease compresses a security decision into a setup click. This guide covers the threat model, what a gateway can see, how to gate tool calls with deterministic checks, and how to roll those checks out in shadow mode.

ValGuard is a runtime validation and policy-enforcement layer for LLM calls and agent steps. It is not an MCP client or server. Use it on the path where tool names and arguments are proposed, before side effects run.

What MCP changes

Traditional integrations are reviewed as code or API clients. MCP servers are often installed as configuration. A JSON entry launches a process or points at a remote endpoint. The client discovers tools dynamically.

A server may reach local files, databases, credentials, browser sessions, or SaaS APIs. It may expose read-only resources and mutating tools on the same connection. It may update without a client rebuild.

Discovery is not authorization. A server can describe a tool correctly and still request more runtime access than the job needs.

Threat model

ThreatWhat goes wrongControl focus
Tool poisoningMalicious or sloppy tool descriptions steer the modelReview text; pin versions; re-review on change
Over-broad permissionsOne tool name, filesystem or network far beyond needRuntime envelope separate from tool allowlist
Prompt injection via tool resultsRetrieved text instructs the model to call dangerous toolsValidate proposed calls; constrain args; least privilege downstream
Shared credentialsOne token for every serverPer-server identity and attribution
Silent capability growthUpdate adds a mutating toolSnapshot diff on reconnect

Asking the model whether a call is "safe" is a weak control. The model sees text. It may not know which mounts, env vars, or egress rules the server has.

What a validation gateway can see

On an OpenAI-compatible path, ValGuard sees chat messages and tool-call proposals you send through the proxy. It can enforce:

  • Tool name allowlists per agent
  • Argument schemas and required fields
  • Injection and secret patterns in arguments
  • Output checks before the next step consumes a tool result

It cannot see filesystem mounts inside an MCP server process you run elsewhere. Runtime isolation stays an ops control. Pair both layers.

Pattern: tool gate (P-tool-gate)

Validate tool name and args before execution.

flowchart LR
  M[Model proposes tool_call] --> V[ValGuard rules]
  V -->|pass| X[MCP / tool executor]
  V -->|block| H[Refuse or human review]
  X --> R[Tool result]
  R --> G[Optional result validation]
  G --> N[Next model turn]

Recommended sequence:

  1. Inventory servers: name, version, transport, tools, resources, hosts, credential access.
  2. Approve a version digest, not a floating branch.
  3. Map each agent to an allowlist of tool names.
  4. Attach argument schemas for high-risk tools.
  5. Run shadow mode on live proposals.
  6. Enforce block or re-ask when rates look sane.
  7. Re-review when the capability snapshot changes.

Example: allowlist and schema

Illustrative decision payload used in review playbooks:

{
  "server_name": "support-readonly",
  "tools": ["search"],
  "tool_calls": [
    {"function": {"name": "search", "arguments": "{\"q\":\"invoice 4412\"}"}}
  ],
  "resources": ["tickets"],
  "external_hosts": [],
  "credential_access": false,
  "decision": "approve"
}

Wire-format lesson from production: if your whitelist parser expects tool_calls but inventory lives only under tools, the negative test never fires. Put both inventory and executable call shape in the contract.

curl against a ValGuard agent that carries tool-call rules:

curl -s https://api.valguard.ai/v1/chat/completions \
  -H "Authorization: Bearer $VG_API_KEY" \
  -H "X-VG-Agent: tool-gate" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [
      {"role": "user", "content": "Find ticket for invoice 4412"}
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "search",
          "parameters": {
            "type": "object",
            "properties": {"q": {"type": "string"}},
            "required": ["q"]
          }
        }
      }
    ]
  }'

See also agent tool calls without delete surprises and the Agentic Tool Call Validator playbook.

Runtime permissions are separate

A harmless-looking search tool can still run with home-directory mounts and unrestricted egress. Review:

  • Filesystem mounts and working directories
  • Inherited environment variables
  • Network egress allowlists
  • Credential sources and rotation
  • For remote servers: TLS, auth, tenant separation, retention, region

Least privilege must show up in deployment config. A promise in a README is not a boundary.

Arguments need their own contract

Allowing search does not make every query safe. High-risk tools need field-level rules: tenant id required, path under an approved root, SQL limited to read-only statements. Mutating tools should use proposal then approval when the blast radius is high.

Webhook-backed tools need signature checks. See webhook signature verification.

Shadow rollout and audit

Shadow mode evaluates every rule on real traffic and logs failures without blocking. It is available on every plan, including Free. Watch would-block rate by rule ID. Tune allowlists and schemas. Then enforce.

Audit export and per-request rule rows answer "which rule blocked this tool on Tuesday?" Prompt archaeology does not.

Latency and streaming

Tool gates should be cheap next to the model. Engine packs are microseconds. HTTP path about 0.36 ms p50 with a mocked upstream. If block or re-ask is enabled, ValGuard buffers the completion before emit. See methodology and Trust.

Deployment notes

Hosted SaaS is the default. There is no public local ValGuard runtime. Enterprise self-host places the stack in your VPC under license. MCP servers you operate still need their own isolation story either way.

What this guide does not claim

  • We do not prove server code is benign. Provenance, scanning, and runtime monitoring remain separate.
  • Client-side validation does not fix a privileged database account behind the server.
  • Deterministic rules are strongest on narrow contracts. An "execute anything" tool should be redesigned, not decorated with a long prompt.
  • ValGuard does not replace MCP authorization specs or client allowlists; it complements them on the LLM path.
  • Fluent social-engineering inside tool results may still influence the model if you do not constrain the next proposed call.

Related reading

FAQ

Is MCP a ValGuard competitor? No. MCP is a protocol. ValGuard validates LLM and tool-call payloads on a proxy path.

Can I validate only mutating tools? Yes. Use stricter packs on agents that may write, and narrower allowlists.

What if ValGuard is unreachable? The model call fails. Build an explicit fallback if the agent must degrade. Trust.

Next step

Quickstart, then attach a tool allowlist and run shadow mode before enforce.