Skip to content

MCP primer

A short orientation for contributors new to the Model Context Protocol. Not a replacement for the spec. Just enough context to read the codebase.

New to MCP? Read the official MCP quickstart first. This page assumes basic familiarity.

What MCP is

MCP is a JSON-RPC protocol. It lets an AI client (such as Claude Desktop, Cursor, Claude Code, or Continue) talk to a server that exposes capabilities.

The capabilities come in a few primitive types:

  • Tools — functions the model calls
  • Resources — data the application can read
  • Prompts — templated workflows users invoke

The client side can also offer capabilities back: sampling, roots, and elicitation. coolify-mcp uses none of these client primitives today.

The protocol is transport-agnostic. Most servers today run over stdio: the client spawns the server as a child process. HTTP+SSE and streamable-HTTP transports also exist for remote-hosted servers.

The primitive types

Control hierarchy

MCP draws a clean distinction about who controls invocation:

PrimitiveControlled byExample
PromptsUserSlash commands, menu picks
ResourcesApplicationFile contents, git history attached to context
ToolsModelAPI calls the LLM decides to make

This is why tools have safety affordances (annotations, output schemas, error semantics) — the model is autonomously choosing to call them.

What coolify-mcp uses today

Tools only. All 42 entry points are MCP tools registered via this.tool(name, description, schema, handler). No resources, no prompts, no annotations, no structured outputs.

That approach worked well for v1 and v2 but leaves several capabilities unused:

  • The client has no way to know which tools are destructive vs read-only
  • The model gets text blobs back from list_* instead of typed objects (harder to chain into get_* calls)
  • Long-running ops like deploy block the whole MCP transaction for minutes
  • There's no way for the client to subscribe to a Coolify entity and get pushed updates

See the v3 vision for the plan to address all of this.

How a tool call flows

The server doesn't see the user's prompt — it only sees the tool calls. That's a security boundary: the model decides what tools to call, the client decides whether to allow it, the server executes.

Error semantics

MCP draws a careful line between two error types:

TypeWhereWhenWhy it matters
Protocol errorJSON-RPC error fieldUnknown tool, malformed requestReturned to the client, NOT the LLM. Model can't self-correct.
Tool execution errorResult with isError: trueValidation failure, API down, business-logic errorReturned to the LLM. Model can read the error message and retry with adjusted parameters.

Most things you'd want to communicate ("missing required field", "Coolify returned 404", "deployment timed out") should be tool execution errors, not protocol errors.