@uptimizr/mcp 1.1.0 → 1.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/AGENTS.md +265 -28
- package/README.md +92 -22
- package/dist/bin.js +9 -2
- package/dist/bin.js.map +1 -1
- package/dist/capabilities.d.ts +21 -3
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +86 -15
- package/dist/capabilities.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +32 -83
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts +23 -4
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +66 -4
- package/dist/resources.js.map +1 -1
- package/dist/server.d.ts +65 -8
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +150 -25
- package/dist/server.js.map +1 -1
- package/llms.txt +190 -15
- package/package.json +11 -10
- package/skills/attention-hotspots/SKILL.md +88 -0
- package/skills/conversion-investigation/SKILL.md +97 -0
- package/skills/performance-regression-triage/SKILL.md +106 -0
- package/skills/weekly-scene-health/SKILL.md +105 -0
- package/skills/xr-comfort-audit/SKILL.md +95 -0
package/AGENTS.md
CHANGED
|
@@ -5,13 +5,18 @@
|
|
|
5
5
|
|
|
6
6
|
## What this package is
|
|
7
7
|
|
|
8
|
-
A
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
A Model Context Protocol (MCP) server over an Uptimizr collector's API. It lets an agent query a
|
|
9
|
+
consumer's **own** 3D analytics in natural language. Each analytics tool maps to one documented
|
|
10
|
+
collector read endpoint; the server is a thin wrapper that holds no business logic (ADR 0005,
|
|
11
|
+
ADR 0017).
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
**Events are read-only.** Nothing here can write, alter or delete an analytics event, and there is
|
|
14
|
+
no ingestion tool. The one writable surface is **project metadata** — annotations, the glossary,
|
|
15
|
+
saved analyses and pinned dashboard panels — and it is gated: those tools are registered only when
|
|
16
|
+
the configured key holds the `annotate` capability (ADR 0051 §5/§7/§9). See "Metadata tools" below.
|
|
17
|
+
|
|
18
|
+
It connects **only to the collector's HTTP API** (never to the database directly), so the collector
|
|
19
|
+
remains the single gateway that enforces auth, per-project scoping, and privacy.
|
|
15
20
|
|
|
16
21
|
## Run
|
|
17
22
|
|
|
@@ -19,7 +24,44 @@ collector remains the single gateway that enforces auth, per-project scoping, an
|
|
|
19
24
|
UPTIMIZR_COLLECTOR_URL="https://collect.example.com" UPTIMIZR_API_KEY="utk_…" npx @uptimizr/mcp
|
|
20
25
|
```
|
|
21
26
|
|
|
22
|
-
##
|
|
27
|
+
## Required key capability
|
|
28
|
+
|
|
29
|
+
`UPTIMIZR_API_KEY` must hold the **`query`** capability. Mint one with
|
|
30
|
+
`uptimizr new-key <projectId> --capabilities query --label "mcp-agent"` (`query` is the default).
|
|
31
|
+
`GET /api/v1/whoami` reports what a key holds — the server calls it once at start-up for exactly
|
|
32
|
+
this reason — and an authenticated key missing a capability is refused with `403`, not `401`.
|
|
33
|
+
|
|
34
|
+
Add **`annotate`** only if you want the agent to leave notes, definitions, saved analyses and
|
|
35
|
+
pinned dashboard panels behind (`--capabilities query,annotate`). Without it the server starts
|
|
36
|
+
read-only and never offers those tools; with it, every write is bounded at the collector's edge and
|
|
37
|
+
recorded in the audit log.
|
|
38
|
+
**`query:raw` is optional, and off by default.** Every tool in the default catalog is an aggregate
|
|
39
|
+
read: there is no raw per-session, replay or live-follow tool, so a plain `query` key is all most
|
|
40
|
+
deployments should grant. A key that _does_ hold `query:raw` additionally gets the
|
|
41
|
+
**`session_narrative`** tool — an ordered, bounded account of what one session did — but only when
|
|
42
|
+
the server was told about the capability, and only on a collector running with
|
|
43
|
+
`ENABLE_RAW_SESSION_RETENTION`. `createMcpServer(client, { capabilities })` takes the set that
|
|
44
|
+
`GET /api/v1/whoami` reports; the `uptimizr-mcp` binary looks it up at start-up (best effort — if
|
|
45
|
+
the call fails it serves the `query` surface). Omitting the option serves the `query` surface too,
|
|
46
|
+
so a tool that would always answer `403` is never advertised. `annotate` (metadata writes) and
|
|
47
|
+
`ingest` are never used here. Prefer a dedicated, labelled key with its own `--rate-limit-max` /
|
|
48
|
+
`--rate-limit-window-ms` budget: the collector's agent audit log records activity per key id, which
|
|
49
|
+
is what makes an agent's reads reviewable.
|
|
50
|
+
|
|
51
|
+
## Transports
|
|
52
|
+
|
|
53
|
+
`createMcpServer()` is transport-agnostic, and two transports serve the identical catalog,
|
|
54
|
+
resources and prompts:
|
|
55
|
+
|
|
56
|
+
- **stdio** — what this package's `bin` runs (`npx @uptimizr/mcp`), launched by a desktop client.
|
|
57
|
+
- **Collector-hosted Streamable HTTP** — a collector started with `COLLECTOR_MCP_HTTP=1` serves the
|
|
58
|
+
same server at `POST`/`GET`/`DELETE` `/mcp` (ADR 0051 §7). A remote client connects with a URL
|
|
59
|
+
plus `x-api-key` or `Authorization: Bearer <key>` — no local install — and every request is
|
|
60
|
+
re-authenticated, capped by `COLLECTOR_MCP_MAX_SESSIONS` and charged to the key's rate limit.
|
|
61
|
+
Tool calls are audited with `surface: "mcp-http"`; this package's own reads are plain HTTP reads
|
|
62
|
+
and are audited as `http`.
|
|
63
|
+
|
|
64
|
+
## Analytics tools (read-only)
|
|
23
65
|
|
|
24
66
|
<!-- generated:registry-tool-names:start — generated by `pnpm gen:docs`; edit the metric registry, not this table -->
|
|
25
67
|
|
|
@@ -29,22 +71,31 @@ UPTIMIZR_COLLECTOR_URL="https://collect.example.com" UPTIMIZR_API_KEY="utk_…"
|
|
|
29
71
|
`position_heatmap`, `session_trajectory`, `aggregate_paths`, `scene_coverage`, `camera_distance`,
|
|
30
72
|
`click_rays`, `flow_links`, `top_meshes`, `mesh_sources`, `mesh_trend`, `mesh_dwell`,
|
|
31
73
|
`mesh_blind_spots`, `mesh_interaction_kinds`, `mesh_reachability`, `dead_clicks`, `rage_clicks`,
|
|
32
|
-
`hover_dwell`, `interaction_sources`, `top_input_actions`, `
|
|
33
|
-
`
|
|
34
|
-
`
|
|
35
|
-
`
|
|
36
|
-
`
|
|
37
|
-
`
|
|
38
|
-
`
|
|
39
|
-
`
|
|
40
|
-
`scene_retention`, `load_bounce_funnel`, `variant_leaderboard
|
|
74
|
+
`hover_dwell`, `interaction_sources`, `top_input_actions`, `custom_event_vocabulary`,
|
|
75
|
+
`camera_gestures`, `navigation_stats`, `backtrack_ratio`, `perf_summary`, `render_scale_truth`,
|
|
76
|
+
`perf_distribution`, `fps_histogram`, `frame_time_percentiles`, `jank_rate`, `perf_churn`,
|
|
77
|
+
`perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_summary`,
|
|
78
|
+
`resource_percentiles`, `stability_counts`, `graphics_diagnostics`, `error_heatmap`,
|
|
79
|
+
`rendering_technology`, `capability_changes`, `xr_rotation`, `xr_sources`, `xr_abandonment`,
|
|
80
|
+
`xr_locomotion`, `xr_tracking_quality`, `boundary_heatmap`, `boundary_heatmap_stats`,
|
|
81
|
+
`xr_boundary_contacts`, `ar_placement_time_to_place`, `ar_placement_attempts`,
|
|
82
|
+
`ar_placement_surfaces`, `funnel`, `scene_retention`, `load_bounce_funnel`, `variant_leaderboard`,
|
|
83
|
+
`insight_baseline`, `insight_movers`, `insight_anomalies`, `insight_significance`,
|
|
84
|
+
`insight_scene_health`
|
|
85
|
+
|
|
86
|
+
Only on a key holding `query:raw`, and only when the collector runs with
|
|
87
|
+
`ENABLE_RAW_SESSION_RETENTION` (ADR 0003):
|
|
88
|
+
|
|
89
|
+
`session_narrative`
|
|
41
90
|
|
|
42
91
|
<!-- generated:registry-tool-names:end -->
|
|
43
92
|
|
|
44
|
-
**
|
|
45
|
-
metric
|
|
46
|
-
|
|
47
|
-
|
|
93
|
+
**75 tools, generated** from the `@uptimizr/metrics` semantic metric registry (ADR 0051 §1) — one
|
|
94
|
+
per metric a plain `query` key may read, plus `session_narrative` above for a `query:raw` key. With
|
|
95
|
+
the `query` DSL tool and `list_subscriptions` below, a `query` key sees **77** tools in all. Names
|
|
96
|
+
are the registry ids; the full table lives in [README.md](./README.md), and
|
|
97
|
+
`uptimizr://capabilities` enumerates them at runtime with each tool's grain, column units and
|
|
98
|
+
caveats.
|
|
48
99
|
|
|
49
100
|
Orientation: `list_sessions`, `list_scenes`, `session_meta`, `scene_representation`, `timeseries`,
|
|
50
101
|
`event_counts`. Attention: the `*_heatmap` family, `mesh_dwell`, `mesh_blind_spots`, `hover_dwell`.
|
|
@@ -59,15 +110,177 @@ Navigation: `aggregate_paths`, `session_trajectory`, `scene_coverage`, `navigati
|
|
|
59
110
|
`variant_leaderboard`.
|
|
60
111
|
|
|
61
112
|
Most accept `since`/`until` (epoch ms) plus endpoint-specific filters (`scene`, `session`, `source`,
|
|
62
|
-
`bins`, `cellSize`, `limit`, `cameraMode`, `region`, …). Every tool declares an `outputSchema`
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
small sample.
|
|
113
|
+
`bins`, `cellSize`, `limit`, `cameraMode`, `region`, …). Every tool declares an `outputSchema`
|
|
114
|
+
covering all three `format` envelopes and returns the one you asked for as `structuredContent`
|
|
115
|
+
alongside the JSON text — read the schema instead of guessing the row shape, and read the tool
|
|
116
|
+
description for the metric's caveats before trusting a small sample.
|
|
117
|
+
|
|
118
|
+
## The `query` tool (the query DSL)
|
|
119
|
+
|
|
120
|
+
One tool is **not** per-metric: `query` (ADR 0051 §3). Its input is the query DSL, so it runs any
|
|
121
|
+
metric in the catalog above with any filter that metric declares:
|
|
122
|
+
|
|
123
|
+
```jsonc
|
|
124
|
+
{
|
|
125
|
+
"v": 1,
|
|
126
|
+
"metric": "mesh_sources",
|
|
127
|
+
"range": { "since": 1757000000000, "until": 1757600000000 },
|
|
128
|
+
"filters": { "scene": "lobby", "cameraMode": "first-person" },
|
|
129
|
+
"limit": 20,
|
|
130
|
+
"format": "summary",
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- **`range` is required** (both ends, epoch ms) — there is no unbounded query.
|
|
135
|
+
- **`format` defaults to `table`** here rather than `full`.
|
|
136
|
+
- The grammar is closed: metrics, dimensions and filters are exactly the vocabulary in the
|
|
137
|
+
`uptimizr://capabilities` resource. Naming something outside it is a `400` whose
|
|
138
|
+
`issues[].accepted` lists what would have worked — read it instead of guessing again.
|
|
139
|
+
- `dimensions` may be any subset a metric declares **when** its measure is a portable count —
|
|
140
|
+
event counts, mesh and interaction tallies, input actions, camera gestures. A spatial heatmap or a
|
|
141
|
+
percentile is computed at one fixed grain and refuses anything else, naming the grain it supports.
|
|
142
|
+
- **`compare`** — another `{ range }` or `{ segment }`; the result comes back joined on the
|
|
143
|
+
dimension key as `{ current, previous, delta, deltaPct }`, with a significance test where the
|
|
144
|
+
measure is a count and both windows clear the metric's minimum. Never subtract two results by hand.
|
|
145
|
+
- **`explain: true`** — the compiled plan instead of the rows: the tier, the SQL with its parameters
|
|
146
|
+
left unbound, `params` by name and type (never value), `rowsScanned`, and `warnings` (a capture
|
|
147
|
+
channel that produced nothing, a sample below the metric's minimum, a truncated result).
|
|
148
|
+
- **`drillQuery`** — every row of a `summary` carries the whole query narrowed to that row, ready to
|
|
149
|
+
send straight back.
|
|
150
|
+
- `order` takes a measure column, and only where the result is a ranked list.
|
|
151
|
+
|
|
152
|
+
Its `structuredContent` is `{ result }` rather than `{ rows }`, because what comes back depends on
|
|
153
|
+
the `format` asked for.
|
|
154
|
+
|
|
155
|
+
## Non-metric reads
|
|
156
|
+
|
|
157
|
+
Alongside the generated per-metric tools, the server registers `list_subscriptions` (ADR 0051
|
|
158
|
+
§6): the project's standing conditional subscriptions, what each watches for, and how each last
|
|
159
|
+
went. It takes no arguments. It is read-only like everything else here — creating or deleting a
|
|
160
|
+
subscription needs the `annotate` capability and is done over plain HTTP, not through MCP.
|
|
161
|
+
|
|
162
|
+
## Result formats (`format`)
|
|
163
|
+
|
|
164
|
+
Every **aggregate** tool takes a `format` argument choosing the envelope its rows arrive in
|
|
165
|
+
(ADR 0051 §2). It filters nothing. **Omit it and you get `table`** — the tools' own default,
|
|
166
|
+
sent explicitly, so the HTTP endpoints still default to `full` for every other client.
|
|
167
|
+
|
|
168
|
+
| `format` | Returns | Use it when |
|
|
169
|
+
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- |
|
|
170
|
+
| `summary` | A bounded digest — `ranked` top rows, a `series` trend, merged spatial `clusters`, or a single `record` — with shares, a sample size, the metric's `caveats` and a templated `reading` sentence. | **Ask for it.** Whenever you do not need every row — always, for a heatmap or a leaderboard. |
|
|
171
|
+
| `table` | `{ meta, rows }`: the same rows plus the metric, range, applied filters, sample size, row count, a truncation flag and the registry's limits. | **What you get if you name no format.** Every row _and_ the context to judge it. |
|
|
172
|
+
| `full` | The bare rows, unchanged, with no envelope at all. | You are post-processing the rows yourself and already know the sample is adequate. |
|
|
173
|
+
|
|
174
|
+
- `summary` is capped at the metric's `limits.maxSummaryRows`, so a 500-bin heatmap costs the same
|
|
175
|
+
as a 5-bin one. That is the whole point: a heatmap, voxel cloud or long leaderboard returned as
|
|
176
|
+
`full` can fill a context window on its own.
|
|
177
|
+
- `reading` and `caveats` are templated from the registry's column semantics by pure code — no model
|
|
178
|
+
is involved, so identical rows always produce identical words. Quote them; don't re-derive them.
|
|
179
|
+
- Shares appear only where the measure can honestly be summed. A metric in FPS or a ratio reports
|
|
180
|
+
`total: null` and no shares — do not compute a percentage from those rows.
|
|
181
|
+
- Cluster coordinates are **grid indices**: multiply by the effective `cellSize` for world space.
|
|
182
|
+
When the scene has a registered proxy and named regions, each cluster of a world-space heatmap is
|
|
183
|
+
also **labelled** — `region` (the smallest containing region), `regions[]` (all of them),
|
|
184
|
+
`nearestMesh`, and `distance` in world units (`0` when the mesh box contains the hotspot) — and
|
|
185
|
+
`drill.region` becomes that region **id**, ready to pass straight back as the `region` argument.
|
|
186
|
+
Say the label, not the coordinate. A `null` means the scene registered nothing that could answer
|
|
187
|
+
and the `caveats` name which; inventing a landmark instead is a hallucination.
|
|
188
|
+
- `session_meta` and `scene_representation` are single stored records, not aggregations, and take no
|
|
189
|
+
`format`. Raw `/api/v1/sessions/:id/events` has an unrelated `format=json|ndjson`, and this server
|
|
190
|
+
exposes no tool for it.
|
|
191
|
+
- `table` is only self-_describing_, not bounded: its `meta` costs a fixed ~200 characters and the
|
|
192
|
+
rows are still all of them. When the answer could be large, ask for `summary`.
|
|
193
|
+
|
|
194
|
+
## Metadata tools (`annotate` only)
|
|
195
|
+
|
|
196
|
+
Registered **only** when `GET /api/v1/whoami` reports the `annotate` capability, so a read-only key
|
|
197
|
+
yields a read-only server. They write project metadata and nothing else — no event is touched.
|
|
198
|
+
|
|
199
|
+
| Tool | What it does |
|
|
200
|
+
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
|
201
|
+
| `annotate` | Pin a note to the project, a scene, a mesh, a region, a metric or a period of time (`targetKind`, `text`). |
|
|
202
|
+
| `define_term` | Record what a name means in this project (`term`, `meaning`). Idempotent — redefining replaces. |
|
|
203
|
+
| `save_analysis` | Store a titled question plus its conclusion (`title`, `query`, `conclusion?`). |
|
|
204
|
+
| `list_annotations` | Read the notes already left. Call it **before** explaining a spike someone may already have explained. |
|
|
205
|
+
| `list_glossary` | Read the project's vocabulary before interpreting mesh names, scene ids or custom events. |
|
|
206
|
+
| `list_analyses` | Read questions this project has asked before, and what they concluded. |
|
|
207
|
+
| `pin_panel` | Keep a question on the project's dashboard (`title`, `query`, `chart`, `encoding?`, `span?`, `note?`). |
|
|
208
|
+
| `unpin_panel` | Remove one pinned panel by its `id`. It goes for **everyone** on the project, not just you. |
|
|
209
|
+
| `list_panels` | Read the panels already pinned, oldest first. Call it **before** pinning, so you extend the grid. |
|
|
210
|
+
|
|
211
|
+
Bounds are enforced by the collector: `text` ≤ 2 000 characters, `meaning` ≤ 500, `title` ≤ 120,
|
|
212
|
+
`conclusion` ≤ 4 000, a panel `note` ≤ 500, and per project at most 500 annotations, 200 terms,
|
|
213
|
+
200 analyses and 50 pinned panels (a write past a cap answers `409`). Stored rows record that an
|
|
214
|
+
**agent** wrote them; the collector decides that from the calling client, never from the payload.
|
|
215
|
+
|
|
216
|
+
`pin_panel` stores a **spec**, not code: a metric id, a chart name and some column names, which the
|
|
217
|
+
dashboard draws with panel components it already ships. Pass the same `query` document the `query`
|
|
218
|
+
tool takes, with `range` set to `"inherit"` so the panel follows the dashboard's own time filter
|
|
219
|
+
instead of freezing the window you asked in, and a `chart` the metric's grain supports —
|
|
220
|
+
`line`/`area` need a time-bucketed metric, `heatmap2d` a binned one, `world3d` a voxelised one,
|
|
221
|
+
`stat` a single-record one, `bar` a ranking, `table` anything. A spec that does not fit is a `400`
|
|
222
|
+
naming the charts that would have worked.
|
|
223
|
+
|
|
224
|
+
Use them at the end of an investigation, not for scratch state: a note that says what was concluded
|
|
225
|
+
and why is worth keeping, a note per query is noise.
|
|
226
|
+
|
|
227
|
+
## Resources and prompts
|
|
228
|
+
|
|
229
|
+
- `uptimizr://context` (`application/json`) — the **live project context document**
|
|
230
|
+
(`GET /api/v1/context`, ADR 0051 §5). **Read this first, before capabilities.** It describes the
|
|
231
|
+
project in front of you rather than the API: the scene ids with their labels and named region ids,
|
|
232
|
+
the custom events the application emits with the `props` keys and coarse types they carry, the top
|
|
233
|
+
meshes and bound input actions, data freshness (`lastEventAt`, `sessions24h`), whether raw session
|
|
234
|
+
retention is on, the store engine and collector version, the project glossary and recent
|
|
235
|
+
annotations, and `metrics.disabledByCapture` — the metrics that WILL return empty because every
|
|
236
|
+
capture channel feeding them is off. Bounded (<16 KB) and cached per project for ~30 s. Use the ids
|
|
237
|
+
and names it gives you; never infer a scene id, a region id or a custom-event name.
|
|
238
|
+
- `uptimizr://capabilities` (`application/json`) — the machine-readable descriptor: schema version,
|
|
239
|
+
the canonical event types, the tool catalog, the parameter-semantics glossary, and `metrics`, the
|
|
240
|
+
whole registry with each metric's grain, column units, row JSON Schema, filters, limits,
|
|
241
|
+
interpretation, caveats, source channels and related metrics. Served from the package; **no
|
|
242
|
+
collector call**. The companion to the context: it is how to plan a query without trial and error.
|
|
243
|
+
- `uptimizr://scenes` (`application/json`) — the **live** list of scene ids with recent activity,
|
|
244
|
+
fetched through the read-only query API. These are the valid values for the `scene` parameter, so
|
|
245
|
+
resolve a user's scene name against it instead of guessing a string.
|
|
246
|
+
- `uptimizr://skills` (`application/json`) — the packaged methodology skills below: name, title,
|
|
247
|
+
description, the tools each method relies on, the capabilities it needs and the arguments it
|
|
248
|
+
takes. Served from the package; **no collector call**. Use it when the client has no prompt
|
|
249
|
+
picker; the method itself comes from `prompts/get`.
|
|
250
|
+
- Prompts: one per packaged skill, listed below. Each opens by telling the agent to read
|
|
251
|
+
`uptimizr://context` first, then renders one user message that sequences the existing read-only
|
|
252
|
+
tools; they fetch nothing themselves and name no exact arguments, so the agent still resolves the
|
|
253
|
+
epoch-ms range and the filters.
|
|
254
|
+
|
|
255
|
+
## Packaged methodology skills (ADR 0051 §7)
|
|
256
|
+
|
|
257
|
+
A prompt names tools; a **skill** carries the method. Each is an Agent Skills file,
|
|
258
|
+
`skills/<name>/SKILL.md`, shipped in this tarball (authored in `@uptimizr/agent-core`) and
|
|
259
|
+
registered here as a prompt template. The body is the investigation as numbered steps: orient on
|
|
260
|
+
`uptimizr://context`, start from the score, test a change before calling it real, date it, and end
|
|
261
|
+
with recommendations. Read one before improvising: `skills/weekly-scene-health/SKILL.md`.
|
|
262
|
+
|
|
263
|
+
<!-- generated:registry-skill-names:start — generated by `pnpm gen:docs`; edit the SKILL.md files, not this list -->
|
|
264
|
+
|
|
265
|
+
- `attention_hotspots` (scene (required), range) — Find where visitors look and click in a scene: view-direction concentration, gaze→mesh flow, the objects that draw the most interaction, and the ones nobody ever notices. USE FOR: deciding where to put a call to action, finding ignored or invisible content, explaining why an object gets no clicks, laying out a scene around what people actually look at.
|
|
266
|
+
Method: `skills/attention-hotspots/SKILL.md`. Tools: `camera_heatmap`, `flow_links`, `click_rays`, `top_meshes`, `mesh_dwell`, `mesh_blind_spots`, `query`.
|
|
267
|
+
- `conversion_investigation` (scene, range) — Find out where a funnel loses people and whether the loss is real: step-by-step drop-off, the bounce that happens before the funnel even starts, scene-to-scene retention, variant performance, and the interaction failures (dead clicks, rage clicks, unreachable meshes) that explain a stalled step. USE FOR: a funnel that converts worse than expected, an A/B variant comparison, "where do people drop off", diagnosing a step nobody completes.
|
|
268
|
+
Method: `skills/conversion-investigation/SKILL.md`. Tools: `funnel`, `load_bounce_funnel`, `scene_retention`, `variant_leaderboard`, `dead_clicks`, `rage_clicks`, `mesh_reachability`, `flow_links`, `insight_significance`, `insight_movers`, `query`.
|
|
269
|
+
- `performance_regression_triage` (scene, range) — Triage a frame-rate or stability regression: confirm it moved, date it, locate it (which scene, device class, place in the scene), and name the mechanism — jank, shader compile stalls, memory pressure, a render-scale change or a rendering-technology shift. USE FOR: "the app got slower", a FPS drop after a release, stutter reports, deciding whether a regression is real or noise.
|
|
270
|
+
Method: `skills/performance-regression-triage/SKILL.md`. Tools: `insight_movers`, `insight_anomalies`, `insight_significance`, `insight_baseline`, `perf_summary`, `perf_distribution`, `frame_time_percentiles`, `jank_rate`, `perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_percentiles`, `render_scale_truth`, `rendering_technology`, `query`.
|
|
271
|
+
- `weekly_scene_health` (scene, range) — A weekly health check for a scene (or the whole project): a weighted health score with every factor traced back to the metric behind it, what changed against last week, traffic, event mix, performance, and the most-interacted meshes. USE FOR: the recurring "how is the scene doing?" review, a scheduled weekly or monthly report, a first look at a project you do not know yet, deciding which scene to investigate next.
|
|
272
|
+
Method: `skills/weekly-scene-health/SKILL.md`. Tools: `insight_scene_health`, `insight_movers`, `insight_baseline`, `insight_significance`, `insight_anomalies`, `event_counts`, `timeseries`, `perf_summary`, `top_meshes`, `list_sessions`, `query`.
|
|
273
|
+
- `xr_comfort_audit` (scene, range) — Audit VR/AR comfort for a scene (or the whole project): rapid head rotation, locomotion style, tracking quality, guardian/boundary contacts, input-source mix, and the short sessions that mean someone took the headset off. USE FOR: motion-sickness complaints, immersive sessions that end early, choosing a locomotion scheme, checking whether a play space is big enough.
|
|
274
|
+
Method: `skills/xr-comfort-audit/SKILL.md`. Tools: `xr_rotation`, `xr_locomotion`, `xr_abandonment`, `xr_sources`, `xr_tracking_quality`, `xr_boundary_contacts`, `boundary_heatmap_stats`, `insight_scene_health`, `insight_movers`, `query`.
|
|
275
|
+
|
|
276
|
+
<!-- generated:registry-skill-names:end -->
|
|
66
277
|
|
|
67
278
|
## Rules for agents
|
|
68
279
|
|
|
69
|
-
- **
|
|
70
|
-
|
|
280
|
+
- **Events are read-only; privacy-preserving throughout.** Never add an ingestion tool, an
|
|
281
|
+
event-mutating tool, or a raw per-session event tool here. The only writes are the `annotate`-gated
|
|
282
|
+
metadata tools above, and they must stay a separate `writeTools` export from `readTools` so the
|
|
283
|
+
read-only stance remains inspectable. No data leaves the consumer's infrastructure (ADR 0003).
|
|
71
284
|
- The server talks only to the configured collector with the consumer's `x-api-key`; never hardcode
|
|
72
285
|
or log credentials.
|
|
73
286
|
- Keep it a thin wrapper: the `readTools` catalog (defined in `@uptimizr/agent-core`) is
|
|
@@ -77,13 +290,37 @@ small sample.
|
|
|
77
290
|
- **No database driver, ever.** This server talks to a collector over HTTP; it must stay installable
|
|
78
291
|
with `npx`. Never add `@uptimizr/db` (or any package with a native/optional binary dependency) to
|
|
79
292
|
`dependencies` — `src/__tests__/dependencies.test.ts` fails if you do.
|
|
293
|
+
- **Start from an insight primitive on an open-ended question.** "How are things?" does not mean
|
|
294
|
+
"call thirty tools": `insight_scene_health` says _which scene_ to look at (six weighted factors,
|
|
295
|
+
each naming the metric, raw value and project baseline behind it; 50 is the project norm, not a
|
|
296
|
+
pass mark), `insight_movers` compares every comparable metric with the previous equal window and
|
|
297
|
+
ranks the changes by how unusual each is, and `insight_baseline` says whether a level is outside
|
|
298
|
+
normal for that scene. Two fields decide whether a mover is reportable: `direction` is the
|
|
299
|
+
registry's opinion of what a _rise_ means (so a rise in a `down` metric is a regression, not an
|
|
300
|
+
improvement), and `aboveMinSample: false` means the delta is arithmetic but not evidence — those
|
|
301
|
+
rows are returned rather than dropped, and must never be reported as findings.
|
|
302
|
+
- **Then `insight_anomalies` to put a date on it.** `insight_movers` compares two windows you
|
|
303
|
+
chose; `insight_anomalies` walks one metric's whole series and names the buckets that do not
|
|
304
|
+
belong — `spike` / `drop` for a single bucket far from the ones before it, and `shift` at the
|
|
305
|
+
bucket where the level moved and _stayed_ moved, which is the shape a release regression has and
|
|
306
|
+
the one no per-bucket threshold can see. Quote `bucketStart`, and read `contributor`: where the
|
|
307
|
+
metric declares a dimension it can be split by, the row names the mesh, source, input action,
|
|
308
|
+
event type or scene holding the largest share of the excess. Its `z` is in standard deviations
|
|
309
|
+
while `insight_movers`' is the same ratio unscaled, so the two columns are not comparable.
|
|
310
|
+
- **Call `insight_significance` before calling a single change real.** It reports the effect, a 95%
|
|
311
|
+
interval and a p-value for one metric across two windows, with the test chosen from what the
|
|
312
|
+
measure is. Read `ci95` before `p` — an interval straddling 0 means you cannot tell yet — and
|
|
313
|
+
read `powerNote`, which distinguishes "no effect" from "not enough data". It compares two
|
|
314
|
+
_windows_, not two segments.
|
|
80
315
|
- Tool definitions are pure (`buildRequest`) and must stay unit-testable without a live collector.
|
|
81
316
|
|
|
82
317
|
## Programmatic API
|
|
83
318
|
|
|
84
|
-
`readMcpConfig()`, `createMcpServer(client)
|
|
319
|
+
`readMcpConfig()`, `createMcpServer(client, options?)` (`options.capabilities` is the key’s
|
|
320
|
+
capability set from `/api/v1/whoami`; it gates the `query:raw` and `annotate` tools),
|
|
321
|
+
`buildCapabilities(options?)`, and the shared building blocks re-exported from
|
|
85
322
|
[`@uptimizr/agent-core`](https://www.npmjs.com/package/@uptimizr/agent-core):
|
|
86
|
-
`createCollectorClient(config)` and `
|
|
323
|
+
`createCollectorClient(config)`, `readTools`, `rawTools` and `writeTools`.
|
|
87
324
|
|
|
88
325
|
## More
|
|
89
326
|
|
package/README.md
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
# @uptimizr/mcp
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server over an Uptimizr
|
|
4
|
+
collector's API. It lets an AI agent ask natural-language questions about your
|
|
5
5
|
3D analytics ("what was the most-clicked mesh this week?") and have them answered by querying
|
|
6
6
|
**your own** collector — nothing is sent to any third party.
|
|
7
7
|
|
|
8
|
-
The server is a thin wrapper: each tool maps one-to-one to a documented collector query
|
|
9
|
-
(see
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
The server is a thin wrapper: each analytics tool maps one-to-one to a documented collector query
|
|
9
|
+
endpoint (see
|
|
10
|
+
[integration docs](https://github.com/RaananW/Uptimizr/blob/main/docs/integration.md)) and performs
|
|
11
|
+
`GET` requests only. There is **no ingestion tool**, and nothing here can write, alter or delete an
|
|
12
|
+
analytics event — **events are read-only**. A key holding `query:raw` additionally gets
|
|
13
|
+
`session_narrative`, the compacted account of one session, on a collector running with
|
|
14
|
+
`ENABLE_RAW_SESSION_RETENTION`.
|
|
15
|
+
|
|
16
|
+
The one exception is gated: when the configured key holds the `annotate` capability the server also
|
|
17
|
+
registers the **project-metadata** tools `annotate`, `define_term` and `save_analysis` (plus
|
|
18
|
+
`list_annotations`, `list_glossary`, `list_analyses`), which leave notes, definitions and saved
|
|
19
|
+
analyses behind. The server asks `GET /api/v1/whoami` once at start-up, so a read-only key yields a
|
|
20
|
+
read-only server, and every metadata write is recorded in the project's agent audit log.
|
|
12
21
|
|
|
13
22
|
## How it connects
|
|
14
23
|
|
|
@@ -28,10 +37,10 @@ UPTIMIZR_API_KEY="utk_…" \
|
|
|
28
37
|
npx @uptimizr/mcp
|
|
29
38
|
```
|
|
30
39
|
|
|
31
|
-
| Environment variable | Required | Notes
|
|
32
|
-
| ------------------------ | -------- |
|
|
33
|
-
| `UPTIMIZR_COLLECTOR_URL` | yes | Base URL of **your** collector.
|
|
34
|
-
| `UPTIMIZR_API_KEY` | yes | Your project API key (`x-api-key`)
|
|
40
|
+
| Environment variable | Required | Notes |
|
|
41
|
+
| ------------------------ | -------- | ----------------------------------------------------------------------------------------------- |
|
|
42
|
+
| `UPTIMIZR_COLLECTOR_URL` | yes | Base URL of **your** collector. |
|
|
43
|
+
| `UPTIMIZR_API_KEY` | yes | Your project API key (`x-api-key`). `query` reads; add `annotate` to enable the metadata tools. |
|
|
35
44
|
|
|
36
45
|
### Configure an MCP client
|
|
37
46
|
|
|
@@ -76,14 +85,14 @@ For **GitHub Copilot CLI**, put the same entry in `~/.copilot/mcp-config.json` w
|
|
|
76
85
|
## Tools
|
|
77
86
|
|
|
78
87
|
The catalog is **generated from the semantic metric registry** in `@uptimizr/metrics` (ADR 0051 §1):
|
|
79
|
-
every
|
|
88
|
+
every metric the collector serves on a read endpoint is a tool — **76** of them, one table row
|
|
89
|
+
each, of which a plain `query` key sees 75 (`session_narrative` needs `query:raw`). Each tool's
|
|
80
90
|
description carries the metric's interpretation notes and caveats, and each declares an MCP
|
|
81
|
-
`outputSchema`
|
|
82
|
-
`
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
row schema and caveats; the same registry drives the collector's `GET /api/v1/openapi.json`.
|
|
91
|
+
`outputSchema` covering every `format` envelope it can answer with — the rows, the `table`
|
|
92
|
+
envelope around them (the tools' default), or a `summary` digest. Results come back as both
|
|
93
|
+
`content` text and `structuredContent`. Read `uptimizr://capabilities` for each tool's result
|
|
94
|
+
grain, column units, row schema and caveats; the same registry drives the collector's
|
|
95
|
+
`GET /api/v1/openapi.json`.
|
|
87
96
|
|
|
88
97
|
Most tools accept an optional time range (`since` / `until`, epoch ms) and the filters the
|
|
89
98
|
underlying endpoint supports (`scene`, `session`, `source`, `bins`, `cellSize`, `interval`, `type`,
|
|
@@ -96,6 +105,7 @@ underlying endpoint supports (`scene`, `session`, `source`, `bins`, `cellSize`,
|
|
|
96
105
|
| ---------------------------- | ---------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
97
106
|
| `list_sessions` | `/api/v1/sessions` | Recent sessions | `since`, `until`, `bins`, `limit`, `cameraMode`, `format` |
|
|
98
107
|
| `session_meta` | `/api/v1/sessions/:id/meta` | Session descriptor | `session` |
|
|
108
|
+
| `session_narrative` | `/api/v1/sessions/:id/narrative` | Session narrative | `session`, `minDwellMs`, `fpsThreshold`, `maxEntries`, `format` |
|
|
99
109
|
| `scene_representation` | `/api/v1/scenes/:sceneId/representation` | Scene representation | `scene` |
|
|
100
110
|
| `list_scenes` | `/api/v1/scenes` | Active scenes | `since`, `until`, `limit`, `format` |
|
|
101
111
|
| `timeseries` | `/api/v1/timeseries` | Event volume over time | `since`, `until`, `interval`, `scene`, `type`, `format` |
|
|
@@ -127,6 +137,7 @@ underlying endpoint supports (`scene`, `session`, `source`, `bins`, `cellSize`,
|
|
|
127
137
|
| `hover_dwell` | `/api/v1/hover/dwell` | Hover hesitation per object | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
|
|
128
138
|
| `interaction_sources` | `/api/v1/interactions/sources` | Interactions by input source | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
|
|
129
139
|
| `top_input_actions` | `/api/v1/input-actions/top` | Most-used shortcuts and actions | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
|
|
140
|
+
| `custom_event_vocabulary` | `/api/v1/vocabulary/custom-events` | Discovered custom-event vocabulary | `since`, `until`, `scene`, `limit`, `format` |
|
|
130
141
|
| `camera_gestures` | `/api/v1/camera-gestures` | Camera navigation gestures | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
|
|
131
142
|
| `navigation_stats` | `/api/v1/navigation` | Navigation effort per session | `since`, `until`, `moveThreshold`, `limit`, `scene`, `session`, `format` |
|
|
132
143
|
| `backtrack_ratio` | `/api/v1/backtrack` | Path retrace / backtracking | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `format` |
|
|
@@ -163,6 +174,11 @@ underlying endpoint supports (`scene`, `session`, `source`, `bins`, `cellSize`,
|
|
|
163
174
|
| `scene_retention` | `/api/v1/scene-retention` | Scene-to-scene retention | `since`, `until`, `limit`, `format` |
|
|
164
175
|
| `load_bounce_funnel` | `/api/v1/load-bounce` | Load → bounce funnel | `since`, `until`, `scene`, `bands`, `format` |
|
|
165
176
|
| `variant_leaderboard` | `/api/v1/variant-leaderboard` | Variant → conversion leaderboard | `since`, `until`, `scene`, `cameraMode`, `variant`, `conversion`, `limit`, `format` |
|
|
177
|
+
| `insight_baseline` | `/api/v1/insights/baseline` | Metric baseline | `metric`, `scene`, `window`, `bucket`, `since`, `until`, `format` |
|
|
178
|
+
| `insight_movers` | `/api/v1/insights/movers` | What changed | `scene`, `metrics`, `bucket`, `limit`, `since`, `until`, `refSince`, `refUntil`, `format` |
|
|
179
|
+
| `insight_anomalies` | `/api/v1/insights/anomalies` | Anomalous buckets | `metric`, `scene`, `window`, `bucket`, `sensitivity`, `since`, `until`, `format` |
|
|
180
|
+
| `insight_significance` | `/api/v1/insights/significance` | Statistical significance | `metric`, `scene`, `bucket`, `since`, `until`, `refSince`, `refUntil`, `format` |
|
|
181
|
+
| `insight_scene_health` | `/api/v1/insights/scene-health` | Scene health score | `scene`, `window`, `since`, `until`, `bucket`, `limit`, `weights`, `format` |
|
|
166
182
|
|
|
167
183
|
<!-- generated:registry-tools:end -->
|
|
168
184
|
|
|
@@ -172,11 +188,59 @@ The server also exposes read-only **resources** for self-discovery — `uptimizr
|
|
|
172
188
|
(a machine-readable descriptor of event types, the tool catalog, parameter semantics, and
|
|
173
189
|
`metrics`: the collector's whole semantic metric registry, with each metric's result grain, column
|
|
174
190
|
units, row JSON Schema, row limits, interpretation and caveats — ADR 0051) and
|
|
175
|
-
`uptimizr://scenes` (live scene ids)
|
|
176
|
-
`
|
|
177
|
-
|
|
191
|
+
`uptimizr://context` (the live project context document), `uptimizr://scenes` (live scene ids) and
|
|
192
|
+
`uptimizr://skills` (the methodology catalog below) — plus one curated **prompt per packaged
|
|
193
|
+
skill**, listed in the table below, that drives the tools above. See the
|
|
178
194
|
[MCP guide](https://uptimizr.com/docs/guides/mcp/) for details.
|
|
179
195
|
|
|
196
|
+
## Methodology skills
|
|
197
|
+
|
|
198
|
+
Each prompt template is a packaged **skill** — an Agent Skills file, `skills/<name>/SKILL.md`,
|
|
199
|
+
shipped in this package — so the method an agent follows is readable, forkable and identical to the
|
|
200
|
+
one `uptimizr agent report --skill` runs on your collector. The catalog is also served as the
|
|
201
|
+
`uptimizr://skills` resource. `*` marks a required argument.
|
|
202
|
+
|
|
203
|
+
<!-- generated:registry-skills:start — generated by `pnpm gen:docs`; edit the SKILL.md files, not this list -->
|
|
204
|
+
|
|
205
|
+
| Skill | Arguments | What it produces, and when to use it | Tools its method names |
|
|
206
|
+
| ------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
207
|
+
| `attention_hotspots` | `scene*`, `range?` | Find where visitors look and click in a scene: view-direction concentration, gaze→mesh flow, the objects that draw the most interaction, and the ones nobody ever notices. USE FOR: deciding where to put a call to action, finding ignored or invisible content, explaining why an object gets no clicks, laying out a scene around what people actually look at. | `camera_heatmap`, `flow_links`, `click_rays`, `top_meshes`, `mesh_dwell`, `mesh_blind_spots`, `query` |
|
|
208
|
+
| `conversion_investigation` | `scene?`, `range?` | Find out where a funnel loses people and whether the loss is real: step-by-step drop-off, the bounce that happens before the funnel even starts, scene-to-scene retention, variant performance, and the interaction failures (dead clicks, rage clicks, unreachable meshes) that explain a stalled step. USE FOR: a funnel that converts worse than expected, an A/B variant comparison, "where do people drop off", diagnosing a step nobody completes. | `funnel`, `load_bounce_funnel`, `scene_retention`, `variant_leaderboard`, `dead_clicks`, `rage_clicks`, `mesh_reachability`, `flow_links`, `insight_significance`, `insight_movers`, `query` |
|
|
209
|
+
| `performance_regression_triage` | `scene?`, `range?` | Triage a frame-rate or stability regression: confirm it moved, date it, locate it (which scene, device class, place in the scene), and name the mechanism — jank, shader compile stalls, memory pressure, a render-scale change or a rendering-technology shift. USE FOR: "the app got slower", a FPS drop after a release, stutter reports, deciding whether a regression is real or noise. | `insight_movers`, `insight_anomalies`, `insight_significance`, `insight_baseline`, `perf_summary`, `perf_distribution`, `frame_time_percentiles`, `jank_rate`, `perf_by_device`, `perf_by_scene`, `perf_heatmap`, `compile_stalls`, `resource_percentiles`, `render_scale_truth`, `rendering_technology`, `query` |
|
|
210
|
+
| `weekly_scene_health` | `scene?`, `range?` | A weekly health check for a scene (or the whole project): a weighted health score with every factor traced back to the metric behind it, what changed against last week, traffic, event mix, performance, and the most-interacted meshes. USE FOR: the recurring "how is the scene doing?" review, a scheduled weekly or monthly report, a first look at a project you do not know yet, deciding which scene to investigate next. | `insight_scene_health`, `insight_movers`, `insight_baseline`, `insight_significance`, `insight_anomalies`, `event_counts`, `timeseries`, `perf_summary`, `top_meshes`, `list_sessions`, `query` |
|
|
211
|
+
| `xr_comfort_audit` | `scene?`, `range?` | Audit VR/AR comfort for a scene (or the whole project): rapid head rotation, locomotion style, tracking quality, guardian/boundary contacts, input-source mix, and the short sessions that mean someone took the headset off. USE FOR: motion-sickness complaints, immersive sessions that end early, choosing a locomotion scheme, checking whether a play space is big enough. | `xr_rotation`, `xr_locomotion`, `xr_abandonment`, `xr_sources`, `xr_tracking_quality`, `xr_boundary_contacts`, `boundary_heatmap_stats`, `insight_scene_health`, `insight_movers`, `query` |
|
|
212
|
+
|
|
213
|
+
<!-- generated:registry-skills:end -->
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
cat node_modules/@uptimizr/mcp/skills/weekly-scene-health/SKILL.md
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Hosted transport (Streamable HTTP)
|
|
220
|
+
|
|
221
|
+
The collector can serve **this same server** itself, over MCP's Streamable HTTP transport, so a
|
|
222
|
+
remote agent connects with a URL and a key instead of launching this package locally (ADR 0051 §7).
|
|
223
|
+
Start the collector with `COLLECTOR_MCP_HTTP=1` and point a client at `/mcp`:
|
|
224
|
+
|
|
225
|
+
```jsonc
|
|
226
|
+
{
|
|
227
|
+
"mcpServers": {
|
|
228
|
+
"uptimizr": {
|
|
229
|
+
"type": "http",
|
|
230
|
+
"url": "https://collect.example.com/mcp",
|
|
231
|
+
"headers": { "Authorization": "Bearer utk_…" },
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`x-api-key` works in place of the bearer header, the key still needs only `query`, and the tools,
|
|
238
|
+
resources and prompts are identical — both transports are built by the same `createMcpServer()`
|
|
239
|
+
below. Tool calls made that way are recorded in the collector's audit log with
|
|
240
|
+
`surface: "mcp-http"`; calls from this package over stdio are ordinary HTTP reads, recorded as
|
|
241
|
+
`http`. See the
|
|
242
|
+
[hosted transport guide](https://uptimizr.com/docs/guides/mcp/#hosted-transport-streamable-http).
|
|
243
|
+
|
|
180
244
|
## Programmatic use
|
|
181
245
|
|
|
182
246
|
```ts
|
|
@@ -188,8 +252,14 @@ const server = createMcpServer(client);
|
|
|
188
252
|
await server.connect(new StdioServerTransport());
|
|
189
253
|
```
|
|
190
254
|
|
|
191
|
-
|
|
192
|
-
|
|
255
|
+
`createMcpServer(client, options?)` takes an optional `options.capabilities` — the capability set of
|
|
256
|
+
the API key the server instance is bound to. The collector-hosted transport passes the key it
|
|
257
|
+
resolved, so a session's surface can only narrow to what its key may do; omitting it (as the stdio
|
|
258
|
+
entry point does) registers the whole read catalog and leaves enforcement to the collector.
|
|
259
|
+
|
|
260
|
+
The package also exports `readTools`, `rawTools`, `writeTools`, `CollectorError`, `version`, and the
|
|
261
|
+
related public types. The read-only analytics catalog (`readTools`), the `annotate`-gated
|
|
262
|
+
`writeTools`, and the collector client are defined in the
|
|
193
263
|
framework-agnostic [`@uptimizr/agent-core`](../agent-core/README.md) package and re-exported here,
|
|
194
264
|
so the agent tool surface is defined once and shared across the MCP server, the dashboard assistant,
|
|
195
265
|
and the demo assistant (ADR 0050). Building a non-MCP agent? Depend on `@uptimizr/agent-core`
|
package/dist/bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { createCollectorClient } from "@uptimizr/agent-core";
|
|
4
4
|
import { readMcpConfig } from "./config.js";
|
|
5
|
-
import { createMcpServer } from "./server.js";
|
|
5
|
+
import { createMcpServer, fetchKeyCapabilities } from "./server.js";
|
|
6
6
|
/**
|
|
7
7
|
* Entry point: read configuration from the environment, build a read-only
|
|
8
8
|
* collector client and MCP server, and serve over stdio (the transport MCP
|
|
@@ -11,7 +11,14 @@ import { createMcpServer } from "./server.js";
|
|
|
11
11
|
async function main() {
|
|
12
12
|
const config = readMcpConfig();
|
|
13
13
|
const client = createCollectorClient(config);
|
|
14
|
-
|
|
14
|
+
// Ask the collector what this key may do before building the server, so the
|
|
15
|
+
// capability-gated tools match the key: the raw-session tools of #314 only for
|
|
16
|
+
// `query:raw`, the metadata write tools of #310 only for `annotate`. Best
|
|
17
|
+
// effort — a collector older than `/api/v1/whoami`, an offline start or a
|
|
18
|
+
// transient failure yields "no extra capabilities", which serves the ordinary
|
|
19
|
+
// `query` surface this binary has always served.
|
|
20
|
+
const capabilities = await fetchKeyCapabilities(client);
|
|
21
|
+
const server = createMcpServer(client, { capabilities });
|
|
15
22
|
const transport = new StdioServerTransport();
|
|
16
23
|
await server.connect(transport);
|
|
17
24
|
}
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;GAIG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC7C,4EAA4E;IAC5E,+EAA+E;IAC/E,0EAA0E;IAC1E,0EAA0E;IAC1E,8EAA8E;IAC9E,iDAAiD;IACjD,MAAM,YAAY,GAAG,MAAM,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,OAAO,IAAI,CAAC,CAAC;IACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type EventType } from "@uptimizr/schema";
|
|
2
|
-
import { type ColumnSemantics, type DimensionId, type FilterId, type MetricCategory, type MetricComparison, type MetricGrain } from "@uptimizr/metrics";
|
|
2
|
+
import { type ColumnSemantics, type DimensionId, type FilterId, type MetricCapability, type MetricCategory, type MetricComparison, type MetricGrain } from "@uptimizr/metrics";
|
|
3
3
|
/**
|
|
4
4
|
* One tool the server exposes, described for self-discovery: its name, a human
|
|
5
5
|
* title, what it returns, and the parameter names it accepts.
|
|
@@ -34,11 +34,17 @@ export interface CapabilityMetricDescriptor {
|
|
|
34
34
|
category: MetricCategory;
|
|
35
35
|
/** What one row represents. */
|
|
36
36
|
grain: MetricGrain;
|
|
37
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The collector route it is served on, when it has one. `capability` names the
|
|
39
|
+
* API-key capability the route requires; it is absent for the ordinary
|
|
40
|
+
* `query` surface and `"query:raw"` for a metric gated behind raw-session
|
|
41
|
+
* retention (ADR 0051 §7).
|
|
42
|
+
*/
|
|
38
43
|
endpoint?: {
|
|
39
44
|
method: "GET";
|
|
40
45
|
path: string;
|
|
41
46
|
pathParams?: readonly FilterId[];
|
|
47
|
+
capability?: MetricCapability;
|
|
42
48
|
};
|
|
43
49
|
/** Group-by dimensions the rows are keyed by. */
|
|
44
50
|
dimensions: readonly DimensionId[];
|
|
@@ -93,10 +99,22 @@ export interface CapabilitiesDescriptor {
|
|
|
93
99
|
/** Human-oriented notes about scope and discovery. */
|
|
94
100
|
notes: readonly string[];
|
|
95
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* What the descriptor should describe.
|
|
104
|
+
*/
|
|
105
|
+
export interface BuildCapabilitiesOptions {
|
|
106
|
+
/**
|
|
107
|
+
* The capability set of the key this descriptor is being built for, as
|
|
108
|
+
* `GET /api/v1/whoami` reports it. Defaults to `["query"]` — the ordinary
|
|
109
|
+
* aggregate read surface, and the safe answer for a caller that has not
|
|
110
|
+
* looked the key up.
|
|
111
|
+
*/
|
|
112
|
+
capabilities?: readonly string[];
|
|
113
|
+
}
|
|
96
114
|
/**
|
|
97
115
|
* Build the capabilities descriptor from the metric registry and the event
|
|
98
116
|
* schema. Pure and synchronous — it introspects definitions only, never the
|
|
99
117
|
* collector, so it is safe to serve as a static resource.
|
|
100
118
|
*/
|
|
101
|
-
export declare function buildCapabilities(): CapabilitiesDescriptor;
|
|
119
|
+
export declare function buildCapabilities(options?: BuildCapabilitiesOptions): CapabilitiesDescriptor;
|
|
102
120
|
//# sourceMappingURL=capabilities.d.ts.map
|