Blog / Essay

Least privilege for agent skills

A multipurpose agent is only as safe as the most careless skill it can load. Today most skills get everything the agent has: its files, its tokens, its network. They should get what they declare, nothing more, and every use should leave a record.

Skills are the new dependencies

Agents grow by adding skills and tools: a calendar connector here, a database tool there, a set of instructions for filing expenses. With the Model Context Protocol, plugging one in takes minutes. That convenience is exactly how package ecosystems got their supply-chain problems, and agents are repeating the history faster.

  • postmark-mcp, the first malicious MCP server found in the wild, shipped fifteen clean versions before quietly copying every email it sent to its author.
  • mcp-remote, a widely used helper with more than 437,000 downloads, had a remote-code-execution flaw.
  • In April 2026 a single design flaw in MCP's STDIO transport produced ten CVEs across tools with more than 150 million downloads.

A skill is code and instructions from someone else, running with your agent's authority. We would never give a new npm package our production credentials by default. Agents do it every day.

The lethal trifecta

Simon Willison names the combination that turns any agent into an exfiltration tool: access to private data, exposure to untrusted content, and the ability to communicate externally. If an agent has all three, a single poisoned web page, issue or email can instruct it to send your data somewhere else. Meta's “Rule of Two” turns that into a design rule: an agent running without human supervision should hold at most two of the three.

The useful insight for skills is that these are capabilities, and capabilities can be granted per skill rather than per agent. A summarise-this-web-page skill needs untrusted content but no private data and no outbound channel. A send-invoice skill needs private data and an outbound channel, so it should never read untrusted content in the same run without a human in the loop.

Five rules for a safe skill

  1. Declare, then grant. A skill ships a manifest listing the tools it calls, the data scopes it reads and writes, and the network destinations it may reach. Anything not declared is denied, not prompted for.
  2. Run sandboxed. Each skill runs in its own process or sandbox with no ambient credentials, no shared file system and no network beyond its declared destinations.
  3. Check every call. The runtime checks each tool call against the manifest and the policy before it runs: argument limits, data that must not leave (account numbers, IDs, secrets), and destinations nobody approved.
  4. Ask a human for the irreversible. Sending money or email, deleting data, and anything that reaches a new external destination needs explicit approval, the way Salesforce made confirmation the default for email actions after the PipeLeak attack.
  5. Leave a signed record. Every decision (allowed, blocked, approved and by whom) goes into a tamper-evident log. When something goes wrong you can replay what the skill asked for and what it got.

None of this needs the model to behave. That is the point: the model will sometimes be fooled, so the guarantees have to live outside it. Google DeepMind's CaMeL shows the approach works, solving 77% of AgentDojo tasks with provable security against 84% with no defense.

What a manifest looks like

Here is the shape we are designing for Saaf Skills. It is illustrative, not a released format.

[skill]
name = "invoice-sender"
version = "0.3.1"

[capabilities]
tools   = ["crm.read_customer", "billing.create_invoice", "email.send"]
data    = { read = ["customers:billing"], write = ["invoices"] }
network = ["api.billing.example.com"]      # nothing else is reachable

[policy]
untrusted_content = false                  # never runs with web or inbox input
redact_before_model = ["BANK_ACCOUNT", "TAX_ID", "PHONE"]
require_approval  = ["email.send"]         # a human confirms every send
max_calls_per_run = 20

Read the manifest and you know the worst this skill can do. A reviewer can approve it in minutes, and a change that widens its capabilities shows up in the diff.

What this does not solve

Least privilege limits the blast radius; it does not stop an agent from being misled inside its permissions. A skill allowed to email customers can still be tricked into emailing the wrong one. That is why approval for irreversible actions, data redaction before the model sees anything sensitive (what Saaf does today), and detection layered underneath all still matter. Our argument is about where to put the hard guarantees: outside the model, at the boundary where tools act. More on that in Agents need a firewall.

Sources

  1. Simon Willison, The lethal trifecta for AI agents.
  2. Help Net Security, OWASP on prompt injection and Meta's Rule of Two.
  3. AuthZed, A timeline of MCP breaches.
  4. Model Context Protocol, Security best practices.
  5. VentureBeat, ForcedLeak, PipeLeak and Salesforce's confirmation default.
  6. Google DeepMind, CaMeL: Defeating Prompt Injections by Design (arXiv 2503.18813).

The manifest above is a design sketch for a research project, not a shipped format. Facts checked against the linked sources on 27 October 2026.