Blog/

Advanced

10 min read

MCP Security — What Nobody Tells You Before You Deploy

Most MCP deployment guides skip the security conversation. After spending a few weekends reading MCP server source code, here's what I found.

LL

Lee Li

Independent Developer · MCP Enthusiast

·

Not every MCP server is obviously unsafe. That's the annoying part.

I wasn't planning to write about security. I was reading source code on a Saturday because I wanted to figure out which tools on mcp-find.org deserved to be surfaced more prominently on the homepage. I'd been going through repositories all afternoon, skimming implementations, checking README files, the usual.

After the fourth repo in a row where something felt off, I closed my laptop and went to make tea. Came back. Opened a fresh tab. Kept going.

The thing that finally made me start taking notes was a server that accepted user-provided strings and fed them into a shell invocation with almost no guardrails.

I want to be precise here because it wasn't the cartoonish version where someone literally writes os.system(user_input). It was more subtle than that. The tool took a filename argument from the model, did some light sanitisation, and then passed it into a subprocess call that could be coerced into doing more than the README described. The line between "convenient automation" and "why is this even allowed" was way too thin for my comfort.

I copied the relevant function into a scratch file and stared at it for a while. Went back to the README. Nothing about trust boundaries. Nothing about what happens if the model sends something unexpected. The docs just said, "pass a filename, and the tool will process it." Cool. What if the model passes ../../etc/passwd? What if it passes a string with semicolons in it? The code didn't really address either case.

Great.

The repo-hopping mistake

About 40 minutes into this, I made the mistake of opening ten repos at once. Don't do that. Your brain starts blending one codebase into another. You can't remember which server hardcoded a config path and which stored API keys in a JSON file sitting right there in the repo root. I actually had to go back and re-read three of them because I'd mixed up my mental notes.

