MCP security best practices for teams
Nine practices that keep a team safe when AI assistants connect to its tools over MCP, drawn from the MCP specification and OWASP guidance, and what each looks like in practice.
8 min read
The MCP security best practices that matter most for a team are these: connect only servers you trust and keep a list of them; sign in with OAuth rather than pasting keys; give each connection the fewest scopes that do the job; give every person their own token and never share one; read tool descriptions before you approve them; keep a human in the loop for anything destructive; make sure the server keeps an attributed log; and know how to revoke in one step. Each one below comes with where it is written down, and what it looks like on an ordinary Tuesday.
This piece is about what to do. For the other half, what can go wrong and why, see MCP security risks. If you are new to the protocol itself, start with what MCP is.
1. Connect only servers you trust, and write the list down
An MCP server tells your assistant what tools exist and what they do, and your assistant believes it. So the first control is the simplest: decide which servers your team uses, and do not add others casually. Prefer the server published by the vendor of the tool itself over a community copy with a similar name. Claude Code’s own documentation (opens in a new tab) is direct about this: verify you trust each server before connecting it, and note that servers which fetch external content can expose you to prompt injection. Its managed settings let an administrator set an allowlist and a denylist of servers, and where both match, the denylist wins.
For a team without an administrator, the list can be a note in the repository. Servers configured for a whole project in Claude Code live in .mcp.json and go through version control, and Claude Code asks for approval before using a project-scoped server from that file. That puts a new server in front of a reviewer rather than on one person’s laptop.
2. Prefer OAuth sign-in over pasted keys
The MCP authorization specification (opens in a new tab) builds remote sign-in on OAuth 2.1, with PKCE required of clients, protected resource metadata so a client can discover where to sign in, and a way for the client to identify itself without anyone copying a client id: a Client ID Metadata Document, which the July 2026 revision recommends, or dynamic client registration, which that revision keeps for compatibility but deprecates. The practical result is that a person signs in through a browser, approves, and the assistant is handed a token. Nothing is pasted into a config file, a dotfile, a chat or shell history.
Keep pasted tokens for what genuinely cannot open a browser, such as a scheduled job. When you do issue one, store it where your other secrets live, never in a committed file, and give it its own name so it can be revoked on its own.
3. Grant the fewest scopes that do the job
The specification’s security guidance has a section on scope minimisation. Its advice is to start with a minimal set of low-risk read operations and raise it only when a privileged operation is first needed, and it lists wildcard or omnibus scopes and bundling unrelated privileges “to preempt future prompts” as common mistakes. OWASP’s Excessive Agency (opens in a new tab) (LLM03 in the 2026 list, LLM06 in 2025) makes the same point from the other side: much of the damage an agent can do comes from permissions it never needed.
In practice: connect a new assistant read-only for its first week. Let it report what it would do. Widen the scopes when its reports are ones you would have acted on. The cost is a few days; the saving is every mistake it would have made with write access in that time.
4. One token per person, per tool
A shared token is a shared identity. Every change made with it looks the same in the log, and revoking it stops everyone at once. Give each person their own connection for each assistant they use, named for what it is. The specification makes a related rule for server builders: an MCP server must not accept any token that was not explicitly issued for it, and must not pass a client’s token through to another service. Part of the reason given is accountability. If a server cannot tell clients apart, nobody can investigate what happened later.
5. Read tool descriptions, and notice when they change
The descriptions a server gives its tools are read by the model as instructions. The specification (opens in a new tab) says clients must treat tool annotations as untrusted unless they come from trusted servers. Before approving a new server, read its tool list the way you would read a new dependency’s install script: is there anything in a description that is not about what the tool does? For servers you run locally, pin the version you reviewed rather than always pulling the latest, and re-read the list when you upgrade. A description that changes after you approved it is one of the risks set out in the companion piece.
6. Keep a human in the loop for destructive tools
The MCP tools specification says there should always be a human in the loop with the ability to deny tool invocations, and that clients should prompt for confirmation on sensitive operations and show tool inputs before calling the server. OWASP’s guidance on prompt injection says the same: human approval for privileged operations. Most clients let you choose which tools run without asking. Leave deletes, sends and anything that moves money on “ask”. Claude Code’s organisation settings can force a tool to prompt on every call, even in modes that otherwise skip prompts.
The server can help too. A tool that makes a soft delete, which keeps the record and can be undone, is safer to hand to an assistant than one that destroys data outright.
7. Make sure the server keeps an attributed log
The specification asks clients to log tool usage for audit purposes, and OWASP’s MCP Top 10 (opens in a new tab) lists “Lack of Audit and Telemetry” as MCP08:2025. A client-side log helps, but it lives on one machine. What a team needs is the server’s own record of every change, naming both the assistant and the person it acted for. Then read it. A monthly routine for that is in how to audit AI agents.
8. Know how to revoke, before you need to
Revoking should be one step, take effect at once, and not sign the person out of anything else. Check this when you connect a server, not on the day a laptop goes missing. There are two sides to it. On the client, Claude Code can clear a server’s authentication from the /mcp panel or with a command:
claude mcp logout fenbs # clear this machine's sign-in for the server claude mcp remove fenbs # remove the server and its stored tokens
On the server, find where connections are listed and revoked. That is the side that matters when the device is not in your hands.
9. If you build a server, follow the MUSTs
Teams increasingly write their own MCP servers for internal tools. The security best practices page (opens in a new tab) in the specification is short and worth reading whole. The points most often missed:
- Validate that every token was issued for your server, and never forward a client’s token to another API. Use a separate token of your own for upstream calls.
- If your server fronts a third-party API with a single static client id, you must obtain the user’s consent for each dynamically registered client before forwarding to the third party. Skipping that is the confused deputy problem the specification describes.
- Verify every inbound request, and never treat a session id, or any handle returned by a tool, as proof of who is calling.
- On a consent page, name the requesting client, show the scopes and where the token will be sent, and prevent the page being framed.
- Match redirect URIs exactly against the registered value; no patterns, no wildcards.
A worked example: how fenbs applies these
fenbs is a task board and is itself an MCP server, so it is a useful place to see the practices in one product. Connecting is a browser sign-in that follows the MCP authorization specification: OAuth 2.1 with PKCE, protected resource metadata, dynamic client registration and the issuer returned on every sign-in response, public clients only. The assistant never holds a long-lived key: its access token lasts an hour and is renewed with a refresh token, and revoking the connection in Settings ends both.
- The approval page shows the name the client gave itself, marked “unverified” because anybody can register as “Claude”, where you will be sent back to, and the board it will work in. It cannot be framed, and fenbs never redirects to an address it has not verified against the client’s registration.
- Three scopes: read, write and comment. Read is always on; the other two are ticked by you. A token is also capped by your own role on the board, and the narrower of the two wins, checked on every call.
- One token per connection, listed by name in Settings, “Connect an AI assistant”, with its scopes and when it was last used. A token issued by hand for a script is shown once and cannot be read back.
- Every change is recorded in History as “Claude via” the person it acted for, written by the service rather than the client.
- Deleting a task is soft: its number, comments and links are kept, and it can be restored. Refusals are sentences naming the missing permission and the role, so an assistant can relay them rather than hunt for a workaround.
- Revoke is one click, stops the token at once, and leaves your own sign-in untouched. What the assistant did stays in History.
Next steps
Connect an assistant with the smallest scopes using the connection guide, or see the steps for Claude Code and Cursor. For the argument behind roles and scopes, read roles and permissions for humans and AI agents.