Skip to main content
Glama

mcp101

ci

A hands-on sample MCP server built with FastMCP, used to try out the Model Context Protocol — basic tools first, then a real-world Google OAuth example that calls Google APIs on a user's behalf.

It accompanies the MCP Tooling learning note.

Layout

File

What it is

src/mcp101/server.py

Minimal no-auth server — add, greet, a time://now resource, a summarize prompt. Runs with zero credentials. Start here.

src/mcp101/google_server.py

Google OAuth server — authenticates the user with Google and lists their calendars via the Calendar API.

src/mcp101/github_server.py

GitHub OAuth server — authenticates the user with GitHub and lists their repos. Quickest auth demo to set up.

Related MCP server: Google-Workspace-MCP-Server

Requirements

  • Python >= 3.11

  • uv

  • just (optional, for the shortcuts below)

  • Node.js (only for the MCP Inspector)

Quick start (no auth)

just install          # or: uv sync
just run              # stdio transport, for Claude Desktop / Cursor
just run-http         # streamable HTTP on http://localhost:8000/mcp

Test it visually with the MCP Inspector:

just inspect          # npx @modelcontextprotocol/inspector uv run mcp101

Wire it into Claude Desktop (claude_desktop_config.json):

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

Google OAuth demo

This is the part worth understanding: it does not just log you in, it acts as you against Google. FastMCP's GoogleProvider is an OAuthProxy (Google has no Dynamic Client Registration), so FastMCP brokers the OAuth flow, encrypts and stores the upstream Google token server-side, and hands the MCP client only a reference token. Inside a tool, get_access_token().token gives you the real Google access token to call Google with — and the proxy refreshes it transparently.

1. Create Google credentials

  1. Google Cloud Console → APIs & Services → OAuth consent screen — configure it (External; add yourself as a test user while in "Testing" mode).

  2. APIs & Services → Credentials → Create Credentials → OAuth client ID → application type Web application.

  3. Add an Authorized redirect URI that matches exactly: http://localhost:8000/auth/callback.

  4. APIs & Services → Library → enable Google Calendar API.

  5. Copy the Client ID (...apps.googleusercontent.com) and Client Secret (GOCSPX-...).

2. Configure and run

cp .env.example .env       # then fill in the two Google values
set -a; source .env; set +a
just run-google            # or: uv run mcp101-google

Connect with the Inspector (HTTP transport, URL http://localhost:8000/mcp). It will bounce you through Google's consent screen, then whoami and list_calendars will work.

Scopes

Requested in google_server.py:

  • openid, .../auth/userinfo.email — identity

  • .../auth/calendar.readonly — read the user's calendars

Prefer .readonly scopes unless you need write access.

Gotchas

  • Restricted scopes (Gmail, Drive contents) need Google's app-verification review for non-test users. In "Testing" mode you are limited to explicitly added test users.

  • Tokens are per-user and in-memory in this demo. For production, configure persistent encrypted token storage and stable JWT signing keys, or tokens die on restart and everyone re-consents.

  • The redirect URI must match exactly between Google and BASE_URL — a trailing slash mismatch will fail the flow.

GitHub OAuth demo

The same OAuthProxy mechanics as the Google demo, but a GitHub OAuth App takes a minute to create and needs no verification review — so this is the fastest way to see real on-behalf-of-user API calls.

1. Create a GitHub OAuth App

  1. GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.

  2. Homepage URL: http://localhost:8000.

  3. Authorization callback URL (must match exactly): http://localhost:8000/auth/callback.

  4. Register, then Generate a new client secret. Copy the Client ID (Iv1...) and secret.

2. Configure and run

cp .env.example .env       # fill in the two GITHUB_ values
set -a; source .env; set +a
just run-github            # or: uv run mcp101-github

Connect with the Inspector (Streamable HTTP, URL http://localhost:8000/mcp). After the GitHub consent screen, whoami returns your profile and list_repos lists your repositories.

Requested scopes (in github_server.py): read:user, user:email, public_repo.

Docker

The image defaults to the basic server over streamable HTTP — sensible for a container / Kubernetes deployment (stdio only makes sense for local desktop clients).

docker build -t mcp101 .
docker run --rm -p 8000:8000 mcp101            # basic server on :8000/mcp

Run the Google demo instead by overriding the command and passing credentials:

docker run --rm -p 8000:8000 \
    -e GOOGLE_CLIENT_ID=...apps.googleusercontent.com \
    -e GOOGLE_CLIENT_SECRET=GOCSPX-... \
    -e BASE_URL=https://your-public-host \
    mcp101 mcp101-google

For Kubernetes, expose port 8000, set BASE_URL to the externally reachable URL, and remember the Google Authorized redirect URI must be <BASE_URL>/auth/callback. For anything beyond a demo, also configure persistent encrypted token storage and stable JWT signing keys (see the gotchas above).

CI

.github/workflows/ci.yaml runs on every push/PR to main:

  • ruff format --check and ruff check (lint)

  • an import smoke test for both servers

  • a docker build to validate the image

License

MIT

Available Tools

2 tools
addA

Add two numbers and return the sum.

Inputs: a (required), b (required). Returns: result

greetB

Build a greeting for name. Set formal for a more polite tone.

Inputs: name (required), formal. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 2 tools

    Disambiguation5/5

    The two tools serve entirely different purposes—arithmetic and greeting—so there is no risk of confusion or overlap.

    Naming Consistency5/5

    Both tool names are single verbs in imperative form, following a consistent and predictable pattern.

    Tool Count3/5

    With only 2 tools, the server is on the lower end of the appropriate range; it feels thin but not unreasonable for a small utility server.

    Completeness2/5

    The tools cover two unrelated capabilities with no clear domain, leaving significant gaps for any coherent purpose beyond these trivial operations.

    Related MCP Connectors

    Related MCP Servers