MCP Server Scaffolding Generator

Generate an MCP server skeleton that starts correctly the first time: capabilities declared, logging on stderr where stdio requires it, Origin validated where HTTP requires it, and a tool description written as an interface rather than a summary.

The server
The first tool

mcp-server.txt

updates as you type

    Examples

    Worked setups you can load into the form above. Each one is a decision the generator makes differently, and the reason it makes it.

    A stdio server logs on stderr

    stdout is the transport. The generated Node server uses console.error throughout, because one console.log corrupts the JSON-RPC stream and the client reports a parse error nowhere near the print.

    name
    docs-search
    language
    typescript
    transport
    stdio
    tool-name
    search_docs
    tool-when
    Call when the user asks about API behaviour documented in the handbook. Do not call for questions about their own code.

    An HTTP server validates Origin

    A local server with no Origin check is reachable from any web page the user visits, which is DNS rebinding. The specification requires the check, so the skeleton ships with it.

    name
    metrics-bridge
    language
    typescript
    transport
    http
    port
    3000
    tool-name
    query_metrics
    tool-when
    Call to read a recorded metric series by name. Do not call to write or delete anything.

    A description that says nothing

    The description is the only thing the model reads when deciding whether to call, and it is read on every request. A summary of what the code does produces a tool that is either never used or used for the wrong questions.

    name
    docs-search
    language
    python
    transport
    stdio
    tool-name
    search_docs
    tool-when
    Searches docs

    A tool name the model has to guess at

    Tool names are part of the interface too. camelCase and spaces are inconsistent with the convention every published server uses, and the model matches on the name before it reads the description.

    name
    docs-search
    language
    typescript
    transport
    stdio
    tool-name
    SearchDocs
    tool-when
    Call when the user asks about API behaviour documented in the handbook. Do not call for questions about their own code.

    Common mistakes

    These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

    1. Printing anything to stdout on a stdio server

      stdout is the transport. One startup banner, one `print` left in from debugging, or one dependency that logs on import, and the JSON-RPC stream is corrupt. The client reports a parse error or an empty tool list, and neither points anywhere near the print.

      Instead:Every diagnostic to stderr. In Node that is `console.error`; in Python set `logging.basicConfig(stream=sys.stderr)` before anything else runs.

    2. Declaring an empty capabilities object

      A client that is not told the server has tools never calls `tools/list`, so a perfectly working server offers the model nothing. It presents as a broken server and it is a missing line in `initialize`.

      Instead:Declare each area you implement, `"tools": {}` at minimum, and add `resources` or `prompts` only when you actually serve them.

    3. Writing the tool description for a human reader

      It is the only thing the model reads when deciding whether to call, and it is read on every request. "Searches the docs index" says what the code does and nothing about when to reach for it, which produces a tool that is either never used or used for the wrong questions.

      Instead:Write when to call it and when not to. The negative case is usually what stops the misfires.

    4. Binding an HTTP server to 0.0.0.0 with no Origin check

      A local server is then reachable from the network and from any web page the user visits, which is DNS rebinding. The specification requires servers to validate Origin for exactly this.

      Instead:Bind to 127.0.0.1 and validate Origin against an allowlist. Widening either should be a deliberate edit, not a deleted check.

    Four things that make a first MCP server do nothing

    The code is short. What it is really shipping is the handful of details that turn a server which starts correctly into one that starts, reports itself healthy, and offers the model nothing.

    On stdio, stdout is the transport

    One print statement, one startup banner, one dependency that logs to stdout, and the JSON-RPC stream is corrupt. The client reports a parse error or lists no tools, and neither points anywhere near the print. Every diagnostic goes to stderr, and that includes libraries you did not write: a deprecation warning printed by a dependency breaks this in exactly the same way.

    console.log("ready")    breaks the stream
    console.error("ready")  fine
    
    print("ready")                              breaks it
    logging.basicConfig(stream=sys.stderr)      fine

    An empty capabilities object means an empty tool list

    Capabilities are declared during initialize. A client that is not told the server has tools never calls tools/list, so the server runs perfectly and the model sees nothing at all. It looks like a broken server and it is a missing declaration.

    The tool description is the interface

    It is the only thing the model reads when deciding whether to call, and it is read on every request. A description that says what the tool does internally, rather than when to reach for it, produces a tool that is either never used or used for the wrong questions. Include the negative case: saying when NOT to call is often what stops the misfires.

    A tool error is not a protocol error

    Returning isError with readable content tells the model the tool ran and could not do the job, so it can try something else. Throwing makes it a JSON-RPC error, which tells the model the call failed and nothing about why. Only the first is recoverable.

    On HTTP, bind to loopback and validate Origin

    A local server on 0.0.0.0 with no Origin check is reachable from the network and from any web page the user visits. That second one is DNS rebinding, and the specification requires servers to validate Origin. The generated code does both, and widening the allowlist should be a deliberate edit rather than deleting the check.

    What this generates and what it does not

    A working skeleton with one tool, the capability declaration, the transport wiring and the client config entry, because a server nobody has wired into a host is the other way to have nothing happen. It is not a project: there is no package manifest, no build config, no tests and no dependency pinning, and the SDK import paths move between major versions so check them against the version you install. The tool body is a stub that returns a message; the point is everything around it.