Skip to main content

mcp-hello-server

GitHub tag PyPI Python versions PyPI downloads Docker Hub Docker image size Docker pulls test bdd image-scan

Note: the version and download badges above are cached images (shields.io, and PyPI's own image proxy), so they can lag reality by a while — a version badge showing an older number doesn't mean an old release. For the authoritative current version, check the latest release / tag, the version heading on the PyPI project page itself, or run the server's server_info tool. The downloads badge is PyPI's monthly count via pypistats.org (derived from the public BigQuery dataset, not a live PyPI counter), so a brand-new release can read 0/no-data for a day or two until the batch catches up.

A minimal MCP server built with Python and FastMCP — a good starting point for a new server or a demo. It exposes just two tools:

  • server_info — a health/status check.
  • greet — a friendly greeting in one of a handful of languages, defaulting to English. Ask it to "greet in French" and it replies Bonjour!.

Built with Python, uv, FastMCP, and make, and distributed two ways so you can run it however you like:

It was scaffolded from the sibling random-mcp-server by stripping it down to server_info and adding the greet demo tool.


Quick start — demo an MCP server in 2 minutes

New to MCP? This is a tiny, safe server for seeing how an MCP client discovers and calls tools. Every tool is a harmless in-memory lookup, so it's a good sandbox. All you need is an MCP client and either uv (which provides uvx) or Docker — the steps below use Claude Code (nothing to build or clone), with the equivalent Hermes Agent commands at the end.

1. Add the server. Pick whichever runtime you have — Claude Code launches it per session and talks to it over stdio:

# Python (no Docker) — runs the PyPI package via uvx (needs uv installed),
# downloading it on first use:
claude mcp add hello -- uvx mcp-hello-server

# …or Docker — runs the published image:
claude mcp add hello -- docker run -i --rm -e MCP_TRANSPORT=stdio mitchallen/mcp-hello-server:latest

2. Confirm it connected:

claude mcp list        # "hello" should report ✔ Connected

3. Ask in plain language — Claude discovers the tools and picks one (the tool it calls is in parentheses):

  • "Is the hello server up? What version is it?" → (server_info)
  • "Greet me in French." → (greet → Bonjour!)
  • "Say hello in Japanese to Alice." → (greet → こんにちは (Konnichiwa), Alice!)
  • "What languages can you greet in?" → (server_info, reads languages)

That round trip — the client listing tools, then calling one with arguments and getting structured JSON back — is MCP. Peek at the tool schemas the client sees with make dev (the FastMCP Inspector), or read Tools below.

4. Remove it when you're done:

claude mcp remove hello

Prefer HTTP? Run it as a long-lived server instead — with Python or Docker:

MCP_TRANSPORT=http uvx mcp-hello-server              # Python, serves on :8000
# …or: docker run --rm -p 8000:8000 mitchallen/mcp-hello-server:latest
claude mcp add --transport http hello http://localhost:8000/mcp

See Using a published image or a remote server for other clients and the mcp-remote bridge.

Want it installed, not ephemeral? uvx fetches and runs the package without installing it. To keep it on your PATH, install the PyPI package instead:

pipx install mcp-hello-server        # or: pip install mcp-hello-server
mcp-hello-server                     # the console script runs the same server

Hermes Agent

Hermes Agent takes the command and its arguments as separate flags rather than after a -- separator. Register the server, then check the connection:

hermes mcp add hello --command uvx --args mcp-hello-server
hermes mcp test hello

As with the Claude Code route above, uvx needs uv installed and downloads the PyPI package on first use — nothing to clone or build. Once it connects, the same plain-language prompts in step 3 apply; the server is identical, only the client differs.

Hermes also reads MCP servers from its config.yaml (command:/args: for stdio, url: for an HTTP endpoint like the one in the note above), which is the better route for anything beyond a one-liner; run /reload-mcp in a session to pick up edits. See the Hermes MCP guide for the full configuration reference.


Tools

Tool Title Purpose
server_info() Server Info Health/status: app name, version, uptime, supported languages
greet(language?, name?) Greet Greeting in language (default English); optional name

greet

greet takes two optional arguments:

  • language — a language name, an alternate spelling, or an ISO code (case-insensitive). Omit it to default to English. Supported: english, spanish, french, german, italian, portuguese, japanese, hawaiian (e.g. french, Français, or fr all work).
  • name — optional; personalizes the message (Bonjour, Alice!).

It returns { language, greeting, message }:

// greet(language="french")
{ "language": "french", "greeting": "Bonjour", "message": "Bonjour!" }

// greet(language="spanish", name="Alice")
{ "language": "spanish", "greeting": "Hola", "message": "Hola, Alice!" }

// greet()  -> { "language": "english", "greeting": "Hello", "message": "Hello!" }

An unknown language returns an error listing the supported set.

Annotations

Both tools carry MCP tool annotations — hints a client can use to decide how to present a tool and whether it needs a confirmation prompt:

Annotation Value Meaning
readOnlyHint true The tool does not modify its environment
openWorldHint false It touches no external entities — no network, no disk

Together these say both tools are safe reads against in-process data, so a client can list or auto-approve them without prompting. Each also has a human-readable title (see the table above) for display in place of the raw function name.

destructiveHint and idempotentHint are deliberately not set: the MCP spec defines them as meaningful only when readOnlyHint is false, so setting them on a read-only tool would imply significance they don't carry. A new tool that writes state or reaches the network should flip readOnlyHint/openWorldHint and then set them.

Annotations are hints, not a security boundary — a client is free to ignore them, and they describe the server's own claims about its tools.

Add a language

Add a row to GREETINGS in src/mcp_hello_server/greetings.py (and, optionally, an alias / ISO code in _ALIASES). server_info reports the supported set automatically.


Quick start

Requires uv.

make install     # create .venv and sync deps
make test        # run the test suite
make run         # run the server over stdio

make help lists every target.


Running the server

stdio (default — for MCP clients that launch the server)

uv run mcp-hello-server
# or
make run

Streamable HTTP (for networked clients / containers)

make run-http            # PORT defaults to 8000
PORT=9000 make run-http

Inspect the server

make inspect             # print a summary: name, version, tool count
make dev                 # launch the interactive FastMCP Inspector (web UI)

Configuration

All configuration is via environment variables:

Variable Default Purpose
APP_NAME mcp-hello-server Name reported by server_info
MCP_TRANSPORT stdio stdio, http, or sse
HOST 127.0.0.1 Bind address for http/sse
PORT 8000 Bind port for http/sse

Using with an MCP client — local development (from source)

Point a stdio-based client (e.g. Claude Desktop, Claude Code) at the console script. Example claude_desktop_config.json entry using uv:

{
  "mcpServers": {
    "hello": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/mcp-hello-server", "mcp-hello-server"]
    }
  }
}

