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
libraryandname, because a bare name does not identify a record. - compose_page: requires
intentand acceptspageType,sectionsandtheme, withlimitup 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.