# How MCP tool list changes alter a live session

A live MCP session should not inherit trust for tools that did not exist when you approved it. Tool discovery looks like harmless plumbing until an agent can call a newly available action with the same conversational momentum and the same credentials behind it. At that point, the action surface changed even if nobody changed the prompt.

I have watched engineers approve a coding run for repository work, then leave it running while a connector gained a deployment or ticketing action. The usual defense is, "the agent only had the tools it needed." That sentence becomes false the moment the list changes. A long-lived agent deserves less casual trust than a short command-line process, not more.

The rule I use is simple: compare the tool inventory whenever it changes, classify the authority change, and start a fresh agent run when the change adds or materially alters an action surface. Do not turn this into a policy language project. The point is to preserve a human decision that still means what it meant when you made it.

## A tool list is an authority inventory

An MCP tool list is not a menu. It is the set of operations an agent may propose and, depending on the client, invoke. Each entry combines a name, description, input schema, and behavior that often reaches a service the model cannot inspect directly.

The Model Context Protocol specification describes tools as model-controlled functions exposed by a server. That wording matters. A client may present descriptions to the model, but the description is not a security boundary. The schema and the server-side behavior decide what the call can do.

When I review an inventory, I ask five questions about every tool:

- What data can this call read?
- What state can this call change?
- Where can its output go?
- Which credential or host does it use?
- Can its arguments widen scope beyond what the name suggests?

A tool called `get_build_status` may be read-only and narrow. A tool called `request` may be able to read, write, and send data anywhere its credential permits. The names make the first one easy to approve and the second one easy to underestimate.

The field often blurs tool discovery with permission. Discovery tells the agent what is available. Permission decides whether the agent may use it. If you merge those ideas, a mid-session refresh turns into an unreviewed permission change.

## Added tools deserve the hardest review

An added tool requires a fresh run when it creates a new route to data, commands, or an outside party. That is true even when the new tool seems related to work the agent already does.

Suppose an agent starts with `repo_search`, `read_issue`, and `create_branch`. Mid-session, the server adds `post_comment`. Someone may call this a small extension because the agent already reads issues. It is not small. Reading an issue is private retrieval. Posting a comment sends model-generated text to people who may act on it, and it can reveal details from the conversation or workspace.

Treat these additions as material by default:

- Any action that writes, deletes, deploys, publishes, or sends a message.
- Any general request tool whose destination comes from an argument.
- Any shell or SSH action, including one advertised as diagnostic.
- Any action that reads a new repository, account, host, or data category.
- Any action that uses a credential with broader scope than existing tools.

A new read action can require a restart too. Engineers sometimes reserve concern for writes, then expose an export tool that can read customer records, build logs, or secrets stored in configuration. Exfiltration begins with a read.

There are narrow exceptions. Adding a second name for an already approved, fixed operation can stay within the run if you have checked that it reaches the same service with the same arguments and credential. That is uncommon enough that I do not grant the exception from a release note.

## Removed tools are a signal, not a clean revocation

A removed tool should make you check the client, because disappearance from a fresh list does not prove the running agent lost access to it. The server may have changed its advertised list while the client still holds an earlier list in memory. Another client may refresh immediately. You cannot safely infer behavior from the removal alone.

This matters in incident review. A team sees `delete_environment` removed from the server, assumes the danger passed, and leaves an old session alive. If that client already resolved the tool definition, it may still attempt the call. The server should reject it if the implementation removed the handler, but advertised discovery and actual execution are separate things. Verify both.

Pause the agent and record the before and after inventory. Then test the removed action in a non-production setting, if you own the server, or end the session if you do not. Do not ask the model whether it still has the tool. A model response describes its context poorly enough that it should never settle an authorization question.

Removal also has a more mundane consequence: a tool rename may first appear as one removal and one addition. That is why you compare definitions rather than count names.

## Renames need proof of equivalence

A rename can be cosmetic, but it can also hide a broader contract. The dangerous cases are often ordinary maintenance work: `search_logs` becomes `query_logs`, a schema gains an optional `project` field, and the service begins accepting remote project identifiers. The name barely moved. The authority did.

Build a comparison record that is boring enough to use under pressure. For each old and new tool, record the name, description, JSON Schema, declared read-only or destructive annotations if present, backing endpoint or command, credential identity, and known side effects. JSON Schema does not reveal every server behavior, so treat it as evidence, not a full answer.

This is a useful minimal inventory shape:

```json
{
  "name": "query_logs",
  "inputSchema": {
    "type": "object",
    "properties": {
      "project": {"type": "string"},
      "query": {"type": "string"}
    },
    "required": ["query"]
  },
  "destination": "logs-api",
  "credential": "logs-read",
  "effects": ["read"]
}
```