With Claude Code:

claude mcp add hello -- uv run --directory "$PWD" mcp-hello-server

Confirm it's connected with claude mcp list (or /mcp inside a session).

Example prompts (Claude Code)

Once the server is added, just ask in plain language — Claude picks the right tool. The tool it invokes is shown in parentheses.

  • "Is the hello server up? What version is it?" → (server_info)
  • "Greet me." → (greet, defaults to English → "Hello!")
  • "Greet in French." → (greet with language="french" → "Bonjour!")
  • "Say hello in Japanese to Alice." → (greet with language="japanese", name="Alice")
  • "What languages can you greet in?" → (server_info, then read languages)

Using a published image or a remote server

This section is for consumers who are not building from source — you have the published Docker image, or someone has deployed the server for you.

Option A — Docker image, client launches it (stdio)

The client starts a fresh container per session and talks to it over stdio. Use -i (keep stdin open) and force the stdio transport, since the image defaults to HTTP. The image is published to two registries, so pick one:

// GitHub Container Registry (GHCR)
{
  "mcpServers": {
    "hello": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio",
               "ghcr.io/mitchallen/mcp-hello-server:latest"]
    }
  }
}
// Docker Hub
{
  "mcpServers": {
    "hello": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio",
               "mitchallen/mcp-hello-server:latest"]
    }
  }
}

Claude Code equivalent — again, pick a registry:

# GitHub Container Registry (GHCR)
claude mcp add hello -- docker run -i --rm -e MCP_TRANSPORT=stdio ghcr.io/mitchallen/mcp-hello-server:latest

# Docker Hub
claude mcp add hello -- docker run -i --rm -e MCP_TRANSPORT=stdio mitchallen/mcp-hello-server:latest

(Pin a version like :0.1.2 in place of :latest for a reproducible setup. Add --scope user to register the server for every project on your machine.)

Option B — Long-running container over HTTP (local)

Start the container once (it serves HTTP by default) from either registry, then point an HTTP-capable client at it:

# GitHub Container Registry (GHCR)
docker run -d --rm -p 8000:8000 --name mcp-hello ghcr.io/mitchallen/mcp-hello-server:latest

# Docker Hub
docker run -d --rm -p 8000:8000 --name mcp-hello mitchallen/mcp-hello-server:latest

Claude Code (native HTTP transport):

claude mcp add --transport http hello http://localhost:8000/mcp

For clients that only speak stdio, bridge to the HTTP endpoint with mcp-remote:

{
  "mcpServers": {
    "hello": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
    }
  }
}

Option C — Remote deployment (HTTP)

If the server is hosted elsewhere, use its public URL — everything else matches Option B:

claude mcp add --transport http hello https://mcp-hello.example.com/mcp

Notes for remote use:

  • Prefer HTTPS so traffic is encrypted in transit.
  • This server ships no authentication. If you expose it beyond localhost, put it behind a reverse proxy, gateway, or network policy — or add FastMCP auth.
  • The endpoint path is /mcp (no trailing slash). Requesting /mcp/ works too but returns a 307 redirect to /mcp.

Docker

Published multi-platform (linux/amd64, linux/arm64) images are available from two registries:

  • GitHub Container Registry: ghcr.io/mitchallen/mcp-hello-server
  • Docker Hub: mitchallen/mcp-hello-server

The image runs the server over streamable HTTP by default (MCP_TRANSPORT=http, HOST=0.0.0.0, PORT=8000) so it's reachable on a published port.

