Skip to main content
Glama
euuuuuuan

mcp-server-starter

by euuuuuuan
README.md
# mcp-server-starter

A minimal, **runnable** Model Context Protocol (MCP) server in Python over the
stdio transport, using the official [`mcp`](https://github.com/modelcontextprotocol/python-sdk)
SDK. It exposes two example tools — `echo` and `word_count` — with proper,
auto-generated JSON Schemas. Use it as a clean scaffold to stand up your own tools.

> This is a public, stripped-down version of the same pattern the author runs in
> production in **Baton's MCP daemon**, which exposes a large surface of
> macOS/automation tools (mail, calendar, shell, screen, files, ...) to AI clients
> over these exact MCP conventions.

## What is MCP?

The Model Context Protocol is an open standard for connecting AI applications
(MCP *clients* — e.g. Claude Code, Claude Desktop, IDE assistants) to external
capabilities (MCP *servers*). A server advertises **tools** (callable functions),
**resources** (readable data), and **prompts** (reusable templates); the client
discovers them at connect time and lets the model call them with validated,
schema-checked arguments. Instead of writing a bespoke integration per assistant,
you implement one MCP server and any compliant client can use it — most commonly
launched locally over the **stdio** transport (the client spawns your process and
exchanges JSON-RPC messages over stdin/stdout).

## Quick start

Requires Python 3.10+.

```bash
cd mcp-server-starter
python -m venv .venv && source .venv/bin/activate   # optional but recommended
pip install -r requirements.txt

# Run it directly. It will sit waiting for an MCP client to speak JSON-RPC on stdin.
python server.py
```

Running `server.py` by hand mostly just verifies it imports and starts; the server
is meant to be **launched by a client**. To exercise it interactively, use the
MCP Inspector:

```bash
# Spin up the official inspector UI against this server:
npx @modelcontextprotocol/inspector python server.py
```

In the Inspector you can list the tools, see their schemas, and call `echo` /
`word_count` with test inputs.

## Register with Claude Code

Use the `claude mcp add` command. Point it at the **absolute** path of `server.py`
(and the Python interpreter from your venv if you used one):

```bash
claude mcp add starter -- python /absolute/path/to/mcp-server-starter/server.py

# If you used a venv, prefer that interpreter explicitly:
claude mcp add starter -- /absolute/path/to/mcp-server-starter/.venv/bin/python /absolute/path/to/mcp-server-starter/server.py
```

Pick a scope with `-s`: `-s local` (default, this project only), `-s project`
(shared via a checked-in `.mcp.json`), or `-s user` (all your projects). Verify and
manage it with:

```bash
claude mcp list
claude mcp get starter
claude mcp remove starter
```

Once added, ask Claude Code to "use the word_count tool on this paragraph" and it
will call the server.

## Register with Claude Desktop (or any JSON-config client)

Add an entry under `mcpServers` in the client's config file (Claude Desktop:
`claude_desktop_config.json`), then restart the client:

```json
{
  "mcpServers": {
    "starter": {
      "command": "/absolute/path/to/mcp-server-starter/.venv/bin/python",
      "args": ["/absolute/path/to/mcp-server-starter/server.py"]
    }
  }
}
```

Use absolute paths — the client launches the command from its own working
directory, not yours.

## How to add a new tool

Adding a tool is adding a decorated function. The SDK reads the **type hints** and
**docstring** to build the tool's JSON Schema and description automatically, so
clear annotations directly become a clear tool contract.

```python
@mcp.tool()
def reverse(text: str) -> str:
    """Reverse a string.

    Args:
        text: The text to reverse.
    """
    return text[::-1]
```

Guidelines that carry over from the production Baton tools:

- **Type every argument and the return value.** They become the input/output
  schema; untyped args degrade to permissive `string`.
- **Write the docstring for the model.** State *when* to use the tool, not just
  what it does — clients surface this to help the model decide.
- **Keep tools small and single-purpose**, return structured `dict`s for anything
  a caller may want to parse, and validate inputs inside the function.
- **Side effects need guardrails.** For anything destructive or external
  (sending mail, writing files, hitting an API), validate inputs and consider
  requiring an explicit confirmation argument.

Restart the client (or re-run the Inspector) after editing — the tool list is read
at connect time.