AI & MCP

How to build an MCP server: transport, methods and limits

An MCP server is JSON-RPC 2.0 over a transport, and three methods are enough. Here is the structure, the tool schema and where the protocol stops.

Key points

  • Three methods are enough: initialize, tools/list and tools/call; notifications get no response.
  • The input schema is the documentation the model reads, so keep it narrow and described.
  • Transport is the server's choice: EneUI answers in JSON and keeps no session.

An MCP server is a process that answers JSON-RPC 2.0 over a declared transport and publishes three methods: initialize, tools/list and tools/call. Everything else is optional, and that is the part that surprises people arriving from a framework.

The minimum contract

A client sends initialize, receives version, capabilities and server name, then asks for tools/list. Notifications without an id get no response: the server closes with 202. An unknown method returns error -32601.

The first exchange

curl -s https://ui.eneawork.it/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

The answer sets capabilities.tools.listChanged to false: the server does not announce changes to the list, so the client must not listen. That line saves a persistent connection and explains why the transport can be a single POST.

The input schema is the product

The part a model actually reads is inputSchema. In search_components only query is required; category, library and limit are optional, and the maximum limit is 30. A free-text field without a description is an invitation to guess.

  • get_component: requires library and name, because a bare name does not identify a record.
  • compose_page: requires intent and accepts pageType, sections and theme, with limit up to 20.
  • get_page: requires a site path, for example /components/cat/modal, and returns Markdown.

Errors the model can read

EneUI returns the result as text content and sets isError when the call failed. Protocol errors stay separate: -32600 invalid request, -32601 unknown method, -32602 unknown resource, -32002 rate limit with Retry-After: 60.

Read-only by choice

The 9 EneUI tools read the catalogue and return the install command: none writes to disk, none spends quota. Design systems are also exposed as resources at eneui://design/<slug>, while prompts/list answers with an empty list.

The catalogue

The server serves 111,985 components and 1,556 blocks; one reachable record is shadcn sidebar. Endpoint and configuration for Claude Code and Cursor sit on /mcp, and /agents describes the flow from the agent side.

The limitation

EneUI answers in application/json instead of opening an SSE stream: the specification allows that for a single response, but a server that must push its own messages or progress needs the stream. The protocol requires neither authentication nor a sandbox, so the client must treat results and descriptions as untrusted data.

Where to continue

The protocol is explained in what MCP is; tool design is in designing an MCP tool. For configuration in the three editors read shadcn MCP in Claude Code, Cursor and VS Code, and for a comparison between servers read the best MCP servers for frontend work.

Frequently asked questions

Do I need a framework to build an MCP server?

No. An HTTP server and a JSON parser are enough: the protocol asks for three methods and JSON-RPC responses. Official SDKs cut the code, but they add no capability the protocol lacks.

Which transport should I choose?

stdio for a local process started by the client, Streamable HTTP for a remote endpoint. EneUI uses Streamable HTTP with JSON replies, because a proxy in front of a small VPS should not carry a stream.

How should a tool report errors?

With a text result and <code>isError</code> set to true when the call fails, so the model reads the reason. Protocol errors use JSON-RPC codes, for example -32601 for an unknown method.

Must an MCP server authenticate calls?

The protocol does not require it. EneUI starts anonymous with 120 requests per minute per IP and accepts a bearer token to apply the plan limit, answering 429 with Retry-After once the threshold is met.

Can I expose a read-only catalogue?

Yes, and it is the safer choice. Nine EneUI tools read the catalogue and return commands, with no disk write: the risk stays in the client that runs the command.