Every Tool Server You Connect Is Code You've Decided to Trust.

A tool server extends your agent with new capabilities — and with whatever its author decided those capabilities should quietly also do. Connecting one is a trust decision, not a configuration step.

Field Notes · 7 min read

Adding a tool server to an agent feels like installing a plugin: paste a configuration block, restart, and the agent can suddenly search a ticket system, query a database, or trigger a build. The effort is so small that it rarely feels like a decision, but a tool server is not a feature toggle. It is a process, run by someone, that your agent will now send data to, take text from, and in many setups hand credentials to.

That makes it a trust boundary, and most failures here come from not treating it as one. The risks below belong to the trust relationship, not to any particular protocol. Tool-server protocols such as MCP standardise how an agent discovers and calls tools; they cannot standardise whether the party on the other end deserves to be believed.

A tool server is a trust boundary

Consider what happens when you connect one. The server advertises a list of tools, each with a name, a description, and a parameter schema, and that advertisement is loaded into the model's context so the model knows what it can call. When the model picks a tool, your agent sends the server the arguments the model produced, and often a credential for whatever the server fronts. The server executes something, and its result returns to the context for the model to read and act on.

Every step is a channel. Text flows from the server into the model, data flows from your context out to the server, and, if the server runs locally, code written by its author executes on your machine with the privileges of whatever launched it. None of this is an exploit. It is the mechanism working as designed.

trust boundaryAgentholds credentialsTicketing servervetted + pinnedcreate + commentDocs servervetted + pinnedread-onlyThird-party serverunvetted, unpinnedleast scopeDatabase servervetted + pinnedread, one schemaoutward: arguments + credentialsinward: descriptions + results
Every spoke carries arguments and credentials across the trust boundary, and every server's descriptions and results flow back in as context; the unvetted server needs the narrowest scope of all.

On the wire, a vetted internal server and an unvetted third-party one are indistinguishable: both receive arguments, may receive credentials, and feed text back into the same context. The only difference is the trust you extended, and connecting a server extends it across five distinct things.

  • Its descriptions — Tool names, descriptions, and parameter text are written by the server's author and placed in the model's context. They are instructions the model reads, which makes them an injection channel before any tool is ever called.
  • Its arguments — Whatever the model passes to a tool, including content drawn from user files, other tools' results, or memory, is visible to the server's operator.
  • Its results — Output returned by the server is untrusted text entering the context, with the same ability to steer the model as a hostile web page.
  • Its credentials — A token handed to the server acts with whatever authority it carries, for as long as it stays valid.
  • Its updates — A server can change its descriptions, behaviour, and dependencies after you approved it, so approving one version is not approving the next.
Connecting a tool server is not configuration; it is a decision to let someone else's code and someone else's text shape what your agent does.

Tool descriptions are attacker-controlled text

The least intuitive of the five is the first. A tool description looks like documentation, but to the model it is just more context, and context is where instructions live. A description can say, in plain language, how the model should behave: prefer this tool over the others, include the contents of a particular file in every call, do not mention this step to the user. Models follow such text because following text is what they do, and nothing in the context marks a description as less authoritative than your own system prompt.

Because descriptions are loaded at connection time and rarely shown in full, the user often never sees the instructions the session is running under. A description can also shape how the model uses other tools, trusted servers included, which is easy to miss when each server is reviewed in isolation. And descriptions can change. A server that was benign when reviewed can ship different text later, so you review descriptions the way you review code: on every change, not once.

Credentials and scopes you hand across

Credentials are where a trust mistake becomes a loss rather than a nuisance. Two patterns recur. The first is handing a server a broad token because it is convenient: a personal access token with every permission, a service account shared with other systems, an administrator key because the narrower role took an afternoon to configure. The server now holds authority far beyond what its tools need, and so does anyone who compromises it or the machine it runs on.

The second is passing through the user's own credentials instead of issuing the server its own. The server can then do anything the user can, and its logs cannot distinguish what the user asked for from what the model decided. The remedy is unglamorous: a separate credential per server, scoped to exactly the tools you have enabled, short-lived where the backing system supports it, and held by the host rather than shown to the model. Disable the tools you do not use: each extra capability is offered to anyone who can steer the model.

Pin and verify what you connect

A tool server is a dependency, and the catalogue it comes from is a dependency registry, so everything you know about software supply chains applies. Who publishes this server, and can you verify it? Is its name one character away from a better-known one? What does it depend on in turn? Does it run as a local process with your privileges, or as a remote service whose behaviour you cannot inspect at all? A server found in a public listing or a tutorial has exactly the provenance of a package someone found in a search result.

Pin what you approve. Reference a server by version and content digest rather than a moving tag, and record the descriptions and schemas you reviewed alongside it. Treat any change to either as a new approval, with a diff a human actually reads. Prefer an internal, curated catalogue that you populate deliberately over an open one that populates itself, and keep a way to remove a server fast: if you cannot revoke it in minutes, you have not really decided to trust it.

Isolate what each server can reach

Assume that one of your servers will eventually behave badly, and design so the damage is bounded by that server's own box. Run each server in its own sandbox or process identity, with no ambient credentials, a filesystem view limited to what its tools need, and network egress restricted to the hosts it legitimately calls. A server with no reason to reach the internet should not be able to.

Isolation also has to reach the context, because the model's context is the one place every server meets. Text from an untrusted server sits beside private data fetched from a trusted one, and an agent that can also send data outward has everything an attacker needs: untrusted input, something worth taking, and a way out. It is the same combination that makes prompt injection dangerous in general. The countermeasure is to keep servers of different trust levels out of the same session, or to make sure the sensitive data and the outbound path never share a context with an untrusted server's output.

A defensible connection policy

A workable policy is short enough to enforce. Nothing is connected until it has an owner, a recorded purpose, a reviewed set of tools and descriptions, a pinned version, a dedicated least-privilege credential, and a defined isolation boundary. Changes trigger re-review, and removal is rehearsed rather than theoretical. Server trust is also only one layer: it decides what may be connected, while per-call authorization, enforced in code outside the model, decides what each call may do. You need both, because a trusted server can still be steered by text that an untrusted one placed in the same context.

When 6QURE reviews an agentic deployment, the tool-server inventory is an early artefact: what is connected, who approved it, what it can see, what it holds, and what happens inside a session when one of them turns hostile. The answers show quickly whether the connections were designed or merely accumulated.

Facing something like this in your own environment?