Model Context Protocol: Week 1 Lessons Learned
Model Context Protocol: Week 1 Lessons Learned
I had MCP on my list for three months. Not because I doubted it mattered — I knew it did — but because every time I looked at the documentation, it felt like a rabbit hole that would eat a full week. I was right about that. What I got wrong was thinking “eating a full week” was a reason to wait.
This post is an honest account of what week 1 with Model Context Protocol actually looked like: what broke, what I got wrong, what surprised me, and what I’d do differently if I were starting today.
TL;DR
MCP is the right abstraction for connecting AI agents to external tools and data sources. The learning curve is real but front-loaded. The security surface is non-trivial and almost nobody is talking about it in practical terms. Week 1 cost me roughly 12 hours of actual work and produced one working server, two broken ones, and a much clearer picture of where this fits in a real security-conscious stack.
What MCP Actually Is (And Why the Docs Undersell It)
Model Context Protocol is an open standard developed by Anthropic that defines how AI models communicate with external tools, data sources, and services. Think of it as a USB-C standard for AI integrations — instead of every tool needing a custom integration with every model, you build one MCP server and any compliant client can use it.
The official documentation explains the architecture clearly enough. What it doesn’t prepare you for is the mental model shift. Coming from 25 years of enterprise security — where I’ve built and reviewed hundreds of integrations — I kept trying to map MCP onto existing patterns: it’s like an API gateway, it’s like a middleware layer, it’s like an RPC framework. It’s a little bit of all of those, which means none of those analogies fully fit, and relying on them slows you down.
The actual breakthrough came when I stopped thinking about what MCP resembles and started thinking about what it solves: AI models have no reliable, standardized way to invoke external actions or query external state. MCP solves that. Once I held onto that framing, the rest of the architecture made immediate sense.
Week 1 Setup: What Actually Happened
Day 1 was documentation and environment setup. I followed the Python SDK quickstart, got a basic server running in about 40 minutes. It did nothing useful but it ran. That felt like progress until I realized I had no idea what was actually happening on the wire.
First thing I did was read the transport layer section carefully. MCP supports two transports: stdio (standard I/O, for local process communication) and HTTP with Server-Sent Events (for remote servers). The docs present these as roughly equivalent options. From a security standpoint, they are not remotely equivalent, and that distinction matters before you build anything real.
Day 2 I tried to build something I’d actually use: an MCP server that could query our internal project status from a local SQLite database. This is where the real learning started.
The first version worked. The second version, after I added filtering logic, silently returned empty results for about two hours before I realized I’d introduced a parameter handling bug that the MCP framework had no opinion about. No error, no warning — just nothing. Security people recognize this pattern: silent failure is often more dangerous than loud failure. I added explicit logging at the tool call boundary and the problem became immediately visible.
Day 3 was my first serious pause. I was thinking through how I’d eventually expose an MCP server to handle queries from Claude, and I started mapping the attack surface. What I found wasn’t catastrophic, but it was enough to slow me down.
The Security Surface Nobody Warned Me About
MCP model context protocol security deserves more attention than it currently gets in the community. Here’s what I identified in week 1:
Tool trust is implicit. When a model calls an MCP tool, it trusts the tool’s output completely in the default configuration. If a malicious or misconfigured tool returns manipulated data, the model acts on it. This is a prompt injection vector that originates outside the prompt — from the tool response itself. I’d read about tool poisoning in the abstract; MCP made it concrete.
The permissions model is young. MCP doesn’t currently have a mature, built-in authorization layer. You’re responsible for implementing access controls on your MCP server. That’s not inherently bad — many solid systems work this way — but it means teams adopting MCP need to add authorization logic explicitly rather than assuming the framework handles it.
Stdio transport assumes local trust. If you’re running MCP servers as local processes, you’re implicitly trusting the local environment. In a personal dev setup, that’s probably fine. In a shared environment, CI/CD pipeline, or any context where multiple principals have local access, it’s a meaningful risk.
Logging is opt-in. By default, MCP doesn’t produce audit logs that a security team would consider sufficient. You have to build that yourself. I added structured logging at the tool invocation boundary on day 2 and I’d make that day 1 if I were starting over.
None of these are reasons not to use MCP. They’re reasons to approach it the way you’d approach any new integration protocol: understand the security model before you build on top of it.
What Actually Broke (And Exactly How I Fixed It)
Broken thing 1: Schema validation failures with no useful errors.
My first tool definition had a type mismatch in its input schema. The call failed silently on the model side. Fix: validate your tool schemas independently before wiring them up. The fastmcp library has a --inspect option that surfaces schema errors before runtime. Use it.
Broken thing 2: Async handler blocking the event loop.
I made a synchronous database call inside an async handler. The server appeared to work but would hang under any parallel load. Fix: use asyncio.to_thread() to wrap synchronous I/O, or switch to an async database driver. I went with aiosqlite and the problem disappeared.
Broken thing 3: Tool descriptions that confused the model. This one surprised me. I wrote terse, technically accurate tool descriptions — the kind you’d write for a developer reading documentation. The model consistently chose the wrong tool when similar tools were available. Fix: write tool descriptions for the model, not for a human engineer. Explicit, specific, slightly verbose descriptions produced dramatically better tool selection. This is a prompt engineering problem embedded in your server architecture.
What Week 1 Produced
By the end of the week I had:
- One working MCP server with three tools: project status query, note creation, and a filtered search over a local knowledge base
- A structured logging wrapper I’ll reuse on every MCP server going forward
- A personal security checklist for MCP deployments (shared below)
- A clear picture of where MCP fits versus direct API calls versus agent frameworks
The working server took about 6 hours of actual coding time across the week. The rest was reading, debugging, and thinking through the security model.
MCP Security Checklist for Week 1 Deployments
- Choose transport deliberately: stdio for local trust environments, HTTP+SSE for anything crossing a network boundary
- Implement explicit authorization on every tool that touches sensitive data or performs writes
- Add structured logging at the tool invocation boundary before deploying anywhere beyond localhost
- Write tool descriptions for model comprehension, not developer documentation
- Validate input schemas independently before wiring tools to a live client
- Treat tool response data as untrusted input — sanitize before passing it back into model context
- Document which tools have write access vs. read-only access, and enforce that separation in code
Key Takeaways
MCP is worth learning. The abstraction is sound and the adoption curve in the ecosystem is accelerating. Teams that understand it now will have a meaningful head start.
The security model requires deliberate attention. The framework doesn’t protect you from misconfigured tools, missing authorization, or injection via tool responses. You have to build those controls yourself.
Week 1 will feel slower than it should. The mental model shift is the real work. Once it clicks, the implementation moves quickly.
Write tool descriptions twice. Once for yourself (what it does), once for the model (exactly when to call it and what to expect). Ship the second version.
If you’re a security engineer or technical leader evaluating MCP for your team, the question isn’t whether to adopt it — it’s how to adopt it without inheriting a new attack surface you haven’t mapped. That mapping is week 1’s most important output, and it’s faster than I expected once I stopped waiting to start.
Comments