AuthZ for MCP servers: how to block, allow, and rate-limit MCP tools
MCP servers hand powerful capabilities to AI agents — file access, shell execution, database queries, HTTP calls. Authorization is how you decide which of those an agent is actually allowed to invoke, and how often.
Why MCP authorization matters
Most MCP servers are binary: either the agent can call every tool the server exposes, or it can't connect at all. That's fine for a read-only docs server. It's not fine for a filesystem server with a write_file tool, a shell-exec server with a run_command tool, or a database server that can drop tables.
A few concrete scenarios where authz matters:
- You run a filesystem MCP server scoped to
/tmp, but you'd rather the AI never calldelete_file. - A web-search MCP server has a generous API quota, but you want to cap the agent at 10 calls per minute so a runaway agent loop doesn't drain it.
- A database MCP server exposes both read and write tools, but for most sessions you only want reads.
- You're evaluating a brand-new MCP server and want to allow specific tools one at a time rather than opening the floodgates.
Authentication vs authorization in MCP
Two related concepts that often get conflated:
- Authentication (AuthN) answers "who is calling this tool?" The MCP specification defines an optional OAuth 2.1-based authorization flow for HTTP transports — as of the 2026-07-28 revision it's a full part of the spec, not a draft appendix, and it expects servers to publish OAuth 2.0 Protected Resource Metadata. Local stdio servers are explicitly told not to follow it and to take credentials from their environment instead, because the process itself is the identity boundary.
- Authorization (AuthZ) answers "is this caller allowed to call this specific tool with these arguments, right now?" This is what you almost always actually need, and what this guide covers.
In a local-first workflow, where every MCP server runs on your machine, the interesting unit of authz is the tool, not the user. You know who's calling (it's you, or an agent you started). The question is which tools you want that agent to have.
Two enforcement models
Blocklist mode
Default-allow, with specific tools denied. Most permissive. Best when you mostly trust a server but want to rule out a few dangerous calls. Example: allow everything from a filesystem MCP server except delete_file and move_file.
Allowlist mode
Default-deny, with specific tools enabled. Most restrictive. Best when you're auditing a new server or when you only need one or two tools from a larger surface. Example: from a GitHub MCP server that exposes 20 tools, only allow search_repositories and get_file_contents.
Both modes are per-server. You can run one server in allowlist mode and another in blocklist mode in the same hub.
Rate limiting
Independently of block/allow, you can cap how many times a specific tool is called per minute. Rate limiting matters for two reasons:
- Cost control. Some MCP servers proxy paid APIs. An agent in a loop can burn through credit quickly.
- Blast-radius containment. If an AI agent gets into a weird state and starts hammering a tool, the rate limit gives you a ceiling on damage.
When a rate limit trips, the tool returns an error to the agent. Most agents will back off and try a different approach instead of spinning on the failure.
AuthZ in AppContext's MCP Hub
AppContext's MCP Hub is a natural place to enforce authz because every tool call from every connected AI client routes through it. AppContext stores policies in ~/.appcontext/policies.json and evaluates them in-process before forwarding calls to upstream servers.
Per-server mode + per-tool toggles
In the Servers tab of the app window, each server has a mode toggle (blocklist / allowlist) and a list of its tools. Click a tool name to flip its authorization. Blocked tools return a structured POLICY_BLOCKED error to the AI client.
Per-tool rate limits
Each tool can be assigned a max-calls-per-minute limit. When the limit is exceeded, the tool returns a rate-limit error. The limit resets on a rolling 60-second window.
Readable and writable from the agent side
Policies aren't only a UI concept. AppContext exposes a hub_policies tool over MCP with a list action that returns each server's mode, its per-tool state, and its rate limit, plus block_tool, unblock_tool, and set_rate_limit actions. That means you can ask a connected agent "which tools are currently blocked?" and get an answer without opening the app window — and the agent's own attempts to widen its permissions are subject to the same licensing checks as the UI.
Persisted and portable
Policies live in a JSON file at ~/.appcontext/policies.json. They survive app restarts and can be committed to a dotfiles repo if you want to share authz configuration between machines.
If you enforce at the server, you have to configure it N times for N servers — and not every server supports authz anyway. Enforcing at the hub gives you one consistent policy surface across every MCP server you use, whether or not the upstream supports authz natively.
A worked example
Suppose you've added three upstream MCP servers to your hub:
- filesystem — 8 tools including
read_file,write_file,delete_file - github — 20 tools covering repos, issues, PRs, actions
- postgres —
query,list_tables,describe_table
A sensible default policy for a coding session might be:
- filesystem: blocklist mode, deny
delete_fileandmove_file. - github: allowlist mode, allow
search_repositories,get_file_contents,list_pull_requests,get_pull_request. - postgres: allowlist mode, allow
list_tablesanddescribe_table— notquery(you can toggle that on for sessions where you need it).
Set a rate limit of 10 calls/minute on anything that hits a remote API, so an agent loop can't drain quota.
What's coming next
The current policy engine is per-tool and per-server. On the roadmap:
- Argument-level policies — allow
write_fileonly under/tmp, not under~/Documents. - Prompt-triggered policy changes — "just for this session, allow
postgres:query." - Audit log — every tool call, every block, every rate-limit hit, visible in an activity feed.
If any of those would be valuable to your workflow, we'd love to hear about it — contact@appcontext.dev.
Frequently asked questions
What's the difference between authentication and authorization in MCP?
Authentication answers who is calling a tool. The MCP specification defines an optional OAuth 2.1-based authorization flow for HTTP transports, while stdio servers are expected to take credentials from their environment instead. Authorization answers whether a caller may invoke a specific tool right now — in a local-first setup, that per-tool decision is usually the one you actually need.
Should I use blocklist or allowlist mode?
Blocklist mode is default-allow with specific tools denied — use it when you broadly trust a server but want to rule out a few destructive calls. Allowlist mode is default-deny — use it when auditing a new server or when you only need one or two tools from a large surface. Modes are set per server, so you can mix them.
Where are the policies stored?
In ~/.appcontext/policies.json. Policies survive app restarts and can be committed to a dotfiles repo to share authorization configuration between machines.
Control what your AI can do
Download AppContext and set authz policies across every MCP server you use.
Get AppContext Free