Development Choices

Restrict the tools an MCP server exposes

Author
Joseph Trasatti Member of technical staff
Published
Section
MCP
Length
4 min read3 sources cited

Cloudinary's remote MCP servers accept a cloudinary-tools header holding a comma-separated allowlist, so one connection advertises three tools instead of the server's full set. Enforcement is server-side, unlike a client-side toggle. Fewer tool definitions load into context, the model chooses between four plausible tools rather than forty, and an unadvertised delete tool cannot be called.

Before you start

Three things have to be true before an allowlist is available to you.

Steps

  1. Scope the connection to one job. Write it down in a sentence: “look up assets and read their metadata for the docs pipeline”, not “Cloudinary”. A connection scoped to everything cannot be narrowed, because every tool is in scope by definition. Two narrow connections cost no more to run than one wide one, and each gets its own allowlist.

  2. Read what the server currently advertises. Ask your client to list the connection’s tools; the server answers tools/list with a name, a description and a JSON Schema of the inputs for every tool it offers. Count them. Each of those definitions is loaded into the model’s context on connection, so an unrestricted server spends tokens describing capabilities the task will never call — on every turn, before the model has done anything. The same arithmetic across several connections at once is worked through in the context cost of leaving several MCP servers connected.

  3. Pick the smallest set that completes the job. Read the descriptions rather than guessing from names, and keep the tools the job calls directly plus anything they depend on. The reason to be strict here is not the token count: targeting improves before cost does. A model choosing between four plausible tools misfires far less often than one choosing between forty, and that shows up as fewer wrong calls long before it shows up on a usage graph. When you are unsure about a tool, leave it out — a missing tool produces a loud failure, an unnecessary one produces a quiet misfire.

  4. Add the cloudinary-tools header. Cloudinary’s remote servers accept a comma-separated allowlist in it, so one connection can expose three tools out of the server’s full set (Cloudinary’s MCP and LLM tooling docs).

    {
      "mcpServers": {
        "cloudinary-assets": {
          "type": "http",
          "url": "https://asset-management.mcp.cloudinary.com/mcp",
          "headers": { "cloudinary-tools": "tool-a,tool-b,tool-c" }
        }
      }
    }

    Replace the placeholders with the exact names from step 2; the header matches names, not descriptions. This is the same block that holds whatever the connection uses to authenticate — if that is unsettled, see OAuth against API key headers.

  5. Leave destructive tools out of every connection that does not need one. This is the cheapest blast-radius control available, because a connection that never advertises a delete tool cannot be talked into calling one — not by a confused plan, not by instructions sitting in an asset’s metadata or a page the agent read on the way. OWASP’s Top 10 for LLM applications treats excessive agency as its own failure class, and the remedy it describes is this one: constrain the tools reachable in a session rather than trusting the model’s intentions inside it. It is not a substitute for scoped credentials. The header narrows what this session can reach; the credential decides what the key can do at all. Do both.

  6. Reconnect and count the tools again. The list should be exactly your allowlist. A name misspelled in the header fails silently — it simply never matches — so if the count is short by one, check the spelling against the list you read in step 2. Verify from the connection, not from your client’s settings screen: the header is enforced server-side, unlike disabling a tool in a client’s own settings, which only hides it from the model on that one machine. A tool switched off locally is still advertised, and a teammate who copies the URL without the header gets the full set.

What done looks like

The connection advertises only the tools named in the header, and the model’s context carries that many definitions instead of the server’s full inventory. The model’s tool choice is made among a handful of candidates that are all plausible for the job. Anything outside the list is unreachable through this connection regardless of what the model is asked or told, on every machine that uses it, because the filtering happens on the server before the tool list is ever sent. Adding a capability back is one more name in the header and a reconnect.

Sources

  1. for every tool it offers modelcontextprotocol.io
  2. Cloudinary's MCP and LLM tooling docs cloudinary.com
  3. Top 10 for LLM applications owasp.org

See also