MCP won’t let me be…

Continuing our series on MCP, In this video John explains what MCP is and a birds eye view of how to deploy an MCP server with django-mcp-server

MCP Won’t Let Me Be

…or why your LLM needs a wrapper if you want it to ship real code, not just cute prose

Context: The video at the top of this post is John’s lightning-tour of Model-Context Protocol (MCP) and the quickest path to spinning up an MCP-enabled Django project with django-mcp-server.
Below is the long-form write-up—perfect for pausing, skimming, and copy-pasting at your own pace.


TL;DR for the terminally busy

  • A raw LLM is not an agent; it lacks persistent memory and safe API access.
  • MCP adds typed tool manifests, separate auth, and structured logging—turning the LLM into an orchestrator instead of a kamikaze script-kiddie.
  • One MCP server → every client that speaks MCP (ChatGPT, Copilot, local LLaMA) can call your stuff.
  • Scroll to the bottom for the business payoff and next steps.

1. Raw LLM ≠ Agent

LLMs are brilliant storytellers but amnesiac engineers.
Ask ChatGPT to “create a user” and then immediately “make a DB for that user.” It forgets the ID you just gave it—because every turn starts from zero. Agents need an external memory + tooling system. MCP provides both.


2. Context-Window Reality Check

“128 k tokens” sounds huge—until you remember that’s ~300 KB of text. Your smallest SaaS codebase is 10–100× bigger. The model will never “know” your whole system; it only needs to know how to call it. That’s the job of tool manifests.


3. Tools, Calls & Manifests

Free-hand JSON is the Wild West: fields drift, types change, hallucinations happen. An MCP manifest is a strict contract—names, params, enums, return types. Agents stop guessing and start doing.


4. Security & Compliance Layer

Giving an untrusted LLM direct API keys is like handing the intern root. MCP splits:

  • Auth server – verifies the bearer token / OAuth flow / whatever.
  • Tool server(s) – do the work, gated by scopes & rate-limits.

That clean split lets you pass a SOC 2 audit without losing sleep.


5. Connector Explosion → One Bus

Without a standard you get M × N wrappers (every model × every API).
With MCP you write one server; every agent that speaks MCP plugs in. Think HTTP for agents.


6. Observability & Audit Trails

Every tool call is a structured log entry—great for debugging and governance: replay, rollbacks, blame, glory.


7. Live Code Drop: The Forwarding Pattern

Here’s an abridged version of the OSUser tool demoed in the video. Key idea: zero business logic in the tool—just forward to the classic API.

# osuser_example.py
from typing import Literal, Any, Dict
from modules.mcp.utils import MCPTransport
from mcp_server import MCPToolset

class OSUserAPI:
    def __init__(self, token: str) -> None:
        self.http = MCPTransport(bearer_token=token)

    def list(self) -> list[dict]:
        # This is where you would place logging for auditing, 
        # on each CRUD or whatever tooling you decide.
        # You would have full context to "self.request" which is the same as django "request"
        return self.http.get("/osuser/list/")

    def read(self, data: Dict[str, Any]) -> dict:
        return self.http.get(f"/osuser/read/{data['id']}")

    def create(self, data: Dict[str, Any]) -> dict:
        return self.http.post("/osuser/create/", [data])

    def update(self, data: Dict[str, Any]) -> dict:
        return self.http.post(f"/osuser/update/", [data])

    def delete(self, data: Dict[str, Any]) -> str:
        return self.http.post(f"/osuser/delete/", [data])

class OSUserTools(MCPToolset):
    def osuser(self,
               action: Literal["list","read","create","update","delete"],
               payload: Any | None = None):
        """ in python this would be a docstring and it is where the manifest in django-mcp-server lives. This is where you describe to the LLM how to use your API. 
        """
        return {
            "list":   lambda _: self._api().list(),
            "read":           self._api().read,
            "create":         self._api().create,
            "update":         self._api().update,
            "delete":         self._api().delete,
        }[action](payload or {})
    def _api(self):
        return OSUserAPI(token=self.request.auth)

Flow in one breath: Copilot prompt → OSUserTools.osuser() → Django MCP server → classic REST → Celery worker/Ansible/whatever.


8. Real-World Magic: “Vibe Deploy WordPress”

When a user types:

chat: vibe deploy wordpress on opalstack

the agent:

  1. Creates an OS user & MySQL DB via two MCP tools
  2. Spins a containerless runtime
  3. Runs the WordPress installer
  4. Issues a Let’s Encrypt cert
  5. Returns a secure preview URL

The model never “learned DevOps.” It just chained typed tools together.


9. Business Payoff

  • Speed – one protocol, less glue.
  • Reliability – typed manifests curb hallucinations.
  • Governance – audit trails make SecOps & Legal smile.
  • Ecosystem – every new MCP-speaking service increases the pie.

10. Next Steps

LinkPip
django-mcp-server on pypipip install django-mcp-server

Questions? send us an email, sales@opalstack.com Ready to ship? Grab a PAT, push to Git, and let MCP handle the midnight shift.


Say it. Ship it. Keep the vibes moving. 🚀