playmetrics_mcp
by sanjibani
README.md
# playmetrics_mcp
First MCP server for the **PlayMetrics** youth-sports club management platform. Read-only access to clubs, teams, players, schedule, registrations, and payments - so Claude, Cursor, or any MCP client can answer questions about your club's day-to-day operations.
MIT licensed. Built on the [`mcp-vertical-template`](https://github.com/sanjibani/mcp-vertical-template) standards: shared `httpx.AsyncClient` with pooling + transport retries, typed exception hierarchy, `respx` tests, structlog logging, `py.typed` marker, `ruff` full-rule-set, `mypy --strict`, JSONL audit log per tool call.
## Why this exists
PlayMetrics is the dominant youth-sports club management platform in the US - purpose-built for the messy workflows of running a club, league, or tournament (registrations, rosters, schedules, payments, communications). The platform just acquired SportsEngine (the AYSO / Pop Warner stack), which makes it the de-facto default for thousands of clubs.
But PlayMetrics does not publish a first-party developer API. Club directors spend hours each week pulling rosters, schedules, and registration status by hand - exactly the kind of work an AI agent should be able to do.
This server reverse-engineers the same `api.playmetrics.com` backend the web app uses, exposes it as MCP tools, and ships the engineering discipline B2B procurement teams expect (audit logs, isError-compliance, retry with backoff, structured errors).
## Features
13 tools across 6 resource groups:
| Group | Tools |
|-------|-------|
| Diagnostic | `health_check`, `get_current_role` |
| Clubs | `list_clubs`, `get_club` |
| Teams | `list_teams`, `get_team` |
| Players | `list_players`, `get_player` |
| Schedule / events | `list_events`, `get_event`, `get_schedule`, `list_upcoming_practices` |
| Registrations & payments | `list_registrations`, `get_registration`, `list_payments` |
All tools:
- **Raise** on failure (FastMCP sets `isError=true` on the wire - agents can't get stuck retrying).
- Write one **JSONL audit record** per call (stderr by default, or `PLAYMETRICS_AUDIT_LOG=/path/to/audit.jsonl`).
- Map HTTP failures to a typed exception hierarchy (`PlaymetricsAuthError`, `PlaymetricsNotFoundError`, `PlaymetricsRateLimitError`, `PlaymetricsAPIError`, `PlaymetricsConnectionError`).
- Retry transient 5xx / 429 with exponential backoff + full jitter, honoring `Retry-After`.
- Use a shared `httpx.AsyncClient` with connection pooling and 3 transport retries.
## Install
```bash
git clone https://github.com/sanjibani/playmetrics_mcp
cd playmetrics_mcp
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
```
## Auth setup (3 minutes)
PlayMetrics auth is **session-based against `api.playmetrics.com`**, not API-key-based. You need three things from your web session:
1. **Email + password** - your PlayMetrics login.
2. **`role_id`** - a query parameter PlayMetrics uses to scope a user to one of their many possible contexts (a director might be admin of one club and parent of another player in a different club, each with its own `role_id`).
To find your `role_id`:
1. Log in to https://app.playmetrics.com in your browser.
2. Open DevTools → Network tab.
3. Reload the page or click around the Schedule tab.
4. Find any request to `api.playmetrics.com` (e.g. `/schedule` or `/events`).
5. Copy the `role_id` query parameter value from the request URL.
Then set:
```bash
export PLAYMETRICS_USERNAME='you@example.com'
export PLAYMETRICS_PASSWORD='your-password'
export PLAYMETRICS_ROLE_ID='the-role-id-from-the-url'
playmetrics_mcp
```
(Or supply `PLAYMETRICS_SESSION_TOKEN=...` if you've already minted one via the `/login` endpoint.)
## Example agent prompts
- *"List the teams I'm coaching this season."* → `list_teams`
- *"What's the schedule for team X next two weeks?"* → `list_events(start=today, end=today+14d, team_id=X)`
- *"When's our next practice?"* → `list_upcoming_practices(team_id=X, days=14)`
- *"Show me registrations still pending payment."* → `list_registrations(status=pending)`
- *"Pull today's deposits for the treasurer."* → `list_payments(status=paid, ...)` + filter client-side
## Architecture notes
### Reverse-engineered API surface
PlayMetrics does not publish an API reference. The endpoints here were observed in the web app's network traffic and follow the convention: plural nouns, `role_id` scoped, kebab-case for compound paths (e.g. `/events/upcoming`).
**If PlayMetrics renames an endpoint, point the corresponding method in `client.py` at the new path** - the rest of the stack (auth, retry, audit, typed exceptions) is unaffected. PRs welcome.
### Login flow
`PlaymetricsClient.__init__` does a synchronous `POST /login` (using the sync `httpx` client - no event loop yet) if no `PLAYMETRICS_SESSION_TOKEN` is present, then reuses the resulting bearer token for every async call that follows. The password is held in memory for re-login, but never logged (the audit redactor strips any field named `password` or `PLAYMETRICS_PASSWORD`).
### isError-compliance
Every tool body is wrapped so that any `PlaymetricsError` propagates out and FastMCP wraps it as a `ToolError` on the wire response - setting `isError=true` so the agent sees a real failure rather than an error-shaped string. The Blackwell Systems audit (54 MCPs / 20 bugs) and MCPTox benchmark both flagged the opposite pattern as the most common MCP bug class; this server does not regress.
## Development
```bash
pytest # run all tests (no live API)
pytest -m "not integration" # default - no live API
ruff check src tests # lint
ruff format --check src tests
mypy src # type check (strict)
```
Tests use `respx` to intercept httpx calls - no live API traffic. The login flow is exercised in-process via respx mocks.
## Distribution
Distribution state file: `~/.mavis/state/vertical-mcp/dist/playmetrics-mcp.json`. Picked up daily by the `vertical-mcp-distribute` cron for auto-submission to awesome-mcp-servers, mcp.so, GitHub topics, etc.
## Sister MCPs in the same portfolio
- [`sanjibani/hawksoft-mcp`](https://github.com/sanjibani/hawksoft-mcp) - independent insurance agencies
- [`sanjibani/open-dental-mcp`](https://github.com/sanjibani/open-dental-mcp) - dental practices
- [`sanjibani/ezyvet-mcp`](https://github.com/sanjibani/ezyvet-mcp) - veterinary clinics
- [`sanjibani/jobber-mcp`](https://github.com/sanjibani/jobber-mcp) - home service businesses
- [`sanjibani/practicepanther-mcp`](https://github.com/sanjibani/practicepanther-mcp) - solo/small-firm legal
- [`sanjibani/realm-mp-mcp`](https://github.com/sanjibani/realm-mp-mcp) - church / nonprofit (Realm ACS, MinistryPlatform)
- [`sanjibani/fieldroutes-mcp`](https://github.com/sanjibani/fieldroutes-mcp) - pest control / lawn care
- [`sanjibani/cox-automotive-mcp`](https://github.com/sanjibani/cox-automotive-mcp) - auto dealerships
- [`sanjibani/qualia-mcp`](https://github.com/sanjibani/qualia-mcp) - title & escrow
- [`sanjibani/campspot-mcp`](https://github.com/sanjibani/campspot-mcp) - private campgrounds
- [`sanjibani/cleancloud-mcp`](https://github.com/sanjibani/cleancloud-mcp) - laundromat / dry cleaners
- **Custom MCP engagements**: https://sanjibani.github.io/mcp-services/
## License
MIT. See `LICENSE`. Author: Sanjibani Choudhury <schoudhury1991@gmail.com>.
## Disclaimer
This is an independent, community-built integration. It is **not** affiliated with, endorsed by, or supported by PlayMetrics, Inc. Use at your own risk - the underlying API is reverse-engineered and may change without notice.This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues