pyxis-mcp 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,8 +5,8 @@
5
5
  <img alt="Pyxis — privacy-first product analytics" src="https://raw.githubusercontent.com/samuelcsantana/pyxis-mcp/main/.github/assets/cover-light.png" width="100%">
6
6
  </picture>
7
7
 
8
- **The MCP server of Pyxis: lets an AI agent read your dashboard's numbers, with a read-only token
9
- you can revoke at any time.**
8
+ **The MCP server of Pyxis: lets Claude and other AI agents read your dashboard's numbers, only
9
+ reading, through a connection or a token you can revoke at any time.**
10
10
 
11
11
  [![CI](https://github.com/samuelcsantana/pyxis-mcp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/samuelcsantana/pyxis-mcp/actions/workflows/ci.yml)
12
12
  [![Coverage](https://codecov.io/gh/samuelcsantana/pyxis-mcp/graph/badge.svg)](https://codecov.io/gh/samuelcsantana/pyxis-mcp)
@@ -25,7 +25,8 @@ you can revoke at any time.**
25
25
 
26
26
  > **Status:** on npm as `pyxis-mcp`, every release published from GitHub Actions with
27
27
  > provenance. The fifteen tools read the live Pyxis API. The package also exports them as a
28
- > library, which a host serves over HTTP with each caller's token.
28
+ > library, and the Pyxis API serves them at `https://api.pyxis-analytics.dev/mcp`, the remote
29
+ > connector that Claude and Claude Code sign in to with OAuth.
29
30
 
30
31
  ## Ecosystem
31
32
 
@@ -48,7 +49,10 @@ is no personal data to hand over: events carry no cookies, no addresses and no u
48
49
  ## Tools
49
50
 
50
51
  Every tool only reads, and maps to one `GET` route of the Pyxis API. Dates are `YYYY-MM-DD` in the
51
- project's time zone; `list_projects` gives each project's today, so call it first.
52
+ project's time zone; `list_projects` gives each project's today, so call it first. The server's
53
+ instructions tell the agent what an empty account means: with no project, one is created in the
54
+ dashboard; with a project that has received no event, the site does not run the tracker yet, and
55
+ [llms.txt](https://pyxis-analytics.dev/llms.txt) explains how to install it.
52
56
 
53
57
  | Tool | What it answers |
54
58
  | ------------------ | --------------------------------------------------------------------------------------------------------- |
@@ -75,6 +79,40 @@ unknown project, the rate limit and when to retry). When the API refuses a reque
75
79
  error carries the API's own code and reason, such as a suspended account; only a `5xx` asks to try
76
80
  again later.
77
81
 
82
+ ## Connect from Claude
83
+
84
+ The hosted Pyxis serves these tools at **`https://api.pyxis-analytics.dev/mcp`**. Claude connects
85
+ to it with no token and nothing to install: you sign in to Pyxis once and allow the connection.
86
+
87
+ **Claude** (on the web, the desktop app and the phone):
88
+
89
+ 1. In Claude, open Settings, then Connectors, and choose **Add custom connector**. In some versions
90
+ the menu is Customize, then Connectors.
91
+ 2. Name it `Pyxis` and paste `https://api.pyxis-analytics.dev/mcp`. Leave the advanced OAuth fields
92
+ empty.
93
+ 3. Choose **Connect**. A Pyxis page opens: sign in with the code sent to your e-mail, check the
94
+ app, where its data goes and your projects, and choose **Allow**.
95
+
96
+ **Claude Code:**
97
+
98
+ ```bash
99
+ claude mcp add --transport http pyxis https://api.pyxis-analytics.dev/mcp
100
+ ```
101
+
102
+ Then run `/mcp` in Claude Code, pick `pyxis` and choose **Authenticate**: the same Pyxis page
103
+ opens in the browser.
104
+
105
+ A connection only reads the numbers you see in the dashboard, from every project you can open. It
106
+ renews itself while it is used (each access token lasts an hour) and ends when you revoke it in the
107
+ dashboard, under Settings, Connected apps, or after 90 days without use; you keep up to ten. The
108
+ data Claude asks for goes from the Pyxis API to Anthropic, which handles it under its own terms.
109
+ Only Claude and Claude Code connect this way. A client that sends a header reaches the same address
110
+ with an access token instead:
111
+
112
+ ```bash
113
+ claude mcp add --transport http pyxis https://api.pyxis-analytics.dev/mcp --header "Authorization: Bearer pyxis_at_…"
114
+ ```
115
+
78
116
  ## Configuration
79
117
 
80
118
  | Variable | Required | Meaning |
@@ -85,10 +123,11 @@ again later.
85
123
  A missing or malformed value stops the server with a sentence on standard error that names the
86
124
  variable and never repeats its value.
87
125
 
88
- ## Use it
126
+ ## Run it on your computer
89
127
 
90
- Create a token in the dashboard (Settings, Access tokens), then add the server to your client. It
91
- runs with `npx`, so there is nothing to install beforehand (Node 22 or later).
128
+ To run the tools on your own computer, or against a self-hosted Pyxis, create a token in the
129
+ dashboard (Settings, Access tokens), then add the server to your client. It runs with `npx`, so
130
+ there is nothing to install beforehand (Node 22 or later).
92
131
 
93
132
  Claude Code:
94
133
 
@@ -163,8 +202,8 @@ over 64 KiB. What it leaves to the host: authenticating the caller (and the `401
163
202
  flowchart LR
164
203
  Agent["MCP client<br>(Claude Code, Cursor…)"] -- "stdio, JSON-RPC" --> Server["pyxis-mcp<br>(on the user's machine)"]
165
204
  Server -- "HTTPS GET<br>Authorization: Bearer pyxis_at_…" --> API["Pyxis API"]
166
- Remote["Remote MCP client"] -- "Streamable HTTP POST<br>with the caller's token" --> Host["A host's endpoint<br>handleHttpRequest"]
167
- Host -- "GET with the same token" --> API
205
+ Remote["Claude · Claude Code"] -- "Streamable HTTP POST<br>OAuth access token" --> Host["Pyxis API, /mcp<br>handleHttpRequest"]
206
+ Host -- "GET in process, same token" --> API
168
207
  API --> DB[("Events")]
169
208
  ```
170
209
 
@@ -172,7 +211,8 @@ The local server opens no port and keeps no state. The API decides what the toke
172
211
  exactly as it does for a dashboard session ([ADR 0002](docs/adr/0002-local-read-only-server.md)).
173
212
  A host runs the same tools for remote clients: it authenticates the caller, and the tools read with
174
213
  that caller's token, so the token never leaves the server that issued it
175
- ([ADR 0003](docs/adr/0003-stateless-http-handler-for-hosts.md)).
214
+ ([ADR 0003](docs/adr/0003-stateless-http-handler-for-hosts.md)). The Pyxis API is that host: it
215
+ signs Claude in with OAuth and runs the tools at `/mcp`.
176
216
 
177
217
  ## Tech stack
178
218
 
@@ -253,8 +293,8 @@ docs/adr/ architecture decision records
253
293
  - [x] Tools over the API's read routes, tested against its OpenAPI contract
254
294
  - [x] First release on npm, with provenance
255
295
  - [x] The tools as a library, served over stateless Streamable HTTP by a host
256
- - [ ] A remote endpoint in the Pyxis API, signed in with OAuth, for clients that only take URLs
257
- - [ ] Setup guides for Claude Code, Claude Desktop and Cursor
296
+ - [x] A remote endpoint in the Pyxis API, signed in with OAuth, for clients that only take URLs
297
+ - [x] Setup for Claude, Claude Code and clients that send a header
258
298
 
259
299
  ## Contributing and license
260
300
 
package/dist/server.d.ts CHANGED
@@ -3,7 +3,8 @@ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
3
3
  import type { PyxisApi } from './pyxis-api.js';
4
4
  import { type PyxisTool } from './tools.js';
5
5
  export declare const SERVER_NAME = "pyxis";
6
- export declare const SERVER_INSTRUCTIONS = "Pyxis is privacy-first product analytics: visits, page views, named events and the site's requests to its backend, with no cookies and no personal data. Start with list_projects for the project ids and each project's today, then ask for a range of dates. Every tool only reads.";
6
+ export declare const INSTALL_GUIDE_URL = "https://pyxis-analytics.dev/llms.txt";
7
+ export declare const SERVER_INSTRUCTIONS: string;
7
8
  export declare const UNEXPECTED_ANSWER = "The Pyxis API answered in a shape this server does not recognize. Update pyxis-mcp, or report it at https://github.com/samuelcsantana/pyxis-mcp/issues.";
8
9
  export interface ServerOptions {
9
10
  readonly version?: string;
package/dist/server.js CHANGED
@@ -2,7 +2,10 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { PYXIS_TOOLS, ToolInputError, VISITOR_TEXT_NOTE, } from './tools.js';
3
3
  import { PACKAGE_VERSION } from './version.js';
4
4
  export const SERVER_NAME = 'pyxis';
5
- export const SERVER_INSTRUCTIONS = "Pyxis is privacy-first product analytics: visits, page views, named events and the site's requests to its backend, with no cookies and no personal data. Start with list_projects for the project ids and each project's today, then ask for a range of dates. Every tool only reads.";
5
+ export const INSTALL_GUIDE_URL = 'https://pyxis-analytics.dev/llms.txt';
6
+ export const SERVER_INSTRUCTIONS = "Pyxis is privacy-first product analytics: visits, page views, named events and the site's requests to its backend, with no cookies and no personal data. Start with list_projects for the project ids and each project's today, then ask for a range of dates. Every tool only reads. " +
7
+ 'An empty list means no project was created yet, or none was shared with this person: projects are created in the Pyxis dashboard. ' +
8
+ `A project whose first_event_at is null has received nothing, so its site does not run the tracker yet. No tool here can install it; ${INSTALL_GUIDE_URL} explains how (the npm package pyxis-analytics, started with one of the public keys project_settings lists) and the rules an integration must follow.`;
6
9
  export const UNEXPECTED_ANSWER = 'The Pyxis API answered in a shape this server does not recognize. Update pyxis-mcp, or report it at https://github.com/samuelcsantana/pyxis-mcp/issues.';
7
10
  const READ_ONLY = {
8
11
  readOnlyHint: true,
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE,OAAO,EACL,WAAW,EAGX,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAEnC,MAAM,CAAC,MAAM,mBAAmB,GAC9B,uRAAuR,CAAC;AAE1R,MAAM,CAAC,MAAM,iBAAiB,GAC5B,yJAAyJ,CAAC;AAE5J,MAAM,SAAS,GAAG;IAChB,YAAY,EAAE,IAAI;IAClB,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,KAAK;CACZ,CAAC;AAOX,SAAS,OAAO,CAAC,OAAe;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED,SAAS,OAAO,CAAC,MAAe;IAC9B,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,EAAE,CAAC;aAC5E;SACF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,IAAe,EAAE,IAAa;IAC/C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;YACpC,OAAO,KAAK,CAAC,OAAO,CAAC;QACvB,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAe,EACf,GAAa,EACb,IAAa,EACb,GAAS;IAET,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3D,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC9C,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAa,EAAE,UAAyB,EAAE;IACrE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,eAAe,EAAE,EAClE,EAAE,YAAY,EAAE,mBAAmB,EAAE,CACtC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,CAAC,YAAY,CACjB,IAAI,CAAC,IAAI,EACT;YACE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,SAAS;SACvB,EACD,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAC1C,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyxis-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for Pyxis, privacy-first product analytics: lets an AI agent read your dashboard's numbers with a read-only token, locally over stdio or hosted over HTTP.",
5
5
  "keywords": [
6
6
  "mcp",