That's when I started keeping a checklist. Dead simple, just bullet points in a text file:

  • Does it validate inputs
  • Does it execute shell commands, and if so, how?
  • How many filesystems can it reach
  • Where do secrets go
  • Does it make network calls that the user might not expect
  • Is there any permission model at all, or is it just "the tool can do whatever"
  • Looking at that list now, it feels obvious. It was not obvious when I started. I was checking whether tools worked, not whether they were safe. That's embarrassing to admit, but it's true.

    "Local" does not mean "safe"

    This one keeps coming up, and it drives me a little crazy.

    Most MCP servers use stdio transport, which means they run as a local subprocess on your machine: no network server, no open port, no remote access. And because of that, many server authors seem less constrained about what the tool can touch. The reasoning is something like: it's already on your machine, so what's the harm?

    The harm is that the AI model is now in the loop. You didn't just install a local utility that you control. You installed a tool that a language model can invoke with whatever arguments it decides are appropriate. And language models are creative. Not maliciously, usually, but they'll try things you didn't anticipate.

    The classic version of this is filesystem access. A lot of demo servers grant broad filesystem read/write because the demo works better that way. "Look, Claude can read any file on my desktop!" Yeah. It can. Including the ones with credentials. Including your SSH keys if the path is wide enough. The person who built the demo understood the scope. The person who copies the config from the README might not.

    I saw one server where the suggested config literally used /Users/yourname as the root path. Your entire home directory. For a demo. The author probably tested it on a clean VM and didn't think twice about it. But that config snippet is going to get copied into production machines by people who don't read past the first code block.

    The credential mess

    At one point, I found myself muttering to myself. Out loud. In my apartment. Because a project had environment-variable instructions in one section of the README, example config with hardcoded keys in another section, and a third section that mentioned a .env file but didn't explain which values should go where.

    You know that feeling when documentation isn't technically wrong, it's just arranged in the most inconvenient possible order? Where you read all of it and still aren't sure what the recommended setup is? That.

    The MCP spec doesn't really address credential management. Like, at all. There's no standard for how a server should receive secrets. Some servers expect environment variables. Some read from the host config file (which means your API keys are in plaintext in claude_desktop_config.json). Some have their own config files. A few actually support proper secret stores or mention that you should use one.

    Most don't.

    I checked 30 servers while building mcp-find.org, and fewer than 5 had clear, secure credential handling. The rest were variations of "put your API key here," with a code block showing the key inline.

    Input validation, or the lack thereof

    This one I genuinely did not expect to be as bad as it is.

    I expected rough edges in early-stage projects. I didn't expect so many servers to... trust whatever the model sends them. No type checking beyond what the schema provides. No length limits. No sanitisation of strings that end up in queries or commands.

    I should be fair here. A lot of these projects are maintained by a single person who built them to scratch their own itch. They're not security engineers. They're developers who wanted Claude to talk to their Postgres database or search their notes app. The intent is fine. But the result is that tools which can hit a database, execute browser actions, write files, or proxy requests to external APIs are being distributed with "the model probably won't send anything weird" as the implicit security strategy.

    That's not a security strategy. That's hope.

    One server I looked at had a tool that ran SQL queries. The README showed it doing simple SELECT statements. The code had no query restrictions. DROP TABLE would have worked. I'm sure the model wouldn't normally try that, but "normally" is doing a lot of load-bearing work in that sentence.

    What actually changed how I evaluate servers

    After a few weekends of this, I noticed my standards had shifted. When I first started building the tool directory, I mostly cared whether a server worked. Does it install? Does Claude see it? Does the tool return something useful? If yes to all three, it got listed.

    Now I have an extra filter. I ask myself: Can I explain what this server has access to in one sentence? If I can't, I don't trust it yet. That's not a formal security framework. It's more like a gut check that's saved me from recommending several sketchy tools.

    The things that actually changed my behaviour were pretty simple:

    Read the code if the tool touches any sensitive data. README is marketing. The implementation is true. I found one repo that looked sketchy based on the docs, but had surprisingly solid guardrails in the actual code. Found another where the opposite was true. The README was professional, and the code was terrifying.

    Reduce scope whenever possible. If a tool only needs to read files from a single directory, don't give it your home directory. If it only needs SELECT access to your database, don't hand it a connection string with full privileges. This is basic stuff, but I keep seeing configs in the wild that grant maximum access because it was easier during setup.

    Assume the model will eventually hit the weird edge case. Not because the model is malicious. Language models are unpredictable, and they'll eventually try something you didn't test for. If your server's response to an unexpected input is "try the operation anyway and see what happens," that's a problem.

    Don't give production credentials to a repo you looked at for three minutes. This should be obvious, but I've done it. Not proud of it.

    The spec gap

    Here's where I'm going to say something that might be controversial: the MCP specification itself doesn't say enough about security.

    There's no standard auth mechanism. Every server handles authentication differently, or doesn't handle it at all. There's no standard permission model. Nothing in the spec tells you how to restrict what a tool can access. There's no guidance on input validation patterns, credential storage, or trust boundaries between the host, client, and server.

    That may not be the spec's job. The spec could be supposed to define the communication protocol and leave security to the implementation. I've gone back and forth on this. Part of me thinks a protocol spec should outline the threat model. The other part of me recognises that HTTP didn't ship with a security framework either, and we figured it out eventually with TLS, OAuth, and everything else.

    But HTTP also had decades. MCP is moving fast, and people are deploying servers into production environments right now, today, with tools that can access internal databases and company documents. "We'll figure out security later" is not great when "later" is already here.

    What I'd tell someone deploying MCP at work

    If you're setting up MCP servers for personal use on your own machine, the risk is manageable. You're the only user, you control the scope, and if something goes wrong, you're the only person affected.

    If you're deploying MCP servers in a team or company environment, that's different. Here's what I'd want to know before I signed off on it:

  • What exactly can each server access? Not what the README says. What the code actually does. If no one on your team has read the server's source code, you're trusting a stranger's judgment about what's safe.
  • How are credentials managed? If API keys are in plaintext config files, that's a problem. If they're in environment variables on a shared machine, that's a problem too. Figure out the credential story before you deploy.
  • What happens when the model sends something unexpected? Does the server validate inputs? Does it fail safely? Or does it just try the operation and hope for the best?
  • Is there any logging? If something goes wrong, can you figure out what happened? A surprising number of servers have zero logging. You won't know something bad happened until the consequences show up.
  • I don't have neat answers to all of these. The tooling for enterprise MCP deployment is still basically nonexistent. People are figuring it out as they go. That's fine for experimentation. It's less fine for anything touching production data.

    The part where I contradict myself

    I should be honest about something. Despite everything I just wrote, I still use MCP servers daily. I still recommend them on mcp-find.org. MCP is still one of the most interesting things happening in the AI tooling space right now.

    The security situation isn't good. But it's not uniquely bad either. Plenty of old-school integrations, browser extensions, npm packages, and API wrappers have the same problems. The difference with MCP is that it makes it very easy to hand tool access to a model layer before you've forced yourself to think about the boundary.

    The friction is so low that you can go from "install this" to "Claude can now read all your files" in about 2 minutes. That speed is both the appeal and the risk.

    People are still in the excitement phase. The careful phase comes later. I hope "later" arrives before something really bad happens in a production deployment that makes the news and scares everyone off the whole ecosystem.

    If you've done a security review of an MCP server and found something interesting, good or bad, I'd like to hear about it. lee@mcp-find.org. I won't name you or the project unless you want me to.

    LL

    Lee Li

    Independent Developer · MCP Enthusiast

    Building and breaking things with AI tools since 2023. MCP Find started as a personal project to track the rapidly evolving MCP ecosystem. Based in Hong Kong.

    info@mcp-find.org📍 Sai Kung, Kowloon, Hong Kong

    Sponsored