What you’ll get from this article
If you ship software or AI products, every MCP server you expose is a decision about trust. This piece walks through what read-only and read-write scope declarations actually mean on the wire, how clients surface those scopes during connection, and a short decision method you can use today to pick the narrowest scope a workflow truly requires.
Why scopes matter more than the feature list
The Model Context Protocol (MCP) is the open standard that lets AI applications connect to external tools and data through JSON-RPC 2.0 messages. When you read about MCP, the conversation usually focuses on what a server can do: read files, query a database, post a message, send an email. The quieter decision — and the one that determines whether your product is safe to ship — is what a server is allowed to do once a client connects.
Scope is the answer to that question. It is the contract between a server and the host application that says, “these are the capabilities I am offering, and nothing more.” The MCP specification treats capability negotiation as the moment where that contract is formed. Get it wrong in the generous direction and a single rogue prompt can escalate into data loss. Get it wrong in the strict direction and your agent cannot finish the task your customer hired it for.
For a solo founder shipping an MCP server to paying customers, the scope line is also a product line. “We will only ever ask for read access to your repository” is a sentence that closes deals. “Our agent will write to your production database” is the sentence that loses them.
The minimum you need to know about MCP roles
MCP separates three roles so that authorization has somewhere to live:
- Host is the AI application — the chat client, the IDE plugin, the custom agent your team built. It holds the user’s prompt, the conversation history, and the policy decisions.
- Client is the connector the host creates per server. Each client manages one stateful session and keeps other servers isolated from it.
- Server is the process that exposes resources, prompts, and tools.
This isolation matters for scope. A host running three MCP servers has three separate client instances, and each one negotiates its own capabilities independently. If one server declares write access and another does not, the host can see the difference and route accordingly. Servers only see what the host chooses to pass through, which is the architectural reason least-privilege design is even possible.
What “scope” actually means in MCP
Scope is not a single global flag. It is the combination of three things the server declares during the initialization handshake, plus what the host chooses to grant:
- Which primitives the server offers. Tools are functions the model can call. Resources are data the model can read. Prompts are templated messages.
- What each tool is annotated to do. The server’s tool description tells the host whether a tool reads, writes, deletes, or triggers external side effects. There is no machine-checked “read-only” bit at the tool level in the core spec; the honesty is in the declaration and the host’s interpretation.
- Which features the client opts into. Servers can also request server-initiated features like sampling, roots, or elicitation. Each one is a new permission surface, and each one must be declared and accepted.
When a client connects, it sends an initialize request that includes the client’s capabilities — the features the client is willing to support. The server replies with its own capabilities — the primitives it exposes and the limits it will respect. The resulting session is the union of what both sides declared. Anything not declared is not available, even if the underlying API supports it.
This is the practical lever for an indie developer. You do not control the model and you do not control the host. You control the server’s declaration. The narrowest honest declaration is the scope you ship.
Read-only versus read-write, in plain language
A read-only MCP server exposes resources and read-oriented tools. “Read this file,” “list these records,” “summarize that document.” The server’s tool annotations do not include writes, deletes, or state-changing calls. The host can still pass the model a prompt that asks for a write — the server is the gate, not the prompt — but a well-built read-only server will refuse the call because the capability is not declared.
A read-write MCP server adds tools that mutate state: creating records, updating settings, sending messages, posting comments, deleting files. The same authorization model applies, but the blast radius of a misrouted prompt is larger.
The trap is that “read-only” is a promise with two layers. The first layer is the tool list and its annotations. The second layer is the underlying API the server wraps. If your “read-only” MCP server wraps a database client whose connection string happens to have write privileges, the protocol layer is not what saves you. Scoping is a design discipline, not a config flag.
How scope shows up during connection
From a founder’s perspective, the negotiation is mostly invisible — and that is part of the problem. The user clicks “connect” in their host application, the client and server exchange capabilities, and the user sees a list of tools appear. Whether the server is asking for read or write access is encoded in that list and its descriptions, not in a clear “this server will modify your data” banner.
Some hosts surface authorization prompts at the transport layer. When MCP is used over Streamable HTTP rather than stdio, the connection can carry an OAuth-style authorization flow that asks the user to grant specific delegated permissions. The 2025-06-18 and later specifications explicitly carry authorization metadata, so a remote server can request scoped tokens instead of full access. Whether your host application surfaces that prompt clearly to the user is still a UX decision the host team makes.
This is why the server author’s job is to be boring and explicit. Name tools after verbs that match their effect. “read_file,” “list_invoices,” “create_draft,” “send_email.” Annotate each one with the side effect it has. If a tool name could plausibly be read or write, the user cannot tell from the list alone.
A four-step method for picking the narrowest scope
Use this when you sit down to design a new MCP server or to audit one you have already shipped.
Step 1: Write the user story in one sentence. “The agent reads the latest invoice for account X and summarizes it.” That sentence has zero writes. Your scope is read-only until the sentence changes.
Step 2: List every tool the story needs and tag it. For each tool, write R (read), W (write), D (delete), or E (external side effect such as sending an email). If a tool can be implemented read-only, ship it read-only. Splitting one “manage documents” tool into “read_document” and “update_document” is usually worth the duplication.
Step 3: Strip the server-initiated features you do not need. Sampling, roots, and elicitation each add a new permission surface. If your workflow does not require the server to ask the user follow-up questions, do not declare elicitation. If you do not need the server to invoke the model recursively, do not declare sampling. The default is off.
Step 4: Write the refusal path before you ship. Decide which calls the server will reject, what error code it returns, and how the host should display that error. A scoped server that fails closed is a safer product than a generous server that succeeds when it should not.
Common mistakes indie developers make
- One server for everything. Wrapping your whole product as a single MCP server with thirty tools makes scope negotiation useless. Split by domain. A read-only “support knowledge” server and a write-capable “ticket actions” server are easier to reason about, easier to document, and easier for customers to approve.
- Declaring capabilities “just in case.” If you declare sampling but never use it, a security reviewer will (rightly) assume the worst. Declare only what the current version of the code actually does.
- Hiding writes behind innocuous tool names. “refresh_cache” that drops a table is not a read. Tool names are part of your security model.
- Skipping the audit when the model changes. Foundation models update. Behavior that looked read-only last quarter can drift. Re-read your own tool list every time you upgrade the underlying model your host uses.
A short FAQ
Do I need OAuth to ship a read-only MCP server? No. For local stdio servers, scope is enforced by the server’s own declarations and the host’s policy. OAuth-style authorization becomes relevant when you expose a remote server over Streamable HTTP and want to issue scoped delegated tokens.
Can a host override my server’s scope? The host can refuse to connect, accept the connection, or surface a prompt to the user. It cannot silently promote a read-only tool into a write. The protocol’s honesty is a two-way street.
How do I tell a customer what scope my server uses? Publish the tool list with effect tags, state the declared capabilities in plain English on your docs page, and link to the exact spec version you implemented. Customers buying AI agents are reading those pages more carefully than they read anything else you write.
The next decision you actually have to make
Pick the one workflow you are most comfortable promising as read-only. Build that server first, with the narrowest declaration that still completes the job. Ship it, watch what real users ask it to do, and only then add a second server for the write-capable workflow. Two small honest servers will outperform one generous one every time, both in security reviews and in the trust you build with the people paying you.
Sources
- https://modelcontextprotocol.io/specification/2025-06-18
- https://arxiv.org/html/2505.02279v2
- https://docs.stacklok.com/toolhive/concepts/mcp-primer
- https://www.ietf.org/archive/id/draft-zeng-mcp-troubleshooting-00.html
- https://medium.com/@jamesaspinwall/mcp-tools-resources-and-client-server-interaction-explained-0b6be41287c5
- https://stackoverflow.blog/2026/01/21/is-that-allowed-authentication-and-authorization-in-model-context-protocol
- https://www.descope.com/blog/post/mcp-auth-spec







