Skip to main content
Glama
souvikdey05

demo-server

by souvikdey05
README.md
# MCP Tutorial

A minimal [Model Context Protocol (MCP)](https://modelcontextprotocol.io) tutorial built with the
[MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) (`mcp[cli]`). It contains a small
FastMCP server and shows how to make **Claude Code** aware of it through project configuration — without
running `claude mcp add`.


## Contents

- [The server](#the-server)
- [Prerequisites](#prerequisites)
- [Running the server standalone](#running-the-server-standalone)
- [Registering the server with a client](#registering-the-server-with-a-client)
  - [Claude Code](#claude-code)
    - [Option A — stdio (Claude Code launches the server)](#option-a--stdio-claude-code-launches-the-server)
    - [Option B — HTTP transport](#option-b--http-transport)
    - [Reload and verify](#reload-and-verify)
  - [GitHub Copilot (VS Code)](#github-copilot-vs-code)
    - [stdio](#stdio)
    - [HTTP transport](#http-transport)
    - [Start and verify](#start-and-verify)
- [Approving servers and auto-approving tools (Claude Code)](#approving-servers-and-auto-approving-tools-claude-code)
  - [Step 1 — Approve the server](#step-1--approve-the-server)
  - [Step 2 — Auto-approve tool calls](#step-2--auto-approve-tool-calls)
- [Debugging with the MCP Inspector](#debugging-with-the-mcp-inspector)
- [Configuration reference](#configuration-reference)

## The server

The example server lives in [`examples/snippets/servers/fastmcp_quickstart.py`](examples/snippets/servers/fastmcp_quickstart.py)
and exposes one of each MCP primitive:

| Primitive | Name                  | Description                          |
|-----------|-----------------------|--------------------------------------|
| Tool      | `add(a, b)`           | Adds two numbers                     |
| Resource  | `greeting://{name}`   | Returns a personalized greeting      |
| Prompt    | `greet_user(name, style)` | Generates a greeting prompt       |

## Prerequisites

- [uv](https://docs.astral.sh/uv/) for running the project
- Dependencies are declared in [`pyproject.toml`](pyproject.toml) (`mcp[cli]>=1.28.0`)

Install dependencies:

```bash
uv sync
```

## Running the server standalone

To run the server directly over streamable HTTP (listens on `http://localhost:8000/mcp`):

```bash
uv run examples/snippets/servers/fastmcp_quickstart.py
```

This is only required for the [HTTP transport](#option-b--http-transport) option below. With the
[stdio](#option-a--stdio-claude-code-launches-the-server) option, Claude Code launches the server for
you, so you don't need to run this manually.

## Registering the server with a client

Each MCP client discovers servers from its own config file. The server configuration is nearly identical
across clients — the differences are **which file** holds it and **the top-level key name**:

| Client            | Config file       | Top-level key |
|-------------------|-------------------|---------------|
| Claude Code       | `.mcp.json`       | `mcpServers`  |
| GitHub Copilot    | `.vscode/mcp.json`| `servers`     |

Both files are committed to the repo, so anyone who clones it gets the same server configuration.

## Claude Code

Claude Code learns about **project-scoped** MCP servers from a `.mcp.json` file at the repository root.
This file is the declarative equivalent of `claude mcp add` — it's committed to the repo, so anyone who
clones it gets the same server configuration.

Pick **one** of the two transport options below.

### Option A — stdio (Claude Code launches the server)

Because the server lives in this project, you can let Claude Code spawn it on demand. There's no port and
nothing to keep running separately.

[`.mcp.json`](.mcp.json):

```json
{
  "mcpServers": {
    "demo-server": {
      "command": "uv",
      "args": ["run", "examples/snippets/servers/fastmcp_quickstart.py"]
    }
  }
}
```

> **Note:** For stdio, the server script must use the stdio transport. FastMCP uses stdio by default when
> you call `mcp.run()` with no arguments, so change the last line of the script to `mcp.run()` (or run a
> stdio-specific entry point).

### Option B — HTTP transport

If you'd rather run the server yourself as a long-running HTTP process, point `.mcp.json` at its URL
instead. First start the server (see [Running the server standalone](#running-the-server-standalone)),
then use:

[`.mcp.json`](.mcp.json):

```json
{
  "mcpServers": {
    "demo-server": {
      "type": "http",
      "url": "http://localhost:8000/mcp"
    }
  }
}
```

With this option the server must already be running before Claude Code connects.

> **Note:** For HTTP, the server script must use the streamable HTTP transport. In
> [`fastmcp_quickstart.py`](examples/snippets/servers/fastmcp_quickstart.py) the default `mcp.run()` is
> commented alongside `mcp.run(transport="streamable-http")` — swap the active line so the script runs
> over HTTP:
>
> ```python
> if __name__ == "__main__":
>     mcp.run(transport="streamable-http")
>     # mcp.run()  # default transport is stdio
> ```

### Reload and verify

1. Restart Claude Code (or run `/mcp` in an interactive session).
2. From the terminal, check status with:

   ```bash
   claude mcp list
   claude mcp get demo-server
   ```

3. The server should report **✔ Connected** and expose the `add` tool, the `greeting://{name}` resource,
   and the `greet_user` prompt.

## GitHub Copilot (VS Code)

VS Code discovers workspace MCP servers from [`.vscode/mcp.json`](.vscode/mcp.json). Note the top-level
key is `servers` (not `mcpServers`), and each entry declares its transport with `type`.

Pick **one** of the two transport options below.

### stdio

VS Code launches the server for you — no port, nothing to keep running separately:

[`.vscode/mcp.json`](.vscode/mcp.json):

```json
{
  "servers": {
    "demo-server": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "examples/snippets/servers/fastmcp_quickstart.py"]
    }
  }
}
```

> **Note:** As with Claude Code, the script must use the stdio transport (`mcp.run()`).

### HTTP transport

Run the server yourself first (see [Running the server standalone](#running-the-server-standalone)), then
point VS Code at its URL:

[`.vscode/mcp.json`](.vscode/mcp.json):

```json
{
  "servers": {
    "demo-server": {
      "type": "http",
      "url": "http://localhost:8000/mcp"
    }
  }
}
```

> **Note:** For HTTP, the script must use `mcp.run(transport="streamable-http")` (see the Claude Code
> [HTTP note](#option-b--http-transport)).

### Start and verify

1. Open [`.vscode/mcp.json`](.vscode/mcp.json) — VS Code shows a **Start** action above each server entry.
   You can also run **MCP: List Servers** from the Command Palette to start/stop/inspect them.
2. Open the Copilot Chat view and switch to **Agent** mode (tools are only available in agent mode).
3. Click the tools (🛠) icon to confirm `demo-server` is listed, then ask Copilot to use the `add` tool.

## Approving servers and auto-approving tools (Claude Code)

Claude Code has **two independent safety gates** for project MCP servers, both configured in
`.claude/settings.json` (shared, committed) or `.claude/settings.local.json` (personal, git-ignored):

1. **Server approval** — whether a server from `.mcp.json` is allowed to load at all.
2. **Tool-call approval** — whether Claude may *run* a server's tools without prompting you each time.

This section walks through both, using the `lifespan-demo` server (which exposes the `query_db` tool) as
the example.

### Step 1 — Approve the server

Servers declared in `.mcp.json` start as **⏸ Pending approval** as a safety measure — a cloned repo can't
auto-run servers without your consent. Grant approval declaratively with `enabledMcpjsonServers` instead
of clicking through the interactive prompt:

```json
{
  "enabledMcpjsonServers": ["demo-server", "lifespan-demo"]
}
```

Or trust every server defined in `.mcp.json` at once:

```json
{
  "enableAllProjectMcpServers": true
}
```

After this, the server loads and its tools become visible — but Claude will still ask permission **each
time** it wants to call one.

### Step 2 — Auto-approve tool calls

To stop the per-call prompts, add a `permissions.allow` rule. The MCP rule format is:

| Rule                              | Matches                                    |
|-----------------------------------|--------------------------------------------|
| `mcp__lifespan-demo`              | **Every** tool from the `lifespan-demo` server |
| `mcp__lifespan-demo__query_db`    | Only the `query_db` tool                   |

> **Note:** the tool part does not support `*` wildcards — use the server-only form (`mcp__<server>`) to
> cover all tools, or name a specific tool (`mcp__<server>__<tool>`).

Combined with Step 1, [`.claude/settings.json`](.claude/settings.json) looks like:

```json
{
  "enabledMcpjsonServers": ["demo-server", "lifespan-demo"],
  "permissions": {
    "allow": ["mcp__lifespan-demo"]
  }
}
```

Reload Claude Code (restart or `/mcp`), then ask:

> Use the query_db tool to run `SELECT * FROM users`

It runs without a permission prompt and returns `Result of 'SELECT * FROM users'`.

> **Scope tip:** keep server *approval* (`enabledMcpjsonServers`) in the shared `settings.json` so
> teammates get the server, but consider putting *auto-approval* (`permissions.allow`) in
> `settings.local.json` if you don't want to silently grant tool execution to everyone who clones the repo.

## Debugging with the MCP Inspector

The **MCP Inspector** is an interactive, browser-based tool for poking at a server directly — without
wiring it into Claude Code or Copilot. It's the quickest way to call tools by hand and watch logs and
progress notifications live.

Launch it against **any server** in this repo:

```bash

uv run mcp dev examples/snippets/servers/<server_file>.py

uv run mcp dev examples/snippets/servers/basic_tool.py
```

What this does:

- **`uv run`** runs inside the project virtualenv (so `mcp` and your deps are available).
- **`mcp dev`** starts the server over stdio and attaches the Inspector to it.
- It prints a `localhost` URL — open it in a browser.

The Inspector uses two ports: **6274** (the web UI) and **6277** (the proxy). In the UI you can:

- List and **call tools** — e.g. run `long_running_task` with a `task_name` and `steps`.
- Browse **resources** and **prompts**.
- **Watch logs and progress notifications live** — the best way to see `Context` calls
  (`ctx.info`, `ctx.debug`, `ctx.report_progress`) as they happen.

> **Requires Node.js.** The Inspector is a Node package (`@modelcontextprotocol/inspector`) launched via
> `npx`; the first run downloads it.
>
> **Auth token.** Recent versions print a URL that includes a session token, e.g.
> `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=…` — use that full link from the terminal.
>
> **Port already in use?** `❌ Proxy Server PORT IS IN USE at port 6277` means an Inspector is already
> running — just open <http://localhost:6274>, or stop the existing one first. It's a long-running
> process; stop it with `Ctrl-C`.

## Configuration reference

| File                          | Purpose                                                | Committed?         |
|-------------------------------|--------------------------------------------------------|--------------------|
| `.mcp.json`                   | Declares the project MCP server for **Claude Code**    | Yes (shared)       |
| `.vscode/mcp.json`            | Declares the workspace MCP server for **Copilot**      | Yes (shared)       |
| `.claude/settings.json`       | Approves servers + auto-approves tools for the team    | Yes (shared)       |
| `.claude/settings.local.json` | Approves servers + auto-approves tools just for you    | No (git-ignored)   |

TDQS

A3.5/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of ambiguity. The tool's purpose is clear and distinct.

Naming Consistency5/5

The single tool name 'add' follows a simple verb pattern, and there are no other tools to create inconsistency.

Tool Count1/5

A single tool for addition is extremely limited, lacking any broader functionality. This is a trivial tool count that does not justify a full server.

Completeness1/5

The server only provides addition, missing essential arithmetic operations like subtraction, multiplication, or division. The surface is severely incomplete for any practical use.

Maintenance

ActivityStale
ResponsivenessNo issues