mcp101
A sample MCP server built with FastMCP that demonstrates basic tools and a Google OAuth integration for accessing Google APIs like Calendar.
README
mcp101
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. |
Requirements
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
- Google Cloud Console → APIs & Services → OAuth consent screen — configure it (External; add yourself as a test user while in "Testing" mode).
- APIs & Services → Credentials → Create Credentials → OAuth client ID → application type Web application.
- Add an Authorized redirect URI that matches exactly:
http://localhost:8000/auth/callback. - APIs & Services → Library → enable Google Calendar API.
- 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
just run-google # or: uv run mcp101-google — .env is auto-loaded
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
- GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
- Homepage URL:
http://localhost:8000. - Authorization callback URL (must match exactly):
http://localhost:8000/auth/callback. - 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
just run-github # or: uv run mcp101-github — .env is auto-loaded
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.
OAuth token persistence & troubleshooting
By default FastMCP keeps issued tokens and its JWT signing key in memory, so
every server restart invalidates the tokens your MCP client already holds — the
client then keeps presenting a stale token and falls into a 401 invalid_token /
refresh loop.
To avoid that, the Google and GitHub servers persist OAuth state under .mcp101/
(gitignored): a stable signing key (.mcp101/jwt_signing_key) and a disk-backed
client/token store. You can now restart the server without re-authenticating.
If you still hit 401 invalid_token (e.g. after changing credentials or scopes),
do a clean reset:
rm -rf .mcp101 # wipe server-side OAuth state (key + tokens)
Then clear the client too — in the MCP Inspector, the surest way is to open it in a
fresh incognito window (its OAuth tokens live in browser storage). Restart the
server, connect again, and you should get a full fresh flow (/register →
/authorize → /token) instead of a rotated refresh.
Set JWT_SIGNING_KEY in .env to pin the key explicitly (recommended for any
shared/non-local deployment).
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 --checkandruff check(lint)- an import smoke test for both servers
- a
docker buildto validate the image
License
MIT
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.