What the node command does

An entry such as {"command": "node", "args": ["server.js"]} in Claude's MCP config is an instruction to start a program on your machine. Here is everything that follows, message by message.

By the end of this lesson you can

  • describe the lifecycle of a local server from spawn to shutdown;
  • say what travels on stdin, stdout and stderr, and the one rule about stdout;
  • explain how a tool definition ends up in the model's prompt and how a call comes back;
  • explain why adding a local server is a supply-chain decision.

A config entry is a command

This is all a host needs to run a local server. There is no URL and no token: the host simply starts the program as a child process.

You picked Remote, so here is the other kind of entry first, for contrast. A remote server is a url, not a command: nothing starts on your machine, and the token comes from OAuth instead of env. The rest of this lesson is about the command kind; the remote side is covered in stdio and Streamable HTTP and The OAuth flow for remote servers.

// a remote entry: the host connects, it does not spawn anything
{
  "mcpServers": {
    "deployer": {
      "type": "http",
      "url": "https://deploy.example.com/mcp"   // OAuth on first use; a bearer token on every POST
    }
  }
}
// an MCP config file (Claude Desktop, Claude Code and others use this shape)
{
  "mcpServers": {
    "deployer": {
      "command": "node",
      "args": ["server.js"],
      "env": { "DEPLOY_TOKEN": "…" }   // stdio servers get credentials from the environment, never OAuth
    }
  }
}

A parent, a child and three pipes

Host process reads the config MCP client "command": "node" "args": ["server.js"] Child: node server.js your user, your files, your network cwd and env from the config stdin: requests, one JSON line each stdout: responses, nothing else stderr: logs only (the host may save them) console.log("hi") on stdout = a line that is not JSON-RPC host closes stdin (EOF) server exits; else SIGTERM, SIGKILL {"method":…}\n {"result":…}\n log hi
  1. The host reads the config and creates an MCP client for this server.
  2. It spawns node server.js as a child process, with the working directory and environment from the config. The child runs as you.
  3. The host writes requests to the child's stdin: one JSON-RPC message per line, no embedded newlines.
  4. The server writes responses to its stdout. Each line is one JSON-RPC message, and nothing else may appear there.
  5. Logs go to stderr. The host may capture them, but never parses them as protocol.
  6. A stray console.log on stdout is a line that is not JSON-RPC: the client's parser chokes. This is the classic first bug in a hand-written stdio server.
  7. To stop the server, the host closes stdin. End-of-file is the goodbye; the server should exit, and the host escalates to SIGTERM and SIGKILL only if it lingers.

Spec rule

A server MUST NOT write anything to stdout that is not a valid MCP message. Servers SHOULD exit promptly when stdin is closed.

Step through a local server's life

Three lanes: the model, the host with its client, and the server process. Use Next, Play or the arrow keys, or click any message in the diagram.

From tool list to the model's prompt

Notice where the model appears in the stepper: only at steps 6, 7 and 10. Everything else is the host and the server talking. What the model receives is not JSON-RPC but a tool definition in its own API format, usually renamed to mcp__deployer__deploy so two servers can both offer a tool called deploy.

Spec principle

Hosts must obtain explicit user consent before invoking any tool.

It runs as you

Key idea

Adding a local server means running that program with your permissions. It can read what you can read and call what you can call. That is why a local server is a supply-chain decision, not a settings change.

Common misconception

“The model only calls the tools it is given, so a local server is sandboxed by MCP.” MCP does not sandbox anything. The process can do whatever your account can do, whether or not the model ever calls a tool. Sandboxing, if you want it, is the host's or your operating system's job.

Quick check

Your stdio server needs to print debugging output. Where should it go?

  • stdout, prefixed with "DEBUG:" so the client can skip it
  • stderr
  • A notification on stdout
stdout carries only protocol messages; any other line breaks the client's parser. stderr is for logs, and the host may capture it.

How does a host ask a stdio server to shut down gracefully?

  • It sends a shutdown request and waits for the reply
  • It closes the server's stdin; the server exits on end-of-file
  • It sends SIGKILL straight away
There is no goodbye message. Closing stdin is the graceful signal; SIGTERM and then SIGKILL follow only if the process lingers.

What does the model produce when it wants to use the deploy tool?

  • A JSON-RPC tools/call request
  • A tool_use block that the host turns into tools/call
  • A line on the server's stdin
The model never speaks MCP. It writes a tool-use block in its own API format; the host checks consent and routes it through the client, which writes the JSON-RPC line.

Recap