It's built on a distroless Chainguard/Wolfi Python base — no shell or package manager, runs as a non-root user, and scans 0 known vulnerabilities. Every build is gated by a Trivy scan (fails on fixable CRITICAL/HIGH) and the published :latest is re-scanned daily; see CI / Publish.

Pull and run

# GitHub Container Registry (GHCR)
docker pull ghcr.io/mitchallen/mcp-hello-server:latest
docker run --rm -p 8000:8000 --name mcp-hello ghcr.io/mitchallen/mcp-hello-server:latest

# Docker Hub
docker pull mitchallen/mcp-hello-server:latest
docker run --rm -p 8000:8000 --name mcp-hello mitchallen/mcp-hello-server:latest

Pin a specific release instead of :latest for a reproducible setup, e.g. ghcr.io/mitchallen/mcp-hello-server:0.1.2. Then connect an HTTP MCP client to http://localhost:8000/mcp.

Test a published release with make

Convenience targets pull and run the published image in your local Docker environment — handy for smoke-testing a release without a local build:

make docker-test               # up + smoke + down in one shot (exits non-zero on failure)

make docker-up                 # pull + run ghcr.io/mitchallen latest, detached
make docker-smoke              # MCP `initialize` handshake — passes if the server responds
make docker-down               # stop it

make docker-up TAG=0.1.2                         # pin a version
make docker-up REGISTRY=docker.io/mitchallen     # pull from Docker Hub instead
make docker-up HTTP_PORT=9000                    # publish on a different host port

Build locally

make docker-build        # docker build -t mcp-hello-server .
make docker-run          # serves http on localhost:8000

CI / Publish

Three kinds of GitHub Actions workflows live in .github/workflows/:

  • test — runs on every push/PR to main: the unit suite (pytest --ignore=tests/test_bdd.py).
  • bdd — runs the pytest-bdd scenarios (pytest tests/test_bdd.py) in its own workflow so it passes/badges independently of the unit suite.
  • publish / publish-dockerhub — triggered by pushing a v* tag. Build a multi-platform image and push it to GHCR and Docker Hub, then run make docker-test against the just-published image as a post-publish smoke check. The Docker Hub job needs DOCKERHUB_USERNAME / DOCKERHUB_TOKEN repository secrets and a pre-created mitchallen/mcp-hello-server repo.
  • publish-pypi — also triggered by the v* tag (and can be run manually via workflow_dispatch). Runs the suite on Python 3.11–3.13, then builds and uploads the sdist + wheel to PyPI using trusted publishing (OIDC — no stored token). It needs a matching PyPI publisher configured for this repo, workflow publish-pypi.yml, and a pypi GitHub environment.

To cut a release, use the release target — it bumps version in pyproject.toml (and uv.lock), commits, tags, and pushes, which triggers all three publish workflows (GHCR, Docker Hub, PyPI):

make release              # patch bump (default)
make release BUMP=minor   # or minor / major

The target refuses to run unless the working tree is clean and you're on main.


Development

  • Source: src/mcp_hello_server/
    • greetings.py — greeting data + language resolution (greet)
    • server.py — FastMCP tools + entry point (main)
  • Tests: tests/, run with make test (uv run pytest), driven through an in-memory FastMCP client. Layers:
    • test_greetings.py — plain pytest unit tests for the resolver/builder.
    • test_server.py — the tools through the in-memory client.
    • test_bdd.py + tests/features/*.feature — a pytest-bdd layer.
  • make build produces a wheel/sdist via uv build.
  • Dependencies: uv.lock is committed and the Docker build installs from it with --frozen. Whenever you change dependencies in pyproject.toml, run make lock (or uv lock) to refresh the lockfile and commit it.

License

MIT © Mitch Allen

Metadata

Release files for mcp-hello-server 0.4.10

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-hello-server 0.4.10
File Size Uploaded Provenance
mcp_hello_server-0.4.10.tar.gz 19.2 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-hello-server 0.4.10
File Interpreter ABI Platform
mcp_hello_server-0.4.10-py3-none-any.whl Python 3 none any Details

Total release size: 32.3 kB

Release files / mcp_hello_server-0.4.10.tar.gz

Download URL mcp_hello_server-0.4.10.tar.gz
Size 19.2 kB
Tags Source
SHA-256 checksum
How to use checksums
cc5a9921b25ba6fd7b0d2499b154d63b37394923049fe95b298b4d5ed85836e4
BLAKE2b-256 checksum
How to use checksums
7be56d5ba8561d32b35d984afde7fb51a99a4479f8790eb603a0f94d36838da5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Release files / mcp_hello_server-0.4.10-py3-none-any.whl

Download URL mcp_hello_server-0.4.10-py3-none-any.whl
Size 13.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0c555c625ac2d4281364057e36398bf23b8378bed99f3224182e6b5c9a6d97cb
BLAKE2b-256 checksum
How to use checksums
46346e097f96f2a5e77f4b5b092355c64a4251d0f46c02afa8258e6a8343df6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

Security for mcp-hello-server 0.4.10

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for mcp_hello_server-0.4.10.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page