Skip to content
Mastering Claude

Home / Extending: skills, MCP, subagents, hooks, plugins

Extending: skills, MCP, subagents, hooks, plugins9 minApplication

Building your own MCP server

A homemade MCP server launches as a child process over stdio transport, declares a JSON schema in inputSchema that the server itself must validate, and a precise description decides when Claude calls it.

A homemade MCP server is nothing more than an ordinary program that reads from and writes to its standard input and output following the MCP protocol. Claude Code launches it itself as a child process, via the stdio transport, without this program ever listening on a network port. The same mechanism serves an internal tool, connected to a private database, that no public server could expose without risk.

Declaring it to Claude Code

The registration command clearly separates what belongs to Claude Code from what belongs to the server: claude mcp add --env KEY=value --transport stdio server-name -- command arguments. Everything after the double dash is passed through to the server as is, without interpretation; everything before it, --transport or --env, configures the connection on Claude Code's side.

claude mcp add --env DEPOT=./donnees --transport stdio compteur -- node ./serveurs/compteur.js

The compteur.js process then starts at each session that needs it, receives the DEPOT variable in its environment, and communicates with Claude Code through messages written to its standard output.

The schema decides what gets in, the description decides whether it gets called

Each tool declares a JSON schema that bounds its valid arguments. A tool that counts the words in a text can require a mandatory string and reject any other type:

{
  "name": "compter_mots",
  "description": "Compte le nombre de mots dans un texte fourni en argument.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "texte": { "type": "string" }
    },
    "required": ["texte"]
  }
}

This schema declares what the tool expects, a missing or wrongly typed argument does not conform to it, but validation remains the server's responsibility: the MCP specification requires the server to validate every tool input, the schema is not a guarantee enforced on the client side before the call. Nor does the schema decide whether Claude chooses this tool over another: that role belongs to the description field, exactly as with a skill. A description that promises more than what the tool actually does, for example counting the words of any document without specifying that it only reads a text argument already supplied, backfires as soon as Claude relies on it for a case it does not cover.

The previous lesson shows how to check that a server declared this way indeed shows up connected before you trust it.

Figure 1

From the request to the MCP tool call

01
Reading the description
Claude compares the task at hand to the description of each available tool and picks the one that matches.
02
Building the arguments
Claude prepares the arguments to pass, drawn from the context of the conversation.
03
Sending to the server
The built arguments are sent to the server as is, with no client side validation against the declared schema.
04
Validation then execution
The server, launched by Claude Code as a child process via stdio, validates the received arguments itself against its schema before executing the code and returning a result on its standard output.
The sequence shows the four steps that separate a request made within the conversation from the actual execution of the server's code.
Calibrate it yourself

An engineer registers a homemade MCP server with claude mcp add --transport stdio compteur -- node ./serveurs/compteur.js, whose only declared tool requires a text argument of type string. He then asks Claude to count the words of an integer he types directly into the conversation.

Write in one sentence what this situation establishes, and in one sentence what it does not establish.

What to remember
  • A homemade MCP server is an ordinary program launched as a child process by Claude Code via the stdio transport, never opening a network port.
  • The double dash in the claude mcp add command separates Claude Code's own options, --transport and --env, from the command and arguments passed through to the server as is.
  • A JSON schema, in the inputSchema field, declares a tool's valid arguments, but it is the server's code, never the client's, that the specification requires to validate every call it receives.
  • A tool's description, not its schema, decides whether Claude picks it for a given task, exactly as with a skill.
Do this now

Write, in a text file on your machine, the JSON schema of an imaginary tool with a single mandatory argument, following the example in this lesson, without registering it with Claude Code.

Check the source

Every datable claim in this lesson links here to the public text behind it. A source that does not open proves nothing.