pyxis-mcp
README.md
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/samuelcsantana/pyxis-mcp/main/.github/assets/cover-dark.png">
<img alt="Pyxis — privacy-first product analytics" src="https://raw.githubusercontent.com/samuelcsantana/pyxis-mcp/main/.github/assets/cover-light.png" width="100%">
</picture>
**The MCP server of Pyxis: lets an AI agent read your dashboard's numbers, with a read-only token
you can revoke at any time.**
[](https://github.com/samuelcsantana/pyxis-mcp/actions/workflows/ci.yml)
[](https://codecov.io/gh/samuelcsantana/pyxis-mcp)
[](https://github.com/samuelcsantana/pyxis-mcp/actions/workflows/codeql.yml)
<br>
[](LICENSE)
[](tsconfig.json)
[](https://www.conventionalcommits.org)
[](https://www.npmjs.com/package/pyxis-mcp)
[](https://www.npmjs.com/package/pyxis-mcp#provenance)
**[Pyxis](https://pyxis-analytics.dev)** · **[Live demo](https://demo.pyxis-analytics.dev)** ·
**[API reference](https://samuelcsantana.github.io/pyxis-api/)**
</div>
> **Status:** on npm as `pyxis-mcp`, every release published from GitHub Actions with
> provenance. The fifteen tools read the live Pyxis API.
## Ecosystem
| Repository | Role |
| -------------------------------------------------------- | ------------------------------------------------------------- |
| [pyxis-api](https://github.com/samuelcsantana/pyxis-api) | Ingestion, dashboard sign-in and queries, erasure, retention |
| [pyxis-sdk](https://github.com/samuelcsantana/pyxis-sdk) | Browser tracker published to npm as `pyxis-analytics` |
| [pyxis-web](https://github.com/samuelcsantana/pyxis-web) | The dashboard: overview, funnels, features, requests |
| **pyxis-mcp** (this one) | MCP server that lets an AI agent read the dashboard's numbers |
## Why it exists
More and more questions about a product are asked to an agent instead of a dashboard: "how did
last week compare", "where does the sign-up funnel lose people", "which route started failing".
This server gives Claude Code, Claude Desktop, Cursor and other MCP clients the same numbers the
Pyxis dashboard shows, and nothing more: it uses an access token that only reads, created by an
admin in the dashboard's Settings and revocable there at any moment. Like the rest of Pyxis, there
is no personal data to hand over: events carry no cookies, no addresses and no user agents.
## Tools
Every tool only reads, and maps to one `GET` route of the Pyxis API. Dates are `YYYY-MM-DD` in the
project's time zone; `list_projects` gives each project's today, so call it first.
| Tool | What it answers |
| ------------------ | --------------------------------------------------------------------------------------------------------- |
| `list_projects` | The projects the token can read: id, name, time zone, today there, conversion event, first and last event |
| `overview` | Visits, identified users, conversions and failed writes against the previous period; top pages and events |
| `devices` | Visits and conversions by device type, browser, system, country and region |
| `acquisition` | Visits per day and channel, and the sources and campaigns behind them |
| `engagement` | Entry and exit pages, single-page visits, visit length |
| `time_of_day` | Visits by weekday and local hour |
| `features` | The most used named events or screens, day by day |
| `event_properties` | How the property values of one named event break down |
| `requests` | Calls to the site's backend per route: failures, statuses, durations, latest errors |
| `funnel` | How many visits or people reached each step, and the time between steps |
| `funnel_segments` | The same funnel per device type or channel |
| `funnel_subjects` | The visits or people who reached, or dropped at, one step |
| `visits` | The visits of a range, filtered by pages, events, channel, device, country, campaign, failed requests |
| `visit_timeline` | Every event of one visit, or every visit of one identified person |
| `project_settings` | Time zone, conversion event, allowed origins, retention, live public keys |
A result is JSON text, `{ "untrusted_visitor_text": "…", "report": … }`: the report is the API's
answer, and the first field reminds the agent that names, paths and values inside it were written by
the site's visitors. Problems come back as tool errors the agent can act on (a revoked token, an
unknown project, the rate limit and when to retry).
## Configuration
| Variable | Required | Meaning |
| --------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `PYXIS_TOKEN` | yes | A read-only access token (`pyxis_at_` and 32 letters and digits), created in the dashboard under Settings |
| `PYXIS_API_URL` | no | The Pyxis API to read, as an origin. Defaults to `https://api.pyxis-analytics.dev`; plain http only for localhost |
A missing or malformed value stops the server with a sentence on standard error that names the
variable and never repeats its value.
## Use it
Create a token in the dashboard (Settings, Access tokens), then add the server to your client. It
runs with `npx`, so there is nothing to install beforehand (Node 22 or later).
Claude Code:
```bash
claude mcp add pyxis -e PYXIS_TOKEN=pyxis_at_… -- npx -y pyxis-mcp
```
Claude Desktop (`claude_desktop_config.json`) and Cursor (`.cursor/mcp.json`):
```json
{
"mcpServers": {
"pyxis": {
"command": "npx",
"args": ["-y", "pyxis-mcp"],
"env": { "PYXIS_TOKEN": "pyxis_at_…" }
}
}
}
```
A self-hosted Pyxis adds `PYXIS_API_URL`. To run it from source instead, `npm ci && npm run build`
in a clone and use `node /path/to/pyxis-mcp/dist/cli.js` as the command. Keep the token out of
shared files and chats; revoke it in the dashboard the moment it leaks.
## Architecture
```mermaid
flowchart LR
Agent["MCP client<br>(Claude Code, Cursor…)"] -- "stdio, JSON-RPC" --> Server["pyxis-mcp<br>(on the user's machine)"]
Server -- "HTTPS GET<br>Authorization: Bearer pyxis_at_…" --> API["Pyxis API"]
API --> DB[("Events")]
```
The server opens no port and keeps no state. The API decides what the token may read, exactly as
it does for a dashboard session ([ADR 0002](docs/adr/0002-local-read-only-server.md)).
## Tech stack
TypeScript (strict) on Node 22 or later, ES modules. Vitest with V8 coverage, ESLint
(`typescript-eslint` strict, type-checked) and Prettier, Husky and commitlint, GitHub Actions with
CodeQL, Dependabot and release-please.
## Getting started
```bash
npm ci
npm run lint && npm run typecheck && npm run test:cov && npm run build
```
## Testing
- `npm run test:cov` runs the unit tests in Node and fails below **100%** of statements, branches,
functions and lines. The server is tested with a real MCP client over an in-memory transport and
a fake `fetch`.
- One file is left out of the unit coverage: `src/cli.ts`, which only wires the modules to the
standard input and output. `npm run test:e2e` covers it: after `npm run build`, a real MCP client
starts `dist/cli.js`, lists the tools and calls them against a local fake of the API, checks that
standard output carries nothing but the protocol, and that a bad configuration stops the process
with the reason on standard error.
- `src/contract.test.ts` checks the tools against `contract/openapi.json`, the API's own contract
(`npm run contract:sync` copies it from pyxis-api): every route a token may read has a tool, and
every tool sends only declared parameters, with values inside their enums.
- `npm run test:tooling` tests the lint rule and the scripts.
- CI runs the unit tests on Node 24 and again on Node 22, the oldest version the package supports.
## Project structure
```
src/
├── config.ts reads PYXIS_TOKEN and PYXIS_API_URL
├── pyxis-api.ts one GET to the API, failures as sentences
├── tools.ts the fifteen tools: input schemas, routes, requests
├── server.ts registers the tools on the MCP server
├── version.ts the package version, for the server and the user agent
└── cli.ts the executable: stdio transport
e2e/ the built server driven by a real MCP client
contract/ the API's OpenAPI contract, copied from pyxis-api
eslint-rules/ the local rule that forbids comments
scripts/ the comment check and the contract sync
docs/adr/ architecture decision records
```
## Privacy and security
- Read-only: the API accepts the token only on its read routes; it cannot write, sign in, manage
sessions or create tokens.
- The token only travels in the `Authorization` header to the configured API, over https (plain
http only to localhost), and never reaches a log, an error message or a tool result.
- Text written by a site's visitors is returned as data, and each tool description says it is not
an instruction.
- Vulnerabilities: see [SECURITY.md](SECURITY.md).
## Architecture decisions
| ADR | Decision |
| ------------------------------------------------------ | ------------------------------------------------------------ |
| [0001](docs/adr/0001-record-architecture-decisions.md) | Record architecture decisions |
| [0002](docs/adr/0002-local-read-only-server.md) | A local, read-only server that uses the admin's access token |
## Roadmap
- [x] Repository skeleton, quality gates, configuration reader
- [x] Tools over the API's read routes, tested against its OpenAPI contract
- [x] First release on npm, with provenance
- [ ] Setup guides for Claude Code, Claude Desktop and Cursor
## Contributing and license
See [CONTRIBUTING.md](CONTRIBUTING.md) and the [Code of Conduct](CODE_OF_CONDUCT.md). MIT licensed:
see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues