The Context7 MCP server control model, explained
Context7 is Upstash’s documentation server — ~61k stars, ~554k weekly npm downloads of @upstash/context7-mcp (registry figures, week of August 13, 2026), and default advice in half the “fix your coding agent’s stale docs” threads on the internet. It does one thing: resolve-library-id turns a library name into an ID, query-docs fetches current documentation for it, and the result lands in your agent’s context. This page is the reference — and it’s the inverse of every other page in this series. Everywhere else we ask what the agent can do through the server. Here the answer is nothing, and the question flips: what can the server do to the agent? Sources: the official README and the adding-libraries docs.
This page covers the server’s own controls. For the cross-server picture, see the MCP server controls comparison; for what a control layer adds on top, the coverage matrix.
What’s at stake on this surface
Nothing to do, everything to say. The tool surface can’t merge a PR, submit a form, or touch a file — but its output is injected into the context of an agent that usually can, and it arrives with the strongest trust framing content can have: documentation the agent asked for. An agent that would rightly treat a random web page as untrusted will pattern-match a Context7 result as ground truth about how to write the next piece of code. A server with no write tools can still steer every write the agent makes elsewhere. That’s the entire risk budget of this page, and it’s not hypothetical — see the ContextCrush section below.
The read-only design: credit where due
Two tools, both reads, no configuration needed to keep it that way. This is the only server in the series where the write column is empty by design rather than by flag — no --read-only to remember, no toolset to prune, no capability the agent can talk its way into. As a control posture for the outbound direction, that’s as good as it gets: the blast radius of “agent misuses this server” is bounded at wasted tokens. The GitHub server needs three spellings of read-only to reach the posture Context7 ships as its whole product. Grade that honestly: it’s a legitimate design decision, not an accident, and it’s why this page is short.
Auth and rate limits: a usage tier, not a scope
The server runs without any credential; a free API key from context7.com/dashboard raises rate limits (npx ctx7 setup provisions one via OAuth; manual configs pass Authorization: Bearer). Upstash doesn’t publish the numeric tiers in the README, so we won’t quote any. What matters for this series is the shape: the key gates how much, not what. There are no scopes because there’s nothing to scope. The remote endpoint is https://mcp.context7.com/mcp; note that the MCP server is open source but the backend — parsing engine, crawling engine, the index itself — is private infrastructure. You’re trusting a hosted pipeline you can’t inspect, on both the local and remote install paths.
The content-trust question: the centerpiece
Where do the docs come from? Upstash’s adding-libraries docs answer plainly: anyone can add a public library — you don’t need to own it. Paste a GitHub URL into the web form, Context7 parses the markdown (generating examples from source where docs are thin), and the library goes live under an /org/project ID. No review step is described between submission and indexing. Maintainers can verify ownership afterward via a “Claim Your Library” flow — it unlocks an admin panel and version management — but claiming is optional, and nothing in the tool output marks a claimed library apart from an unclaimed one.
The README’s own disclaimer draws the line for us: projects are community-contributed, Upstash “cannot guarantee the accuracy, completeness, or security” of the docs, use is at your own risk, and there’s a Report button for suspicious content. That’s honest, and it’s also the whole native trust model: open submission in, best-effort reporting out.
The structural risk got a proof of concept. In March 2026, Noma Labs disclosed ContextCrush: Context7’s Custom Rules feature let library maintainers include instructions for AI assistants, and those instructions were passed through unvalidated — so an attacker could register a fake library, embed malicious instructions in its rules, and wait. Because the payload arrives from a server the agent trusts, the assistant “may treat these commands as valid and execute them” — the demonstrated impact class included file deletion and data exfiltration to an attacker-controlled repo. No real-world exploitation was reported, and Upstash patched with rule filtering and additional protections. Read the patch precisely: it closed one unvalidated channel. The submission model that made the attack cheap — register a fake library, no review, no ownership required — is unchanged, because it’s the product. Two adjacent surfaces remain structural: a plausibly-named squatted library sitting next to the real one, and resolve-library-id itself, where a fuzzy name-to-ID match is the step that decides which community-submitted corpus becomes the agent’s ground truth.
What no control here can see
Suppose Context7 shipped every control this series grades for — it still couldn’t cover its own risk, because the risky call never happens on this server. The sequence is: query-docs returns content, the content lands in context, and three tool calls later the agent runs a shell command or pushes a file through the GitHub server shaped by what it read. Context7’s part is finished before anything dangerous occurs, and the server where the write lands has no idea the input was tainted — each server sees only its own slice. This is the same cross-server blindness every page in this series ends on, viewed from the source side: Playwright is where untrusted content and the action surface share one server; Context7 is untrusted content with no action surface, relying on some other server to supply one. It always finds one.
Where the native model ends
The boundary is unusually easy to state, because the native model is small and honestly labeled:
- The outbound direction is solved by construction. No write tools, nothing to configure, nothing for an agent to escalate. Best-in-series, full stop.
- The inbound direction has no per-call control. Nothing distinguishes a claimed library from a squatted one at query time, no verification mark travels with the content, and the trust decision — “treat this as documentation” — is made implicitly by the model, not explicitly by the operator.
- The trust model is open submission plus a Report button. Post-hoc, best-effort, and disclaimed as such by Upstash itself.
- The consequence is always out of frame. The action a poisoned doc steers happens on another server, after this one’s turn is over.
The practical posture: use it — the docs-freshness problem is real and the read-only design means the server itself costs you nothing in blast radius. Prefer explicit library IDs over name resolution where you can pin them, treat resolved IDs for unfamiliar names with the suspicion you’d give a typosquatted package, and remember that “documentation” is a framing, not a property — everything Context7 returns is community-submitted content wearing that framing. Then notice where the actual exposure lives: in the calls your agent makes after reading, on servers that never saw the source. A control that can hold those calls has to see the sequence — reads on one server, writes on another — which is exactly the vantage point a layer standing across all of them has and no single server can.