pyxis-mcp 0.1.0 → 0.2.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 +75 -10
- package/dist/cli.js +1 -3
- package/dist/cli.js.map +1 -1
- package/dist/http.d.ts +12 -0
- package/dist/http.js +44 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/pyxis-api.d.ts +15 -3
- package/dist/pyxis-api.js +29 -17
- package/dist/pyxis-api.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +3 -2
- package/dist/server.js.map +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ you can revoke at any time.**
|
|
|
15
15
|
[](LICENSE)
|
|
16
16
|
[](tsconfig.json)
|
|
17
17
|
[](https://www.conventionalcommits.org)
|
|
18
|
+
[](https://www.npmjs.com/package/pyxis-mcp)
|
|
19
|
+
[](https://www.npmjs.com/package/pyxis-mcp#provenance)
|
|
18
20
|
|
|
19
21
|
**[Pyxis](https://pyxis-analytics.dev)** · **[Live demo](https://demo.pyxis-analytics.dev)** ·
|
|
20
22
|
**[API reference](https://samuelcsantana.github.io/pyxis-api/)**
|
|
@@ -22,7 +24,8 @@ you can revoke at any time.**
|
|
|
22
24
|
</div>
|
|
23
25
|
|
|
24
26
|
> **Status:** on npm as `pyxis-mcp`, every release published from GitHub Actions with
|
|
25
|
-
> provenance. The fifteen tools read the live Pyxis API.
|
|
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.
|
|
26
29
|
|
|
27
30
|
## Ecosystem
|
|
28
31
|
|
|
@@ -68,7 +71,9 @@ project's time zone; `list_projects` gives each project's today, so call it firs
|
|
|
68
71
|
A result is JSON text, `{ "untrusted_visitor_text": "…", "report": … }`: the report is the API's
|
|
69
72
|
answer, and the first field reminds the agent that names, paths and values inside it were written by
|
|
70
73
|
the site's visitors. Problems come back as tool errors the agent can act on (a revoked token, an
|
|
71
|
-
unknown project, the rate limit and when to retry).
|
|
74
|
+
unknown project, the rate limit and when to retry). When the API refuses a request (any `4xx`), the
|
|
75
|
+
error carries the API's own code and reason, such as a suspended account; only a `5xx` asks to try
|
|
76
|
+
again later.
|
|
72
77
|
|
|
73
78
|
## Configuration
|
|
74
79
|
|
|
@@ -109,17 +114,65 @@ A self-hosted Pyxis adds `PYXIS_API_URL`. To run it from source instead, `npm ci
|
|
|
109
114
|
in a clone and use `node /path/to/pyxis-mcp/dist/cli.js` as the command. Keep the token out of
|
|
110
115
|
shared files and chats; revoke it in the dashboard the moment it leaks.
|
|
111
116
|
|
|
117
|
+
## Use it as a library
|
|
118
|
+
|
|
119
|
+
Clients that only accept remote servers need the tools at a URL. The package exports them so a
|
|
120
|
+
host can serve them from its own endpoint, over Streamable HTTP, with the token of whoever is
|
|
121
|
+
calling. It is an ES module that a CommonJS host can also `require()` (Node 22.12 or later).
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { handleHttpRequest, PyxisApi } from 'pyxis-mcp';
|
|
125
|
+
|
|
126
|
+
export function answerMcp(request: Request, callerToken: string): Promise<Response> {
|
|
127
|
+
const api = new PyxisApi({
|
|
128
|
+
token: callerToken,
|
|
129
|
+
apiUrl: 'https://api.pyxis-analytics.dev',
|
|
130
|
+
problemTexts: {
|
|
131
|
+
refusedToken: 'Pyxis refused this connection: connect Pyxis again in your MCP client.',
|
|
132
|
+
unreachableAdvice: 'Try again in a moment.',
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
return handleHttpRequest(request, api);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
| Export | What it does |
|
|
140
|
+
| ------------------------------------------- | --------------------------------------------------------------------- |
|
|
141
|
+
| `handleHttpRequest(request, api, options?)` | Answers one MCP exchange: a web `Request` in, a web `Response` out |
|
|
142
|
+
| `createServer(api, options?)` | The MCP server with the fifteen tools, for a transport of your choice |
|
|
143
|
+
| `new PyxisApi(options)` | The client the tools read through, one per caller |
|
|
144
|
+
|
|
145
|
+
`PyxisApi` options: `token` and `apiUrl` (required); `userAgent` (default `pyxis-mcp/<version>`);
|
|
146
|
+
`fetch` (default the global one; a host can pass one that answers inside its own process);
|
|
147
|
+
`timeoutMs` (default 15 seconds); `problemTexts`, the two sentences that depend on where the server
|
|
148
|
+
runs: `refusedToken` and `unreachableAdvice` (default: the local server's, which name
|
|
149
|
+
`PYXIS_TOKEN` and `PYXIS_API_URL`). `createServer` takes `version` (default the package's) and
|
|
150
|
+
`now` (the clock); `handleHttpRequest` takes those and `allowedOrigins`.
|
|
151
|
+
|
|
152
|
+
What the handler does: each request gets a server and a transport of its own, closed once it is
|
|
153
|
+
answered, so there is no session id and no event stream, only JSON answers. It answers `405` with
|
|
154
|
+
`Allow: POST` to any other method, `403` to a request with an `Origin` header unless that origin is
|
|
155
|
+
in `allowedOrigins` (none by default: MCP clients do not call from web pages), and `413` to a body
|
|
156
|
+
over 64 KiB. What it leaves to the host: authenticating the caller (and the `401` with
|
|
157
|
+
`WWW-Authenticate` that starts a client's sign-in), rate limits and the host name
|
|
158
|
+
([ADR 0003](docs/adr/0003-stateless-http-handler-for-hosts.md)).
|
|
159
|
+
|
|
112
160
|
## Architecture
|
|
113
161
|
|
|
114
162
|
```mermaid
|
|
115
163
|
flowchart LR
|
|
116
164
|
Agent["MCP client<br>(Claude Code, Cursor…)"] -- "stdio, JSON-RPC" --> Server["pyxis-mcp<br>(on the user's machine)"]
|
|
117
165
|
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
|
|
118
168
|
API --> DB[("Events")]
|
|
119
169
|
```
|
|
120
170
|
|
|
121
|
-
The server opens no port and keeps no state. The API decides what the token may read,
|
|
122
|
-
it does for a dashboard session ([ADR 0002](docs/adr/0002-local-read-only-server.md)).
|
|
171
|
+
The local server opens no port and keeps no state. The API decides what the token may read,
|
|
172
|
+
exactly as it does for a dashboard session ([ADR 0002](docs/adr/0002-local-read-only-server.md)).
|
|
173
|
+
A host runs the same tools for remote clients: it authenticates the caller, and the tools read with
|
|
174
|
+
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)).
|
|
123
176
|
|
|
124
177
|
## Tech stack
|
|
125
178
|
|
|
@@ -138,12 +191,17 @@ npm run lint && npm run typecheck && npm run test:cov && npm run build
|
|
|
138
191
|
|
|
139
192
|
- `npm run test:cov` runs the unit tests in Node and fails below **100%** of statements, branches,
|
|
140
193
|
functions and lines. The server is tested with a real MCP client over an in-memory transport and
|
|
141
|
-
a fake `fetch
|
|
194
|
+
a fake `fetch`; the HTTP handler with web `Request` objects, from `initialize` to a tool call,
|
|
195
|
+
and every refusal.
|
|
142
196
|
- One file is left out of the unit coverage: `src/cli.ts`, which only wires the modules to the
|
|
143
197
|
standard input and output. `npm run test:e2e` covers it: after `npm run build`, a real MCP client
|
|
144
198
|
starts `dist/cli.js`, lists the tools and calls them against a local fake of the API, checks that
|
|
145
199
|
standard output carries nothing but the protocol, and that a bad configuration stops the process
|
|
146
200
|
with the reason on standard error.
|
|
201
|
+
- `npm run test:e2e` also starts `e2e/http-host.ts`, a small HTTP host that imports `pyxis-mcp` by
|
|
202
|
+
its name, and drives it with the SDK's Streamable HTTP client: tools against the fake API with
|
|
203
|
+
the caller's token, no session, the host's own words for a refused token, `405` and `403`. The
|
|
204
|
+
package is also loaded by name with `import` and with `require`.
|
|
147
205
|
- `src/contract.test.ts` checks the tools against `contract/openapi.json`, the API's own contract
|
|
148
206
|
(`npm run contract:sync` copies it from pyxis-api): every route a token may read has a tool, and
|
|
149
207
|
every tool sends only declared parameters, with values inside their enums.
|
|
@@ -158,9 +216,11 @@ src/
|
|
|
158
216
|
├── pyxis-api.ts one GET to the API, failures as sentences
|
|
159
217
|
├── tools.ts the fifteen tools: input schemas, routes, requests
|
|
160
218
|
├── server.ts registers the tools on the MCP server
|
|
219
|
+
├── http.ts one stateless Streamable HTTP exchange
|
|
220
|
+
├── index.ts the library's public exports
|
|
161
221
|
├── version.ts the package version, for the server and the user agent
|
|
162
222
|
└── cli.ts the executable: stdio transport
|
|
163
|
-
e2e/ the built
|
|
223
|
+
e2e/ the built package driven by real MCP clients, over stdio and over HTTP
|
|
164
224
|
contract/ the API's OpenAPI contract, copied from pyxis-api
|
|
165
225
|
eslint-rules/ the local rule that forbids comments
|
|
166
226
|
scripts/ the comment check and the contract sync
|
|
@@ -175,20 +235,25 @@ docs/adr/ architecture decision records
|
|
|
175
235
|
http only to localhost), and never reaches a log, an error message or a tool result.
|
|
176
236
|
- Text written by a site's visitors is returned as data, and each tool description says it is not
|
|
177
237
|
an instruction.
|
|
238
|
+
- Over HTTP, the handler keeps no session, refuses requests sent by web pages and bodies over 64
|
|
239
|
+
KiB, and reads with the token of the caller its host authenticated.
|
|
178
240
|
- Vulnerabilities: see [SECURITY.md](SECURITY.md).
|
|
179
241
|
|
|
180
242
|
## Architecture decisions
|
|
181
243
|
|
|
182
|
-
| ADR
|
|
183
|
-
|
|
|
184
|
-
| [0001](docs/adr/0001-record-architecture-decisions.md)
|
|
185
|
-
| [0002](docs/adr/0002-local-read-only-server.md)
|
|
244
|
+
| ADR | Decision |
|
|
245
|
+
| --------------------------------------------------------- | ----------------------------------------------------------------- |
|
|
246
|
+
| [0001](docs/adr/0001-record-architecture-decisions.md) | Record architecture decisions |
|
|
247
|
+
| [0002](docs/adr/0002-local-read-only-server.md) | A local, read-only server that uses the admin's access token |
|
|
248
|
+
| [0003](docs/adr/0003-stateless-http-handler-for-hosts.md) | A stateless HTTP handler that a host runs with the caller's token |
|
|
186
249
|
|
|
187
250
|
## Roadmap
|
|
188
251
|
|
|
189
252
|
- [x] Repository skeleton, quality gates, configuration reader
|
|
190
253
|
- [x] Tools over the API's read routes, tested against its OpenAPI contract
|
|
191
254
|
- [x] First release on npm, with provenance
|
|
255
|
+
- [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
|
|
192
257
|
- [ ] Setup guides for Claude Code, Claude Desktop and Cursor
|
|
193
258
|
|
|
194
259
|
## Contributing and license
|
package/dist/cli.js
CHANGED
|
@@ -3,11 +3,9 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
3
3
|
import { ConfigError, readConfig } from './config.js';
|
|
4
4
|
import { PyxisApi } from './pyxis-api.js';
|
|
5
5
|
import { createServer } from './server.js';
|
|
6
|
-
import { PACKAGE_VERSION, userAgent } from './version.js';
|
|
7
6
|
const EXIT_FAILURE = 1;
|
|
8
7
|
async function start() {
|
|
9
|
-
const
|
|
10
|
-
const server = createServer(new PyxisApi(config, userAgent()), { version: PACKAGE_VERSION });
|
|
8
|
+
const server = createServer(new PyxisApi(readConfig(process.env)));
|
|
11
9
|
await server.connect(new StdioServerTransport());
|
|
12
10
|
}
|
|
13
11
|
start().catch((error) => {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,KAAK,UAAU,KAAK;IAClB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC/B,MAAM,MAAM,GACV,KAAK,YAAY,WAAW;QAC1B,CAAC,CAAC,KAAK,CAAC,OAAO;QACf,CAAC,CAAC,oBAAoB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IACvG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,MAAM,IAAI,CAAC,CAAC;IAC/C,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC;AAClC,CAAC,CAAC,CAAC"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PyxisApi } from './pyxis-api.js';
|
|
2
|
+
import { type ServerOptions } from './server.js';
|
|
3
|
+
export declare const MAX_REQUEST_BYTES: number;
|
|
4
|
+
export declare const ALLOWED_METHOD = "POST";
|
|
5
|
+
export declare const HTTP_REFUSALS: {
|
|
6
|
+
readonly method: "Method not allowed: this server keeps no session and answers POST only.";
|
|
7
|
+
readonly origin: "Forbidden: this server does not answer requests sent by a web page.";
|
|
8
|
+
};
|
|
9
|
+
export interface HttpOptions extends ServerOptions {
|
|
10
|
+
readonly allowedOrigins?: readonly string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function handleHttpRequest(request: Request, api: PyxisApi, options?: HttpOptions): Promise<Response>;
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
2
|
+
import { createServer } from './server.js';
|
|
3
|
+
export const MAX_REQUEST_BYTES = 64 * 1024;
|
|
4
|
+
export const ALLOWED_METHOD = 'POST';
|
|
5
|
+
export const HTTP_REFUSALS = {
|
|
6
|
+
method: 'Method not allowed: this server keeps no session and answers POST only.',
|
|
7
|
+
origin: 'Forbidden: this server does not answer requests sent by a web page.',
|
|
8
|
+
};
|
|
9
|
+
const STATUS_FORBIDDEN = 403;
|
|
10
|
+
const STATUS_METHOD_NOT_ALLOWED = 405;
|
|
11
|
+
const JSON_RPC_SERVER_ERROR = -32000;
|
|
12
|
+
const JSON_MEDIA_TYPE = 'application/json';
|
|
13
|
+
function refusal(status, message, headers = {}) {
|
|
14
|
+
return new Response(JSON.stringify({
|
|
15
|
+
jsonrpc: '2.0',
|
|
16
|
+
error: { code: JSON_RPC_SERVER_ERROR, message },
|
|
17
|
+
id: null,
|
|
18
|
+
}), { status, headers: { 'content-type': JSON_MEDIA_TYPE, ...headers } });
|
|
19
|
+
}
|
|
20
|
+
function comesFromForeignPage(request, allowedOrigins) {
|
|
21
|
+
const origin = request.headers.get('origin');
|
|
22
|
+
return origin !== null && !allowedOrigins.includes(origin);
|
|
23
|
+
}
|
|
24
|
+
export async function handleHttpRequest(request, api, options = {}) {
|
|
25
|
+
if (request.method !== ALLOWED_METHOD) {
|
|
26
|
+
return refusal(STATUS_METHOD_NOT_ALLOWED, HTTP_REFUSALS.method, { allow: ALLOWED_METHOD });
|
|
27
|
+
}
|
|
28
|
+
if (comesFromForeignPage(request, options.allowedOrigins ?? [])) {
|
|
29
|
+
return refusal(STATUS_FORBIDDEN, HTTP_REFUSALS.origin);
|
|
30
|
+
}
|
|
31
|
+
const server = createServer(api, options);
|
|
32
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
33
|
+
enableJsonResponse: true,
|
|
34
|
+
maxRequestBodySize: MAX_REQUEST_BYTES,
|
|
35
|
+
});
|
|
36
|
+
await server.connect(transport);
|
|
37
|
+
try {
|
|
38
|
+
return await transport.handleRequest(request);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await server.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAC;AAEzH,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAE/D,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC3C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AAErC,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,MAAM,EAAE,yEAAyE;IACjF,MAAM,EAAE,qEAAqE;CACrE,CAAC;AAEX,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,yBAAyB,GAAG,GAAG,CAAC;AACtC,MAAM,qBAAqB,GAAG,CAAC,KAAK,CAAC;AACrC,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAM3C,SAAS,OAAO,CAAC,MAAc,EAAE,OAAe,EAAE,UAAkC,EAAE;IACpF,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;QACb,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE;QAC/C,EAAE,EAAE,IAAI;KACT,CAAC,EACF,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,eAAe,EAAE,GAAG,OAAO,EAAE,EAAE,CACrE,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,OAAgB,EAAE,cAAiC;IAC/E,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,OAAO,MAAM,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAgB,EAChB,GAAa,EACb,UAAuB,EAAE;IAEzB,IAAI,OAAO,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACtC,OAAO,OAAO,CAAC,yBAAyB,EAAE,aAAa,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC,EAAE,CAAC;QAChE,OAAO,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,IAAI,wCAAwC,CAAC;QAC7D,kBAAkB,EAAE,IAAI;QACxB,kBAAkB,EAAE,iBAAiB;KACtC,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAoB,MAAM,WAAW,CAAC;AAChE,OAAO,EACL,QAAQ,GAOT,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC"}
|
package/dist/pyxis-api.d.ts
CHANGED
|
@@ -11,18 +11,30 @@ export type ApiOutcome = {
|
|
|
11
11
|
readonly problem: string;
|
|
12
12
|
};
|
|
13
13
|
export type Fetch = (url: string, init: RequestInit) => Promise<Response>;
|
|
14
|
+
export interface ProblemTexts {
|
|
15
|
+
readonly refusedToken: string;
|
|
16
|
+
readonly unreachableAdvice: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const LOCAL_PROBLEM_TEXTS: ProblemTexts;
|
|
19
|
+
export interface PyxisApiOptions extends PyxisConfig {
|
|
20
|
+
readonly userAgent?: string;
|
|
21
|
+
readonly fetch?: Fetch;
|
|
22
|
+
readonly timeoutMs?: number;
|
|
23
|
+
readonly problemTexts?: ProblemTexts;
|
|
24
|
+
}
|
|
14
25
|
export declare const PROBLEMS: {
|
|
15
|
-
readonly refusedToken: "PYXIS_TOKEN was refused: it may be revoked or expired. Create a new access token in the Pyxis dashboard, under Settings, Access tokens.";
|
|
16
26
|
readonly notFound: "Not found: no project with this id among the projects this token can read. Call list_projects for the ids.";
|
|
17
27
|
readonly rejected: "The Pyxis API refused the request.";
|
|
18
28
|
readonly notJson: "The Pyxis API answered with something that is not JSON.";
|
|
19
29
|
};
|
|
20
30
|
export declare function requestUrl(apiUrl: string, path: string, query: Query): string;
|
|
21
31
|
export declare class PyxisApi {
|
|
22
|
-
private readonly
|
|
32
|
+
private readonly token;
|
|
33
|
+
private readonly apiUrl;
|
|
23
34
|
private readonly userAgent;
|
|
24
35
|
private readonly fetch;
|
|
25
36
|
private readonly timeoutMs;
|
|
26
|
-
|
|
37
|
+
private readonly problemTexts;
|
|
38
|
+
constructor(options: PyxisApiOptions);
|
|
27
39
|
get(path: string, query?: Query): Promise<ApiOutcome>;
|
|
28
40
|
}
|
package/dist/pyxis-api.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
import { userAgent } from './version.js';
|
|
1
2
|
export const REQUEST_TIMEOUT_MS = 15_000;
|
|
2
3
|
export const REQUESTS_PER_MINUTE = 60;
|
|
3
|
-
const STATUS_BAD_REQUEST = 400;
|
|
4
4
|
const STATUS_UNAUTHORIZED = 401;
|
|
5
5
|
const STATUS_NOT_FOUND = 404;
|
|
6
6
|
const STATUS_TOO_MANY_REQUESTS = 429;
|
|
7
|
+
const FIRST_CLIENT_ERROR = 400;
|
|
8
|
+
const FIRST_SERVER_ERROR = 500;
|
|
7
9
|
const RETRY_AFTER_HEADER = 'retry-after';
|
|
8
10
|
const JSON_MEDIA_TYPE = 'application/json';
|
|
9
|
-
export const
|
|
11
|
+
export const LOCAL_PROBLEM_TEXTS = {
|
|
10
12
|
refusedToken: 'PYXIS_TOKEN was refused: it may be revoked or expired. Create a new access token in the Pyxis dashboard, under Settings, Access tokens.',
|
|
13
|
+
unreachableAdvice: 'Check PYXIS_API_URL and the network, then try again.',
|
|
14
|
+
};
|
|
15
|
+
export const PROBLEMS = {
|
|
11
16
|
notFound: 'Not found: no project with this id among the projects this token can read. Call list_projects for the ids.',
|
|
12
17
|
rejected: 'The Pyxis API refused the request.',
|
|
13
18
|
notJson: 'The Pyxis API answered with something that is not JSON.',
|
|
@@ -56,37 +61,44 @@ function rateLimitedProblem(response) {
|
|
|
56
61
|
: 'in a minute';
|
|
57
62
|
return `Too many requests: each access token may make ${String(REQUESTS_PER_MINUTE)} requests a minute. Try again ${wait}.`;
|
|
58
63
|
}
|
|
59
|
-
|
|
64
|
+
function isRefusal(status) {
|
|
65
|
+
return status >= FIRST_CLIENT_ERROR && status < FIRST_SERVER_ERROR;
|
|
66
|
+
}
|
|
67
|
+
async function problemOf(response, texts) {
|
|
60
68
|
switch (response.status) {
|
|
61
69
|
case STATUS_UNAUTHORIZED:
|
|
62
|
-
return
|
|
70
|
+
return texts.refusedToken;
|
|
63
71
|
case STATUS_NOT_FOUND:
|
|
64
72
|
return PROBLEMS.notFound;
|
|
65
73
|
case STATUS_TOO_MANY_REQUESTS:
|
|
66
74
|
return rateLimitedProblem(response);
|
|
67
|
-
case STATUS_BAD_REQUEST:
|
|
68
|
-
return rejectedProblem(response);
|
|
69
75
|
default:
|
|
70
|
-
return
|
|
76
|
+
return isRefusal(response.status)
|
|
77
|
+
? rejectedProblem(response)
|
|
78
|
+
: `The Pyxis API answered ${String(response.status)}. Try again later.`;
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
export class PyxisApi {
|
|
74
|
-
|
|
82
|
+
token;
|
|
83
|
+
apiUrl;
|
|
75
84
|
userAgent;
|
|
76
85
|
fetch;
|
|
77
86
|
timeoutMs;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
87
|
+
problemTexts;
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.token = options.token;
|
|
90
|
+
this.apiUrl = options.apiUrl;
|
|
91
|
+
this.userAgent = options.userAgent ?? userAgent();
|
|
92
|
+
this.fetch = options.fetch ?? globalThis.fetch;
|
|
93
|
+
this.timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
94
|
+
this.problemTexts = options.problemTexts ?? LOCAL_PROBLEM_TEXTS;
|
|
83
95
|
}
|
|
84
96
|
async get(path, query = {}) {
|
|
85
97
|
let response;
|
|
86
98
|
try {
|
|
87
|
-
response = await this.fetch(requestUrl(this.
|
|
99
|
+
response = await this.fetch(requestUrl(this.apiUrl, path, query), {
|
|
88
100
|
headers: {
|
|
89
|
-
authorization: `Bearer ${this.
|
|
101
|
+
authorization: `Bearer ${this.token}`,
|
|
90
102
|
accept: JSON_MEDIA_TYPE,
|
|
91
103
|
'user-agent': this.userAgent,
|
|
92
104
|
},
|
|
@@ -97,11 +109,11 @@ export class PyxisApi {
|
|
|
97
109
|
const reason = error instanceof Error ? error.name : 'Error';
|
|
98
110
|
return {
|
|
99
111
|
ok: false,
|
|
100
|
-
problem: `The Pyxis API at ${this.
|
|
112
|
+
problem: `The Pyxis API at ${this.apiUrl} did not answer (${reason}). ${this.problemTexts.unreachableAdvice}`,
|
|
101
113
|
};
|
|
102
114
|
}
|
|
103
115
|
if (!response.ok) {
|
|
104
|
-
return { ok: false, problem: await problemOf(response) };
|
|
116
|
+
return { ok: false, problem: await problemOf(response, this.problemTexts) };
|
|
105
117
|
}
|
|
106
118
|
const body = await readJson(response);
|
|
107
119
|
return body === undefined ? { ok: false, problem: PROBLEMS.notJson } : { ok: true, body };
|
package/dist/pyxis-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pyxis-api.js","sourceRoot":"","sources":["../src/pyxis-api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pyxis-api.js","sourceRoot":"","sources":["../src/pyxis-api.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export declare const SERVER_NAME = "pyxis";
|
|
|
6
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.";
|
|
7
7
|
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
8
|
export interface ServerOptions {
|
|
9
|
-
readonly version
|
|
9
|
+
readonly version?: string;
|
|
10
10
|
readonly now?: () => Date;
|
|
11
11
|
}
|
|
12
12
|
export declare function runTool(tool: PyxisTool, api: PyxisApi, args: unknown, now: Date): Promise<CallToolResult>;
|
|
13
|
-
export declare function createServer(api: PyxisApi, options
|
|
13
|
+
export declare function createServer(api: PyxisApi, options?: ServerOptions): McpServer;
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { PYXIS_TOOLS, ToolInputError, VISITOR_TEXT_NOTE, } from './tools.js';
|
|
3
|
+
import { PACKAGE_VERSION } from './version.js';
|
|
3
4
|
export const SERVER_NAME = 'pyxis';
|
|
4
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
6
|
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.';
|
|
@@ -45,9 +46,9 @@ export async function runTool(tool, api, args, now) {
|
|
|
45
46
|
const report = tool.report(outcome.body, now);
|
|
46
47
|
return report === undefined ? failure(UNEXPECTED_ANSWER) : success(report);
|
|
47
48
|
}
|
|
48
|
-
export function createServer(api, options) {
|
|
49
|
+
export function createServer(api, options = {}) {
|
|
49
50
|
const now = options.now ?? (() => new Date());
|
|
50
|
-
const server = new McpServer({ name: SERVER_NAME, version: options.version }, { instructions: SERVER_INSTRUCTIONS });
|
|
51
|
+
const server = new McpServer({ name: SERVER_NAME, version: options.version ?? PACKAGE_VERSION }, { instructions: SERVER_INSTRUCTIONS });
|
|
51
52
|
for (const tool of PYXIS_TOOLS) {
|
|
52
53
|
server.registerTool(tool.name, {
|
|
53
54
|
title: tool.title,
|
package/dist/server.js.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pyxis-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Pyxis, privacy-first product analytics: lets an AI agent read your dashboard's numbers with a read-only token.",
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|
|
7
7
|
"model-context-protocol",
|
|
@@ -21,6 +21,15 @@
|
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"author": "Samuel Santana",
|
|
23
23
|
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
24
33
|
"engines": {
|
|
25
34
|
"node": ">=22"
|
|
26
35
|
},
|