Overview
GroupMe MCP is a thin MCP server layered over the GroupMe API v3. It exposes GroupMe operations as MCP tools that any compatible AI client can call. The server is built with:- FastMCP from the
mcplibrary — handles tool registration and the MCP protocol - httpx — async HTTP client for all GroupMe API requests
- stdio transport — the server communicates with MCP clients via standard input/output
src/groupme_mcp/.
Source structure
client.py — GroupMeClient
GroupMeClient is an async HTTP client that wraps the GroupMe REST API.
Key details:
- Reads
GROUPME_TOKENfrom the environment at__init__time — the server will not start without it - Base URL is
https://api.groupme.com/v3 - Three private helpers —
_get,_post, and_delete— handle all HTTP communication. They inject the token as a query parameter (?token=...) on every request and callraise_for_status()so HTTP errors propagate as exceptions - All methods are
asyncand open a freshhttpx.AsyncClientper request
_get helper adds token to params and sends the request:
server.py — MCP tools
server.py defines all MCP tools using FastMCP’s @mcp.tool() decorator. A single GroupMeClient instance is created at module load time and shared across all tools.
Key details:
- Each tool is a thin shim — it validates or transforms arguments if needed, then delegates directly to the corresponding
GroupMeClientmethod - Tools that send messages (
send_message,send_direct_message) auto-generate asource_guidUUID when the caller omits it. This prevents the GroupMe API from rejecting duplicate sends if the tool is called twice with the same payload - Docstrings are required — FastMCP uses the docstring and
Args:block to generate the tool’s JSON schema exposed to MCP clients
main.py — Entry point
main.py is the entrypoint invoked by the groupme-mcp script (defined in pyproject.toml).
It performs one check before starting: if GROUPME_TOKEN is not set in the environment, it prints a helpful error message to stderr and exits with code 1. This prevents a confusing crash inside GroupMeClient.__init__.
server is intentionally deferred — it instantiates GroupMeClient, which reads GROUPME_TOKEN. Importing before the check would cause a KeyError instead of the friendly error message.
Transport
The server uses stdio transport (transport="stdio"). MCP clients such as Claude Desktop communicate with the server by spawning it as a subprocess and writing/reading JSON-RPC messages over stdin and stdout.
This is the standard transport for locally installed MCP servers. It requires no network port, no authentication beyond the process boundary, and works with any MCP-compatible client that supports subprocess execution.
Testing approach
Tests are intests/ and split into two files:
tests/test_client.py— tests at the HTTP level, verifying thatGroupMeClientmethods build the correct URLs and pass the correct parameterstests/test_server.py— tests at the tool level, verifying that tool functions call the correct client methods with the correct arguments
pytest-httpx (HTTPXMock) to intercept outgoing httpx requests at the transport level, so no real GroupMe API calls are made. asyncio_mode = "auto" is set in pyproject.toml, so async test functions run automatically without @pytest.mark.asyncio.
Both test files call
os.environ.setdefault("GROUPME_TOKEN", "test-token")
before importing anything from groupme_mcp. This must come first, because
GroupMeClient reads the token at __init__ time and the module is imported
at collection time.