# MCP security guide

> Inventory MCP servers, scope individual tools and credentials, and test policy before allowing agent access.

An MCP server is code with access to tools, data, and often your network. The
friendly name in a config file does not make it safe. If a server can read a
repository, create a release, query production, or send a message, an agent may
be able to do the same thing through that server.

MCP security starts with a plain question: what can this server do from the
account and machine where it runs?

## Why MCP servers need their own security review

There are three common failure modes.

**The server is malicious.** It was installed from an untrusted package or
copied from a config snippet without reviewing the code.

**The server is legitimate but over-scoped.** A GitHub server used to read
issues also has permission to delete repositories. A database server used for
schema inspection can write to production.

**The agent is manipulated.** Prompt injection in a README, issue, web page, or
tool result convinces the agent to call a valid tool for the wrong reason.

The third case is easy to miss.

> [!PRINCIPLE]
> Trusting an MCP server does not mean every call through it is safe.

## Start with an inventory

You cannot secure MCP servers you do not know exist.

```sh
agentjail mcp scan
```

The inventory checks supported Claude Code, Codex, and Cursor configurations,
plus package and container sources. Use it to answer:

- Which servers are configured?
- Where did each server come from?
- Which agents can call it?
- Does the server run locally or connect to a hosted service?
- What account, token, filesystem path, or database can it reach?

Run the inventory again when configuration changes. MCP setup tends to spread
through copied JSON, dotfiles, and team onboarding scripts.

## Allow servers deliberately

agentjail uses an explicit MCP allowlist. During installation it seeds the list
from servers that are already configured, so review that initial trust instead
of assuming it is a permanent verdict.

```sh
agentjail mcp list
agentjail mcp allow github
agentjail mcp block my-payment-bot
```

The same configuration can live in `~/.agentjail/policy.yaml`:

```yaml
mcp:
  allowed:
    - github
    - context7
  blocked:
    - "*payment*"
    - "*billing*"
```

The blocklist wins when a name matches both lists.

## Restrict tools inside an allowed server

Server-level trust is usually too coarse. A server can contain ten read-only
tools and one destructive tool. Blocking the entire server frustrates people;
allowing every tool grants more power than the job needs.

Set a verdict at the tool level:

```sh
agentjail mcp tool allow github list_repos
agentjail mcp tool ask github create_repo
agentjail mcp tool block github delete_repo
```

Or describe it in policy:

```yaml
mcp:
  allowed:
    - github
  servers:
    github:
      blocked_tools:
        - delete_repo
        - transfer_repo
      ask_tools:
        - create_repo
```

A good default is simple:

- Allow read-only discovery tools.
- Ask before writes with a meaningful external effect.
- Block destructive, administrative, billing, and credential operations unless
  there is a specific reason to expose them.

## Limit the credential behind the server

Policy around the tool call is one layer. The server's own credential is
another.

If an MCP server only needs to read issues, give it a token that can read
issues. Do not give it an organization-owner token and rely on the agent to use
it politely. The same rule applies to database users, cloud roles, Slack apps,
and deployment credentials.

Prefer:

- short-lived credentials over standing tokens;
- project or repository scope over organization scope;
- read-only access when writes are not required;
- staging accounts for testing;
- separate identities for agents and people.

If a policy layer fails, the credential should still limit the damage.

## Account for prompt injection

Prompt injection is not limited to chat messages. An agent reads untrusted text
from repositories, issues, package documentation, web pages, and MCP tool
results. Any of that text can contain instructions.

Do not use the model's judgment as the only authorization check. Put a
deterministic decision between the model and the tool:

1. The agent proposes an MCP call.
2. agentjail identifies the server and tool.
3. Local policy returns `allow`, `ask`, or `deny`.
4. Only an allowed or explicitly approved call proceeds.

This does not make prompt injection disappear. It limits which actions an
injected instruction can turn into reality.

## Review network access

A local MCP server can still make outbound requests. Document the hosts it
needs, then compare that list with what the process can actually reach.

For agents launched through the transparent tunnel, agentjail can enforce an
outbound hostname allowlist:

```yaml
network:
  allowed_hosts:
    - api.github.com
    - "*.your-company.example"
```

Network controls should complement tool policy. Tool policy understands the
requested action. Network policy limits where the resulting process can talk.

## MCP security checklist

Before approving a server for a team:

1. Record its source, version, and owner.
2. List every tool it exposes.
3. Identify the credential and permissions behind each sensitive tool.
4. Allow read-only tools first.
5. Put external writes behind `ask`.
6. Block destructive and administrative tools by default.
7. Restrict filesystem and network access around the server process.
8. Test one allowed call and one denied call.
9. Keep decision logs long enough to investigate an incident.
10. Repeat the review when the server or its permissions change.

The goal is not to make MCP unpleasant to use. It is to stop one convenient
integration from quietly becoming a universal admin interface.

## Next steps

- [Default MCP policy](/docs/reference/default-policies#mcp_policy): shipped
  rules, inventory behavior, and configuration examples.
- [Policy model](/docs/concepts/policy-model): how multiple rules resolve to one
  decision.
