Read-only by Construction: An MCP Server That Can't Write to Redis

Learn how to build an MCP server that physically cannot write to Redis by omitting write tools and using read-only credentials. No prompt trickery needed.

miércoles, 29 de julio de 2026 • 5 min read • Q2BSTUDIO Team

Protege tu servidor MCP contra escrituras no deseadas

When we develop systems that integrate language models (LLMs) with operational data, the temptation is to rely on textual instructions. Telling the model 'read-only, do not modify' seems sufficient, but any engineer who has worked with AI agents knows that a sentence is just a suggestion. A model can ignore it, be manipulated by a prompt injection, or simply misinterpret the context. That is why at Q2BSTUDIO, when we tackle projects that connect artificial intelligence with real infrastructures, we apply a fundamental principle: security must be in the wiring, not in the prose.

This article explores how to build an MCP (Model Context Protocol) server that guarantees, by construction, that an LLM can only read data from Redis, with no possibility of writing, deleting, or modifying. It is a design lesson that combines custom software development with best practices in cybersecurity and cloud architecture.

The problem of trusting descriptions

Imagine a typical scenario: an AI agent needs to query a job queue in Redis to report its status. The first reaction is to add in the tool description 'read-only, do not modify'. But the model reads that sentence exactly like any other: as a string of characters it may or may not follow. If an attacker injects a malicious payload into a job, or if a complex reasoning chain leads the model to misinterpret the instruction, the textual restriction vanishes. Descriptions are documentation, not permission checks.

At Q2BSTUDIO, we have learned that when working with cloud services like AWS or Azure, security must scale from code to infrastructure. In the case of an MCP server, the control point is not in the tool description, but in two concrete places: the tool registration table and the credentials used by the handler.

Conditional tool registration

The MCP API allows the server to expose a set of tools. The client calls tools/list and receives names and input schemas. When the model wants to act, it sends tools/call with a name and arguments, and the server routes it to the registered handler. If the name is not registered, the response is an error: 'unknown tool'.

The key is that we only register read tools. For our queue inspector, we define four tools: list_queues, queue_stats, list_jobs, and get_job. Tools that mutate state, such as retry_job or delete_job, are only registered if the server is started without the --read-only flag. With that flag active, those two registration calls never execute, and the process advertises only the four read tools. Thus, even if the model invents or a malicious payload tries to call delete_job, the response is immediately an error. There is no handler to execute.

This approach is simple but robust. A reviewer can confirm that the write path is absent simply by looking at the conditional code, not by trusting a sentence. At Q2BSTUDIO we apply the same philosophy in our artificial intelligence and agent projects: security becomes an observable property of the architecture.

The second barrier: credentials that cannot write

Registering only read tools is a good first step, but handlers can have bugs, and a future developer might inadvertently add a tool that modifies data. That is why we add a second layer: the Redis credential. We create an ACL user with read-only permissions, and revoke dangerous commands even among reads, such as KEYS, which can scan the entire keyspace and stall a busy server.

The configuration is clear: ACL SETUSER inspector on >secret ~* -@all +@read -@dangerous. This grants the read command category, but -@dangerous removes the footguns. Redis classifies GETEX and GETDEL as writes, so +@read correctly excludes them. If a handler attempts to execute a disallowed command, Redis rejects it with a NOPERM error. The database itself becomes the backstop.

For an even stricter control, you can point the inspector at a read-only replica, which rejects any write with a READONLY error. This way, we stack three guarantees: the model only sees read tools in tools/list, an invented or injected write call hits no handler, and the handlers run on a credential that physically cannot write. None of these guarantees depends on the model behaving well.

What read-only does not buy you

It is important to be honest about the limits. Read-only mode ensures the model cannot modify your queue, but it does not mean it is safe to expose data. Tools like get_job return payloads that may contain tokens, emails, or internal IDs. A model with read access is an efficient way to leak that data to a third party. That is why at Q2BSTUDIO we always combine these architectures with cybersecurity services to audit and sanitize sensitive information before exposure.

Moreover, a read can be expensive. Listing all pending jobs in a large queue can spike production latency, and a model might call the tool in a loop while 'exploring'. Therefore, tools must include pagination, strict limits, and timeouts. Here, Business Intelligence also plays a role: monitoring usage and performance is key to avoid surprises.

Another risk is second-order injection. The data we read is not neutral; a job payload can contain text designed to influence the model. If that same model has access to other tools, reading our queue can affect its decisions in other contexts. Making our queue unwritable does nothing about that. It is a property of the model's entire tool set, not of a single server. And finally, this design makes a single server read-only by construction, but it says nothing about a host that also has a second, write-capable server loaded. The blast radius is per-connection, not global.

Conclusion: wiring over words

None of this is revolutionary. It is a disciplined application of basic software engineering principles: register only the tools the mode is meant to expose, keep the mutating ones behind a read-only switch so they never enter the table, and connect with a credential that cannot perform the operation you are preventing. When the caller is a language model, a careful 'read-only, please' is treated like any other string. That is why at Q2BSTUDIO we stop spending the boundary on sentences and put it in the wiring, where it holds whether the model cooperates or not.

For companies looking to integrate AI agents with their systems securely, this approach is a cornerstone. Whether in process automation projects, data analysis with Power BI, or cloud deployments, security by construction is the only reliable path. At Q2BSTUDIO, we help design and implement these architectures, combining custom software development with artificial intelligence, cybersecurity, and cloud.

A BREAK?

Play for a moment before you go

OUR SERVICES

How we can help you

Do you have a project in mind?

Tell us your vision and we'll turn it into a software solution. Whatever the scope, we make your idea real.