Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save jonasvanderhaegen/950f190e4009192541f9b806778e3cb0 to your computer and use it in GitHub Desktop.

Select an option

Save jonasvanderhaegen/950f190e4009192541f9b806778e3cb0 to your computer and use it in GitHub Desktop.
MCP, LSP, custom protocols, and local models — a mental model

MCP, LSP, custom protocols, and local models — a mental model

A short, diagram-first explainer untangling four things people routinely conflate: MCP, LSP, rolling your own agent↔tool protocol, and whether a model "knows" MCP.


1. MCP is not a transport layer

The tempting analogy is "MCP is like HTTP/TCP but for LLMs." That's wrong, and it will mislead you. MCP (Model Context Protocol) is an application-layer protocol for connecting an LLM agent to external capabilities: tools (functions it can call), resources (data it can read), and prompts.

It is JSON-RPC 2.0 on the wire. The transport beneath it is pluggable — stdio (local subprocess) or HTTP / streamable-HTTP+SSE (remote). So MCP sits on top of a transport; it is not one.

  Web stack            does                MCP equivalent
  ─────────────────────────────────────────────────────────
  TCP / HTTP           move bytes          stdio / HTTP  (transport MCP rides on)
  REST API             expose capability   ← THIS is the MCP layer
  Browser              consume the API     the LLM host (e.g. an agent CLI)

The accurate one-liner: MCP is "LSP for AI agents."

Before LSP, integrating N editors with M language servers was N×M custom glue. LSP made it N+M. MCP makes the same bet for agents: implement an MCP server once, and every MCP-compatible host can use it. Same JSON-RPC foundation, same network-effect motivation. That is not a coincidence — MCP was deliberately modeled on LSP.

The three MCP primitives

flowchart LR
    subgraph Host["LLM Host / Client"]
        M[Model loop]
    end
    subgraph Server["MCP Server"]
        T["Tools<br/>(model-invoked fns)"]
        R["Resources<br/>(readable URIs)"]
        P["Prompts<br/>(user-triggered templates)"]
    end
    M -- "tools/call" --> T
    M -- "resources/read" --> R
    M -- "prompts/get" --> P
    T -- result --> M
    R -- data --> M
    P -- template --> M