The failure this prevents is not a malformed request. It prevents a reviewer from accepting a rename while missing the new `project` selector, which can convert a local query into cross-project retrieval.

If the old and new records differ in destination, credential, effects, or argument scope, call it an added capability and restart. If you cannot identify the backing behavior, restart anyway. "Probably the same" is not a review result.

## Schema changes matter more than descriptions

A tool can keep its name and still become more dangerous. Input schemas tell you where that often happens: new URL fields, path fields, account selectors, free-form command strings, recipient lists, and optional flags that change execution mode.

Consider a tool that begins life with this input:

```json
{
  "type": "object",
  "properties": {"issue_id": {"type": "string"}},
  "required": ["issue_id"]
}
```

Later it accepts `include_private_notes` or `destination_url`. The first field changes what the tool reads. The second introduces a route out. Neither needs a dramatic new tool name to require another approval boundary.

Descriptions also drift. A server may change "Get release details" to "Get and update release details" without changing the schema. You should read descriptions, but do not stop there. Ask the owner which handler executes, what principal it uses, and whether it makes outbound requests. A good answer names a command, endpoint, or service account. "It is just an internal helper" tells you nothing useful.

The MCP specification permits tool metadata and annotations, but annotation support and client interpretation vary. Use them as helpful labels. Do not build approval decisions on a label such as read-only when the call ultimately reaches a general-purpose endpoint.

## A fresh run creates a boundary you can explain

A fresh run is required when the approval would mean something different after the inventory change. That boundary has practical value: it stops the current process, makes the next approval attributable to a known executable, and separates the old set of calls from the new set in your records.

The process is short:

1. Stop further tool calls when the inventory changes unexpectedly.
2. Save the old and new inventories, including schemas and server version if available.
3. Mark each difference as removed, renamed, modified, or added.
4. Restart if any difference expands data access, effects, destination scope, or credential scope.
5. Approve the new run only after you can state its allowed action surface in plain language.

Do not restart merely to satisfy a ritual. Restart because it invalidates a specific earlier approval. That distinction keeps the rule usable. A team that restarts for every spelling correction will start ignoring the rule; a team that restarts for new authority will learn to recognize it.

Per-session authorization works well when it binds approval to a process that exits. Sallyport follows that model by showing the code-signing authority for a new agent process, then keeping that approval for the run until it exits. A changed inventory is still a reason to end that process if the actions it can request have widened.

## Per-call approval covers the actions that should stay awkward

Per-call approval is for actions whose consequences are too large to hide inside a broad session approval. Use it for production changes, destructive operations, public messages, financial actions, and requests that can send sensitive data to a destination chosen at runtime.

Some teams try to solve this with a long allowlist of natural-language conditions. That approach remains popular because it promises fewer interruptions. It also gives reviewers a brittle rules engine they must understand while an agent is running. The safer design uses a small number of visible choices: deny while the vault is locked, approve a known process for a bounded run, or require a click for a particular credential use.

Sallyport's per-call key setting fits this last category. The agent can ask to make the action, but it never receives the API key or SSH key in plaintext. That reduces credential leakage, while the person still decides whether this particular request should leave the machine.

Do not mark every action per-call. If a harmless read takes repeated prompts, people approve without reading. Put friction where it preserves judgment.

## The audit record must survive a disputed change

An audit trail should answer which agent process ran, what it called, and whether someone changed the available surface before the call. Logging only the final request is not enough when the dispute is, "Was that tool available when we approved the session?"

Keep an inventory snapshot with a stable digest beside session records. Record the tool name, canonical schema, server identity, and the time your client observed it. When a change occurs, preserve both snapshots and the classification decision. You do not need an elaborate database to begin; a signed or tamper-evident record with the raw discovery response is far better than an oral reconstruction the next morning.

Sallyport records agent runs in its Sessions journal and individual calls in its Activity journal, both projected from one encrypted hash-chained audit log. Its `sp audit verify` command checks that chain offline over ciphertext without needing a vault key. That is useful evidence after a change, but only if the team also records which inventory the run saw.

A hash chain detects alteration of retained entries. It does not prove you logged every event in every component, and it does not tell you whether an approval was sensible. Keep those claims separate. Security reviews get weaker when people ask one mechanism to answer questions it cannot answer.

## Treat dynamic discovery as a deployment event

If your MCP server can change tools without restarting the client, the team has effectively deployed a new action surface into a live control channel. Give that event a release discipline: identify the owner, review the diff, state the expected authority, and make a record that a later investigator can read.

The first practical action is to add an inventory snapshot to the start of every agent run and compare it before any refresh takes effect. Do not wait for a dramatic incident. The quiet rename that adds one optional destination field is exactly the sort of change that slips past people who are busy and otherwise competent.