Loading
  • Tools — model decides to call these (e.g. an edit or grep function).
  • Resources — readable data addressed by URI (e.g. a guide:// doc).
  • Prompts — user-triggered templates (often surfaced as slash commands).

2. The protocol is replaceable; adoption is what costs you

A tool engine (say, a content-hash-guarded line editor) and the protocol that exposes it to an LLM are two separate layers. The engine doesn't know or care about MCP.

  ┌─────────────────────────────────────────────┐
  │  Engine: the actual capability               │  ← protocol-agnostic, reusable
  │  (edit logic, hash guards, search, …)        │
  └───────────────┬─────────────────────────────┘
                  │ exposed via …
   ┌──────────────┼───────────────┬──────────────┐
   ▼              ▼               ▼              ▼
  CLI          MCP          your own JSON-RPC   OpenAI-style
 (argv+stdout)              / REST schema       function-calling

A good tell: many tools ship as both a CLI and an MCP server from the same binary. The CLI path speaks no MCP at all — it parses argv and calls the same internal functions. That proves the editing core is already protocol-agnostic.

So yes, you could invent your own protocol instead of MCP. People did exactly this before MCP existed (raw function-calling, bespoke tool schemas).

The honest catch

If you invent MyProtocol-v1, the capability works fine — but no existing host knows how to speak it. You'd write a client, or a shim that translates your protocol into something hosts already understand… which is precisely the integration tax MCP exists to kill. You'd have rebuilt MCP under a new name.

MCP's value is the network effect, not the technical design. Drive the tool only yourself? A custom protocol (or just the CLI) is completely viable, arguably simpler. Want other people's agents to use it? You're paying for compatibility, and MCP is the cheapest way to pay.


3. "AI LSP server" — the interesting middle case

"AI LSP server" can mean two genuinely different things:

  • (a) An LSP server for AI consumption — you speak real LSP (textDocument/definition, references, hover, diagnostics), but the client is an LLM agent, not an editor.
  • (b) An LSP-shaped protocol for AI — you borrow LSP's design (long-lived server, document sync, position-addressed requests) but the methods are AI-native, not the LSP spec.

The reason you'd bridge LSP → MCP rather than hand an agent raw LSP:

LSP was designed for LLM agents actually need
Addressing {line, character} cursor positions stable symbol/anchor IDs that survive edits
State editor owns the buffer, streams didChange agent has no buffer; server must own truth
Granularity one capability per request coarse, batched, "do the useful thing" calls
Lifecycle tied to an open editor window tied to a task; may be headless

Raw LSP leaks editor assumptions the model doesn't satisfy: position math goes stale the instant a file changes, and the model has no persistent buffer to sync. So a useful "AI LSP server" wraps the LSP intelligence with edit-safe anchoring and serves that to the agent.

flowchart LR
    A[LLM Agent] -- MCP --> W[AI-facing wrapper<br/>hash guards + stable anchors]
    W -- real LSP --> L[Language Server<br/>rust-analyzer, tsserver, …]
    L -- definitions / refs / diagnostics --> W
    W -- agent-safe results --> A
Loading

The wrapping is the product. Underneath: an LSP client. At the agent boundary: MCP (or real LSP, or a custom protocol — your choice on that face).


4. Will any local model from HuggingFace "know" MCP?

No — and this is the most common misconception. Knowing MCP is not a property of the model. It's a property of the host around the model.

A raw model is a function: tokens in, tokens out. It cannot open a stdio pipe or make a JSON-RPC call. It only emits text. The host does every piece of real MCP work.

sequenceDiagram
    participant Mdl as Model (weights)
    participant Host as Host / Client
    participant Srv as MCP Server

    Host->>Srv: tools/list
    Srv-->>Host: tool schemas
    Host->>Mdl: prompt + tool schemas (as text)
    Mdl-->>Host: "call edit(args)" (as text)
    Host->>Srv: tools/call edit(args)   %% the real MCP call
    Srv-->>Host: result
    Host->>Mdl: result (as text)
Loading

Asking "does this model know MCP?" is a category error — like asking whether a CPU knows HTTP. The CPU runs the browser; the browser knows HTTP.

What a downloaded model actually needs

  1. Tool-/function-calling ability — a training property. The model must reliably emit structured tool calls when shown schemas. Instruct-tuned models advertise this (Qwen2.5-Instruct, Llama 3.1+, Mistral-Instruct, Hermes, …). A raw base model is bad at it.
  2. A host that bridges — Ollama + an MCP client, LM Studio, or a custom loop using an MCP client library that injects tools and parses calls.

Tool-capable model + MCP-aware host = your local model can drive MCP tools. The model isn't "knowing MCP"; the host translates between text and protocol.

Capability is a spectrum, not yes/no

  Big instruct (70B+)   ████████████  solid multi-step tool use
  Small instruct (≤7B)  ████          often malformed / hallucinated calls
  Base / non-instruct   ▏             effectively unusable without scaffolding

The protocol always works because it lives in the host. Whether the model uses the tools competently depends entirely on how well that specific model was trained for structured tool-calling.


TL;DR

  • MCP is an application-layer capability protocol (tools/resources/prompts) over JSON-RPC — "LSP for agents," not a transport.
  • The protocol is replaceable; MCP's worth is adoption / network effect, not its design.
  • An "AI LSP server" wraps real LSP intelligence with edit-safe anchoring and serves it over MCP — the wrapper is the product.
  • No model "knows" MCP. The host speaks MCP; the model only needs to be a competent tool-caller, which is a training property that varies a lot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment