@uptimizr/mcp 1.0.0 → 1.1.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 CHANGED
@@ -21,10 +21,48 @@ UPTIMIZR_COLLECTOR_URL="https://collect.example.com" UPTIMIZR_API_KEY="utk_…"
21
21
 
22
22
  ## Tools (read-only)
23
23
 
24
- `list_sessions`, `pointer_heatmap`, `world_heatmap`, `camera_heatmap`, `click_rays`, `flow_links`,
25
- `top_meshes`, `perf_summary`, `list_scenes`, `timeseries`, `event_counts`, `session_meta`,
26
- `scene_representation`. Most accept `since`/`until` (epoch ms) plus endpoint-specific filters
27
- (`scene`, `session`, `source`, `bins`, `cellSize`, `limit`).
24
+ <!-- generated:registry-tool-names:start — generated by `pnpm gen:docs`; edit the metric registry, not this table -->
25
+
26
+ `list_sessions`, `session_meta`, `scene_representation`, `list_scenes`, `timeseries`,
27
+ `event_counts`, `pointer_heatmap`, `mesh_uv_heatmap`, `world_heatmap`, `world_heatmap_stats`,
28
+ `gaze_heatmap`, `gaze_heatmap_stats`, `camera_heatmap`, `view_coverage_histogram`,
29
+ `position_heatmap`, `session_trajectory`, `aggregate_paths`, `scene_coverage`, `camera_distance`,
30
+ `click_rays`, `flow_links`, `top_meshes`, `mesh_sources`, `mesh_trend`, `mesh_dwell`,
31
+ `mesh_blind_spots`, `mesh_interaction_kinds`, `mesh_reachability`, `dead_clicks`, `rage_clicks`,
32
+ `hover_dwell`, `interaction_sources`, `top_input_actions`, `camera_gestures`, `navigation_stats`,
33
+ `backtrack_ratio`, `perf_summary`, `render_scale_truth`, `perf_distribution`, `fps_histogram`,
34
+ `frame_time_percentiles`, `jank_rate`, `perf_churn`, `perf_by_device`, `perf_by_scene`,
35
+ `perf_heatmap`, `compile_stalls`, `resource_summary`, `resource_percentiles`, `stability_counts`,
36
+ `graphics_diagnostics`, `error_heatmap`, `rendering_technology`, `capability_changes`,
37
+ `xr_rotation`, `xr_sources`, `xr_abandonment`, `xr_locomotion`, `xr_tracking_quality`,
38
+ `boundary_heatmap`, `boundary_heatmap_stats`, `xr_boundary_contacts`,
39
+ `ar_placement_time_to_place`, `ar_placement_attempts`, `ar_placement_surfaces`, `funnel`,
40
+ `scene_retention`, `load_bounce_funnel`, `variant_leaderboard`
41
+
42
+ <!-- generated:registry-tool-names:end -->
43
+
44
+ **69 tools, generated** from the `@uptimizr/metrics` semantic metric registry (ADR 0051 §1) — one per
45
+ metric the collector serves on a read endpoint. Names are the registry ids; the full table lives in
46
+ [README.md](./README.md), and `uptimizr://capabilities` enumerates them at runtime with each tool's
47
+ grain, column units and caveats.
48
+
49
+ Orientation: `list_sessions`, `list_scenes`, `session_meta`, `scene_representation`, `timeseries`,
50
+ `event_counts`. Attention: the `*_heatmap` family, `mesh_dwell`, `mesh_blind_spots`, `hover_dwell`.
51
+ Interaction: `top_meshes`, `mesh_sources`, `mesh_trend`, `mesh_interaction_kinds`,
52
+ `mesh_reachability`, `dead_clicks`, `rage_clicks`, `interaction_sources`, `top_input_actions`.
53
+ Navigation: `aggregate_paths`, `session_trajectory`, `scene_coverage`, `navigation_stats`,
54
+ `backtrack_ratio`, `camera_distance`, `camera_gestures`. Performance: `perf_summary`, `jank_rate`,
55
+ `perf_by_device`, `perf_by_scene`, `perf_heatmap`, `fps_histogram`, `frame_time_percentiles`,
56
+ `compile_stalls`, `resource_summary`, `rendering_technology`, … Errors: `stability_counts`,
57
+ `graphics_diagnostics`, `error_heatmap`, `capability_changes`. XR/AR: the `xr_*` and
58
+ `ar_placement_*` families. Conversion: `funnel`, `scene_retention`, `load_bounce_funnel`,
59
+ `variant_leaderboard`.
60
+
61
+ Most accept `since`/`until` (epoch ms) plus endpoint-specific filters (`scene`, `session`, `source`,
62
+ `bins`, `cellSize`, `limit`, `cameraMode`, `region`, …). Every tool declares an `outputSchema` and
63
+ returns `structuredContent` (`{ rows }`) alongside the JSON text — read the schema instead of
64
+ guessing the row shape, and read the tool description for the metric's caveats before trusting a
65
+ small sample.
28
66
 
29
67
  ## Rules for agents
30
68
 
@@ -32,9 +70,13 @@ UPTIMIZR_COLLECTOR_URL="https://collect.example.com" UPTIMIZR_API_KEY="utk_…"
32
70
  tools here. No data leaves the consumer's infrastructure (ADR 0003).
33
71
  - The server talks only to the configured collector with the consumer's `x-api-key`; never hardcode
34
72
  or log credentials.
35
- - Keep it a thin wrapper: a new tool = a new entry in the `readTools` catalog (defined in
36
- `@uptimizr/agent-core`) mapping to a documented query endpoint. Do not add aggregation/business
37
- logic — that lives in the collector.
73
+ - Keep it a thin wrapper: the `readTools` catalog (defined in `@uptimizr/agent-core`) is
74
+ **generated** from the `@uptimizr/metrics` metric registry, so a new tool = a new registry entry
75
+ for a documented query endpoint — never a hand-written array entry here. Do not add
76
+ aggregation/business logic — that lives in the collector.
77
+ - **No database driver, ever.** This server talks to a collector over HTTP; it must stay installable
78
+ with `npx`. Never add `@uptimizr/db` (or any package with a native/optional binary dependency) to
79
+ `dependencies` — `src/__tests__/dependencies.test.ts` fails if you do.
38
80
  - Tool definitions are pure (`buildRequest`) and must stay unit-testable without a live collector.
39
81
 
40
82
  ## Programmatic API
package/README.md CHANGED
@@ -75,37 +75,103 @@ For **GitHub Copilot CLI**, put the same entry in `~/.copilot/mcp-config.json` w
75
75
 
76
76
  ## Tools
77
77
 
78
- Most aggregate tools accept an optional time range (`since` / `until`, epoch ms) and the filters
79
- the underlying endpoint supports (`scene`, `session`, `source`, `bins`, `cellSize`, `interval`,
80
- `type`, `limit`, …). `session_meta` and `scene_representation` take their required IDs only.
81
-
82
- | Tool | Endpoint | Returns |
83
- | ---------------------- | ----------------------------------- | --------------------------------------------------- |
84
- | `list_sessions` | `/api/v1/sessions` | Recent sessions. |
85
- | `pointer_heatmap` | `/api/v1/heatmaps/pointer` | 2D pointer heatmap bins. |
86
- | `world_heatmap` | `/api/v1/heatmaps/world` | 3D world-space pointer voxels. |
87
- | `camera_heatmap` | `/api/v1/heatmaps/camera` | View-direction (spherical) bins. |
88
- | `click_rays` | `/api/v1/heatmaps/click-rays` | View-gated click rays. |
89
- | `flow_links` | `/api/v1/heatmaps/flow` | Gaze→mesh flow links. |
90
- | `top_meshes` | `/api/v1/meshes/top` | Most-interacted meshes. |
91
- | `perf_summary` | `/api/v1/perf` | FPS summary (avg/min/p50). |
92
- | `list_scenes` | `/api/v1/scenes` | Active scenes. |
93
- | `timeseries` | `/api/v1/timeseries` | Event-volume buckets over time. |
94
- | `event_counts` | `/api/v1/event-counts` | Per-event-type counts. |
95
- | `session_meta` | `/api/v1/sessions/:id/meta` | Coarse session descriptor (no raw event stream). |
96
- | `scene_representation` | `/api/v1/scenes/:id/representation` | Registered proxy geometry, if any. |
97
- | `funnel` | `/api/v1/funnel` | Ordered conversion funnel (`steps` JSON, ADR 0038). |
98
- | `aggregate_paths` | `/api/v1/paths` | Crowd-level desire-line routes (ADR 0037). |
99
- | `rendering_technology` | `/api/v1/rendering-technology` | WebGPU/WebGL2 + shading-language mix (ADR 0046). |
100
- | `xr_rotation` | `/api/v1/xr/rotation` | XR head-rotation rate (ADR 0048). |
101
- | `xr_sources` | `/api/v1/xr/sources` | XR input-source usage (ADR 0048). |
102
- | `xr_abandonment` | `/api/v1/xr/abandonment` | XR session abandonment (ADR 0048). |
103
- | `xr_locomotion` | `/api/v1/xr/locomotion` | XR locomotion & comfort (ADR 0048). |
78
+ The catalog is **generated from the semantic metric registry** in `@uptimizr/metrics` (ADR 0051 §1):
79
+ every aggregation the collector serves on a read endpoint is a tool — **69** of them. Each tool's
80
+ description carries the metric's interpretation notes and caveats, and each declares an MCP
81
+ `outputSchema` for the rows it returns (results come back as both `content` text and
82
+ `structuredContent`).
83
+
84
+ The table is **generated** from the collector's semantic metric registry (ADR 0051) — one row per
85
+ registered read metric. Read `uptimizr://capabilities` for each one's result grain, column units,
86
+ row schema and caveats; the same registry drives the collector's `GET /api/v1/openapi.json`.
87
+
88
+ Most tools accept an optional time range (`since` / `until`, epoch ms) and the filters the
89
+ underlying endpoint supports (`scene`, `session`, `source`, `bins`, `cellSize`, `interval`, `type`,
90
+ `limit`, `cameraMode`, `region`, …). `session_meta`, `session_trajectory` and
91
+ `scene_representation` take a required id.
92
+
93
+ <!-- generated:registry-tools:start — generated by `pnpm gen:docs`; edit the metric registry, not this table -->
94
+
95
+ | Tool | Endpoint | Returns | Parameters |
96
+ | ---------------------------- | ---------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
97
+ | `list_sessions` | `/api/v1/sessions` | Recent sessions | `since`, `until`, `bins`, `limit`, `cameraMode`, `format` |
98
+ | `session_meta` | `/api/v1/sessions/:id/meta` | Session descriptor | `session` |
99
+ | `scene_representation` | `/api/v1/scenes/:sceneId/representation` | Scene representation | `scene` |
100
+ | `list_scenes` | `/api/v1/scenes` | Active scenes | `since`, `until`, `limit`, `format` |
101
+ | `timeseries` | `/api/v1/timeseries` | Event volume over time | `since`, `until`, `interval`, `scene`, `type`, `format` |
102
+ | `event_counts` | `/api/v1/event-counts` | Counts per event type | `since`, `until`, `scene`, `format` |
103
+ | `pointer_heatmap` | `/api/v1/heatmaps/pointer` | 2D pointer heatmap | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
104
+ | `mesh_uv_heatmap` | `/api/v1/heatmaps/mesh-uv` | Per-mesh UV (texture-space) heatmap | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `mesh`, `format` |
105
+ | `world_heatmap` | `/api/v1/heatmaps/world` | 3D world-space pointer heatmap | `since`, `until`, `cellSize`, `limit`, `scene`, `source`, `cameraMode`, `region`, `format` |
106
+ | `world_heatmap_stats` | `/api/v1/heatmaps/world/stats` | World heatmap totals | `since`, `until`, `cellSize`, `scene`, `source`, `cameraMode`, `region`, `format` |
107
+ | `gaze_heatmap` | `/api/v1/heatmaps/gaze` | World-space gaze heatmap | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `cameraMode`, `region`, `format` |
108
+ | `gaze_heatmap_stats` | `/api/v1/heatmaps/gaze/stats` | Gaze heatmap totals | `since`, `until`, `cellSize`, `scene`, `session`, `cameraMode`, `region`, `format` |
109
+ | `camera_heatmap` | `/api/v1/heatmaps/camera` | View-direction heatmap | `since`, `until`, `bins`, `limit`, `scene`, `session`, `cameraMode`, `format` |
110
+ | `view_coverage_histogram` | `/api/v1/coverage/view-histogram` | 360° view-coverage histogram | `since`, `until`, `bins`, `limit`, `scene`, `session`, `cameraMode`, `format` |
111
+ | `position_heatmap` | `/api/v1/heatmaps/position` | Floor-plan camera-position heatmap | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `cameraMode`, `region`, `format` |
112
+ | `session_trajectory` | `/api/v1/sessions/:sessionId/trajectory` | Session walked path | `session`, `since`, `until`, `limit`, `scene`, `format` |
113
+ | `aggregate_paths` | `/api/v1/paths` | Aggregate desire-line paths | `since`, `until`, `cellSize`, `limit`, `scene`, `cameraMode`, `format` |
114
+ | `scene_coverage` | `/api/v1/coverage` | Scene coverage / dead zones | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `format` |
115
+ | `camera_distance` | `/api/v1/camera/distance` | Camera distance / zoom distribution | `since`, `until`, `centerX`, `centerY`, `centerZ`, `bucketSize`, `limit`, `scene`, `session`, `format` |
116
+ | `click_rays` | `/api/v1/heatmaps/click-rays` | View-gated click rays | `since`, `until`, `cellSize`, `limit`, `scene`, `source`, `session`, `format` |
117
+ | `flow_links` | `/api/v1/heatmaps/flow` | Gaze → mesh flow links | `since`, `until`, `bins`, `limit`, `scene`, `session`, `cameraMode`, `cellSize`, `groupByOrigin`, `originVoxel`, `format` |
118
+ | `top_meshes` | `/api/v1/meshes/top` | Most-interacted meshes | `since`, `until`, `bins`, `limit`, `session`, `format` |
119
+ | `mesh_sources` | `/api/v1/meshes/sources` | Mesh interactions by input source | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
120
+ | `mesh_trend` | `/api/v1/meshes/trend` | Per-mesh interaction trend | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `interval`, `format` |
121
+ | `mesh_dwell` | `/api/v1/meshes/dwell` | Per-object dwell / attention | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
122
+ | `mesh_blind_spots` | `/api/v1/meshes/blind-spots` | Blind spots / never-noticed meshes | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
123
+ | `mesh_interaction_kinds` | `/api/v1/meshes/kinds` | Interaction kinds per mesh | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
124
+ | `mesh_reachability` | `/api/v1/meshes/reachability` | Mesh reachability by distance | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `bucketSize`, `format` |
125
+ | `dead_clicks` | `/api/v1/clicks/dead` | Dead-click rate | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
126
+ | `rage_clicks` | `/api/v1/clicks/rage` | Rage-click clusters | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `interval`, `minRepeats`, `format` |
127
+ | `hover_dwell` | `/api/v1/hover/dwell` | Hover hesitation per object | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
128
+ | `interaction_sources` | `/api/v1/interactions/sources` | Interactions by input source | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
129
+ | `top_input_actions` | `/api/v1/input-actions/top` | Most-used shortcuts and actions | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
130
+ | `camera_gestures` | `/api/v1/camera-gestures` | Camera navigation gestures | `since`, `until`, `bins`, `limit`, `scene`, `session`, `source`, `cameraMode`, `format` |
131
+ | `navigation_stats` | `/api/v1/navigation` | Navigation effort per session | `since`, `until`, `moveThreshold`, `limit`, `scene`, `session`, `format` |
132
+ | `backtrack_ratio` | `/api/v1/backtrack` | Path retrace / backtracking | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `format` |
133
+ | `perf_summary` | `/api/v1/perf` | Rendering performance summary | `since`, `until`, `bins`, `limit`, `session`, `format` |
134
+ | `render_scale_truth` | `/api/v1/perf/render-scale` | Render-scale truth | `since`, `until`, `bins`, `limit`, `session`, `format` |
135
+ | `perf_distribution` | `/api/v1/perf/distribution` | FPS distribution (per-session) | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
136
+ | `fps_histogram` | `/api/v1/perf/fps-histogram` | Per-session median-FPS histogram | `since`, `until`, `bins`, `limit`, `scene`, `session`, `bucket`, `format` |
137
+ | `frame_time_percentiles` | `/api/v1/perf/frame-time` | Frame-time percentiles | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
138
+ | `jank_rate` | `/api/v1/perf/jank` | Jank rate | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
139
+ | `perf_churn` | `/api/v1/perf/churn` | Perf-correlated churn | `since`, `until`, `bins`, `limit`, `scene`, `session`, `windowMs`, `fpsThreshold`, `stallMs`, `format` |
140
+ | `perf_by_device` | `/api/v1/perf/by-device` | FPS by device class | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
141
+ | `perf_by_scene` | `/api/v1/perf/by-scene` | FPS by scene | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
142
+ | `perf_heatmap` | `/api/v1/heatmaps/perf` | Spatial FPS heatmap | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `format` |
143
+ | `compile_stalls` | `/api/v1/perf/compile-stalls` | Shader / pipeline compile stalls | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
144
+ | `resource_summary` | `/api/v1/perf/resources` | GPU / memory footprint summary | `since`, `until`, `bins`, `limit`, `session`, `format` |
145
+ | `resource_percentiles` | `/api/v1/perf/resource-percentiles` | GPU / memory footprint percentiles | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
146
+ | `stability_counts` | `/api/v1/perf/stability` | Stability incidents | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
147
+ | `graphics_diagnostics` | `/api/v1/graphics-diagnostics` | Engine diagnostic counts | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
148
+ | `error_heatmap` | `/api/v1/heatmaps/errors` | Spatial error heatmap | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `region`, `severity`, `category`, `errorKind`, `format` |
149
+ | `rendering_technology` | `/api/v1/rendering-technology` | Rendering-technology mix | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
150
+ | `capability_changes` | `/api/v1/capabilities` | Capability / fidelity transitions | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
151
+ | `xr_rotation` | `/api/v1/xr/rotation` | XR head-rotation rate | `since`, `until`, `rapidTurn`, `limit`, `scene`, `session`, `format` |
152
+ | `xr_sources` | `/api/v1/xr/sources` | XR input-source usage | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
153
+ | `xr_abandonment` | `/api/v1/xr/abandonment` | XR session abandonment | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
154
+ | `xr_locomotion` | `/api/v1/xr/locomotion` | XR locomotion & comfort | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
155
+ | `xr_tracking_quality` | `/api/v1/xr/tracking` | XR tracking quality | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
156
+ | `boundary_heatmap` | `/api/v1/heatmaps/boundary` | Guardian / boundary-touch heatmap | `since`, `until`, `cellSize`, `limit`, `scene`, `session`, `region`, `format` |
157
+ | `boundary_heatmap_stats` | `/api/v1/heatmaps/boundary/stats` | Boundary heatmap totals | `since`, `until`, `cellSize`, `scene`, `session`, `region`, `format` |
158
+ | `xr_boundary_contacts` | `/api/v1/xr/boundary-contacts` | Boundary contacts per session | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
159
+ | `ar_placement_time_to_place` | `/api/v1/ar/placement/time-to-place` | AR time-to-place distribution | `since`, `until`, `bins`, `limit`, `scene`, `session`, `bucketMs`, `format` |
160
+ | `ar_placement_attempts` | `/api/v1/ar/placement/attempts` | AR re-placement distribution | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
161
+ | `ar_placement_surfaces` | `/api/v1/ar/placement/surfaces` | AR placement surfaces | `since`, `until`, `bins`, `limit`, `scene`, `session`, `format` |
162
+ | `funnel` | `/api/v1/funnel` | Conversion funnel | `since`, `until`, `scene`, `cameraMode`, `steps`, `format` |
163
+ | `scene_retention` | `/api/v1/scene-retention` | Scene-to-scene retention | `since`, `until`, `limit`, `format` |
164
+ | `load_bounce_funnel` | `/api/v1/load-bounce` | Load → bounce funnel | `since`, `until`, `scene`, `bands`, `format` |
165
+ | `variant_leaderboard` | `/api/v1/variant-leaderboard` | Variant → conversion leaderboard | `since`, `until`, `scene`, `cameraMode`, `variant`, `conversion`, `limit`, `format` |
166
+
167
+ <!-- generated:registry-tools:end -->
104
168
 
105
169
  ## Resources & prompts
106
170
 
107
171
  The server also exposes read-only **resources** for self-discovery — `uptimizr://capabilities`
108
- (a machine-readable descriptor of event types, the tool catalog, and parameter semantics) and
172
+ (a machine-readable descriptor of event types, the tool catalog, parameter semantics, and
173
+ `metrics`: the collector's whole semantic metric registry, with each metric's result grain, column
174
+ units, row JSON Schema, row limits, interpretation and caveats — ADR 0051) and
109
175
  `uptimizr://scenes` (live scene ids) — and curated **prompts** (`weekly_scene_health`,
110
176
  `attention_hotspots`, `xr_comfort_review`) that drive the tools above. A remote Streamable HTTP
111
177
  transport is a deferred, auth-gated follow-up (ADR 0050 §7). See the
@@ -1,9 +1,14 @@
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
3
  /**
3
4
  * One tool the server exposes, described for self-discovery: its name, a human
4
- * title, what it returns, and the parameter names it accepts. Sourced directly
5
- * from the shared `@uptimizr/agent-core` catalog so it can never drift from the
6
- * tools actually registered (ADR 0050 §7).
5
+ * title, what it returns, and the parameter names it accepts.
6
+ *
7
+ * Derived from the **semantic metric registry** (`@uptimizr/metrics`, ADR
8
+ * 0051 §1) rather than restated here, so the catalog cannot drift from the
9
+ * metrics the collector can actually compute. `params` are the request
10
+ * parameters of the underlying query endpoint — the definitive *input schema*
11
+ * of a registered MCP tool is the one returned by `tools/list`.
7
12
  */
8
13
  export interface CapabilityToolDescriptor {
9
14
  name: string;
@@ -16,11 +21,58 @@ export interface CapabilityParamDescriptor {
16
21
  name: string;
17
22
  description: string;
18
23
  }
24
+ /**
25
+ * One registry metric, serialised for an agent: everything in the registry
26
+ * entry except the SQL `builder` (an implementation detail with no meaning
27
+ * outside `@uptimizr/db`), with the Zod `row` schema replaced by its JSON
28
+ * Schema so a client with no Zod can still validate or shape a result.
29
+ */
30
+ export interface CapabilityMetricDescriptor {
31
+ id: string;
32
+ title: string;
33
+ description: string;
34
+ category: MetricCategory;
35
+ /** What one row represents. */
36
+ grain: MetricGrain;
37
+ /** The collector route it is served on, when it has one. */
38
+ endpoint?: {
39
+ method: "GET";
40
+ path: string;
41
+ pathParams?: readonly FilterId[];
42
+ };
43
+ /** Group-by dimensions the rows are keyed by. */
44
+ dimensions: readonly DimensionId[];
45
+ /** Accepted request parameters. */
46
+ filters: readonly FilterId[];
47
+ /** JSON Schema of one result row. */
48
+ row: Record<string, unknown>;
49
+ /** Per-column semantics: description, unit, which column is the measure/label. */
50
+ columns: Readonly<Record<string, ColumnSemantics>>;
51
+ /** Registry-declared caps, so no consumer asks for an unbounded payload. */
52
+ limits: {
53
+ maxRows: number;
54
+ maxSummaryRows: number;
55
+ };
56
+ /** How to read the result. */
57
+ interpretation: string;
58
+ /** Small-sample, capture-gating and sampling-rate warnings. */
59
+ caveats: readonly string[];
60
+ /** Capture channels (ADR 0012) that must be enabled for it to have data. */
61
+ sourceChannels: readonly EventType[];
62
+ related: readonly string[];
63
+ comparable?: MetricComparison;
64
+ /**
65
+ * `true` for a **resource** read (a single stored object such as a session
66
+ * descriptor) rather than an aggregation over the event stream.
67
+ */
68
+ resource: boolean;
69
+ }
19
70
  /**
20
71
  * Machine-readable capabilities/schema descriptor an agent can read (via the
21
72
  * `uptimizr://capabilities` resource) to learn what it can ask before guessing.
22
73
  * It is strictly a description of the **read-only** surface — event types, the
23
- * tool catalog, and parameter semantics — and never itself queries any data.
74
+ * metric registry, the tool catalog, and parameter semantics — and never itself
75
+ * queries any data.
24
76
  */
25
77
  export interface CapabilitiesDescriptor {
26
78
  /** Wire-format version of the event schema (`@uptimizr/schema`). */
@@ -33,11 +85,16 @@ export interface CapabilitiesDescriptor {
33
85
  params: readonly CapabilityParamDescriptor[];
34
86
  /** The read-only tool catalog (each entry is one aggregate query endpoint). */
35
87
  tools: readonly CapabilityToolDescriptor[];
88
+ /**
89
+ * The full semantic metric registry: units, grain, dimensions, row schema,
90
+ * limits, interpretation and caveats per metric (ADR 0051 §1).
91
+ */
92
+ metrics: readonly CapabilityMetricDescriptor[];
36
93
  /** Human-oriented notes about scope and discovery. */
37
94
  notes: readonly string[];
38
95
  }
39
96
  /**
40
- * Build the capabilities descriptor from the live tool catalog and the event
97
+ * Build the capabilities descriptor from the metric registry and the event
41
98
  * schema. Pure and synchronous — it introspects definitions only, never the
42
99
  * collector, so it is safe to serve as a static resource.
43
100
  */
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG/E;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,+DAA+D;AAC/D,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,EAAE,IAAI,CAAC;IACf,oEAAoE;IACpE,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IACjC,0DAA0D;IAC1D,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7C,+EAA+E;IAC/E,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC3C,sDAAsD;IACtD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AA0BD;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,sBAAsB,CA6B1D"}
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE/E,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAErB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;;GASG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AAED,+DAA+D;AAC/D,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,cAAc,CAAC;IACzB,+BAA+B;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAA;KAAE,CAAC;IAC7E,iDAAiD;IACjD,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;IACnC,mCAAmC;IACnC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,kFAAkF;IAClF,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;IACnD,4EAA4E;IAC5E,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,4EAA4E;IAC5E,cAAc,EAAE,SAAS,SAAS,EAAE,CAAC;IACrC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,EAAE,IAAI,CAAC;IACf,oEAAoE;IACpE,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IACjC,0DAA0D;IAC1D,MAAM,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC7C,+EAA+E;IAC/E,KAAK,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC3C;;;OAGG;IACH,OAAO,EAAE,SAAS,0BAA0B,EAAE,CAAC;IAC/C,sDAAsD;IACtD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAmCD;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,sBAAsB,CA8C1D"}
@@ -1,58 +1,84 @@
1
1
  import { EVENT_TYPES, SCHEMA_VERSION } from "@uptimizr/schema";
2
- import { readTools } from "@uptimizr/agent-core";
3
- /**
4
- * Semantics for every parameter used across the tool catalog. Kept in one place
5
- * so a param means the same thing everywhere; {@link buildCapabilities} only
6
- * surfaces the entries a tool actually uses, and a unit test asserts coverage.
7
- */
8
- const PARAM_SEMANTICS = {
9
- since: "Start of the time range, epoch milliseconds (inclusive). Omit for all-time.",
10
- until: "End of the time range, epoch milliseconds (exclusive). Omit for up-to-now.",
11
- bins: "Grid resolution per axis for a binned heatmap (1–500).",
12
- limit: "Maximum rows to return (1–1000).",
13
- scene: "Restrict to one developer-assigned scene id (see the uptimizr://scenes resource).",
14
- session: "Scope the aggregate to a single session id.",
15
- cellSize: "Voxel edge length in world units for a spatial (world-space) aggregate.",
16
- interval: "Time-series bucket width in seconds.",
17
- type: "Restrict a time series to one event type (e.g. pointer_click).",
18
- source: "Input source filter: mouse, touch, stylus, pen, xr-controller, hand, gaze, transient, other.",
19
- cameraMode: "Camera navigation mode: 'viewer' (orbit) or 'first-person' (walkable).",
20
- rapidTurn: "Rapid-turn threshold in radians (0..π); XR view turns above this flag discomfort.",
21
- steps: "Funnel steps as a JSON-encoded array of ordered step predicates (ADR 0038).",
22
- sessionId: "The exact session id to describe.",
23
- sceneId: "The exact scene id to fetch.",
24
- };
2
+ import { z } from "zod";
3
+ import { FILTER_TARGETS, allMetrics, isResourceMetric, } from "@uptimizr/metrics";
4
+ /** Every request parameter a metric accepts: its path params, then its filters. */
5
+ function paramsOf(metric) {
6
+ return [...(metric.endpoint?.pathParams ?? []), ...metric.filters];
7
+ }
8
+ /** Serialise one registry entry, dropping `builder` and unwrapping `row`. */
9
+ function toMetricDescriptor(metric) {
10
+ const row = z.toJSONSchema(metric.row, {
11
+ io: "output",
12
+ unrepresentable: "any",
13
+ });
14
+ delete row.$schema;
15
+ return {
16
+ id: metric.id,
17
+ title: metric.title,
18
+ description: metric.description,
19
+ category: metric.category,
20
+ grain: metric.grain,
21
+ ...(metric.endpoint ? { endpoint: metric.endpoint } : {}),
22
+ dimensions: metric.dimensions,
23
+ filters: metric.filters,
24
+ row,
25
+ columns: metric.columns,
26
+ limits: metric.limits,
27
+ interpretation: metric.interpretation,
28
+ caveats: metric.caveats,
29
+ sourceChannels: metric.sourceChannels,
30
+ related: metric.related,
31
+ ...(metric.comparable ? { comparable: metric.comparable } : {}),
32
+ resource: isResourceMetric(metric),
33
+ };
34
+ }
25
35
  /**
26
- * Build the capabilities descriptor from the live tool catalog and the event
36
+ * Build the capabilities descriptor from the metric registry and the event
27
37
  * schema. Pure and synchronous — it introspects definitions only, never the
28
38
  * collector, so it is safe to serve as a static resource.
29
39
  */
30
40
  export function buildCapabilities() {
31
- const tools = readTools.map((tool) => ({
32
- name: tool.name,
33
- title: tool.title,
34
- description: tool.description,
35
- params: Object.keys(tool.inputSchema),
41
+ const metrics = allMetrics();
42
+ const served = metrics.filter((metric) => metric.endpoint != null);
43
+ const tools = served.map((metric) => ({
44
+ name: metric.id,
45
+ title: metric.title,
46
+ description: metric.description,
47
+ params: paramsOf(metric),
36
48
  }));
37
49
  const usedParams = new Set();
38
- for (const tool of tools)
39
- for (const p of tool.params)
40
- usedParams.add(p);
50
+ for (const metric of served)
51
+ for (const param of paramsOf(metric))
52
+ usedParams.add(param);
41
53
  const params = [...usedParams]
42
54
  .sort()
43
- .map((name) => ({ name, description: PARAM_SEMANTICS[name] ?? "" }));
55
+ .map((name) => ({ name, description: FILTER_TARGETS[name].description }));
44
56
  return {
45
57
  schemaVersion: SCHEMA_VERSION,
46
58
  readOnly: true,
47
59
  eventTypes: EVENT_TYPES,
48
60
  params,
49
61
  tools,
62
+ metrics: metrics.map(toMetricDescriptor),
50
63
  notes: [
51
64
  "This MCP surface is strictly read-only: aggregate, privacy-preserving queries only. " +
52
65
  "There are no ingestion, mutation, or raw per-session event tools (ADR 0003 / ADR 0017).",
66
+ "`metrics` is the collector's semantic metric registry (ADR 0051 §1): for each metric it " +
67
+ "gives the result `grain` (what one row is), the `columns` with their units, the JSON " +
68
+ "Schema of a row, `limits`, how to read it (`interpretation`) and how far to trust it " +
69
+ "(`caveats`). Read a metric's caveats before quoting its numbers.",
70
+ "`sourceChannels` names the capture channels (ADR 0012) that feed a metric. If a project " +
71
+ "has that channel disabled or sampled down, the metric is empty or proportional rather " +
72
+ "than exact — say so instead of reporting a zero as a finding.",
73
+ "`tools` lists the registry's served read surface and the request parameters of each " +
74
+ "underlying endpoint. It is exactly the set this server registers, because the tool " +
75
+ "catalog is generated from the same registry (ADR 0051 §1) — the authoritative input " +
76
+ "and output schemas of a registered tool are still the ones returned by `tools/list`.",
53
77
  "Enumerate the concrete scene ids for the `scene` parameter with the uptimizr://scenes " +
54
78
  "resource or the list_scenes tool; enumerate sessions with the list_sessions tool.",
55
79
  "All time ranges use epoch-millisecond `since`/`until`. Omit both for all-time.",
80
+ "The same registry drives the collector's OpenAPI document at GET /api/v1/openapi.json, " +
81
+ "which describes every endpoint below with its parameters and response schema.",
56
82
  ],
57
83
  };
58
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAkB,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AA0CjD;;;;GAIG;AACH,MAAM,eAAe,GAAqC;IACxD,KAAK,EAAE,6EAA6E;IACpF,KAAK,EAAE,4EAA4E;IACnF,IAAI,EAAE,wDAAwD;IAC9D,KAAK,EAAE,kCAAkC;IACzC,KAAK,EAAE,mFAAmF;IAC1F,OAAO,EAAE,6CAA6C;IACtD,QAAQ,EAAE,yEAAyE;IACnF,QAAQ,EAAE,sCAAsC;IAChD,IAAI,EAAE,gEAAgE;IACtE,MAAM,EACJ,8FAA8F;IAChG,UAAU,EAAE,wEAAwE;IACpF,SAAS,EAAE,mFAAmF;IAC9F,KAAK,EAAE,6EAA6E;IACpF,SAAS,EAAE,mCAAmC;IAC9C,OAAO,EAAE,8BAA8B;CACxC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,KAAK,GAA+B,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;KACtC,CAAC,CAAC,CAAC;IAEJ,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM;YAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAEzE,MAAM,MAAM,GAAgC,CAAC,GAAG,UAAU,CAAC;SACxD,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IAEvE,OAAO;QACL,aAAa,EAAE,cAAc;QAC7B,QAAQ,EAAE,IAAI;QACd,UAAU,EAAE,WAAW;QACvB,MAAM;QACN,KAAK;QACL,KAAK,EAAE;YACL,sFAAsF;gBACpF,yFAAyF;YAC3F,wFAAwF;gBACtF,mFAAmF;YACrF,gFAAgF;SACjF;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -7,6 +7,12 @@ import { type CollectorClient } from "@uptimizr/agent-core";
7
7
  * collector's JSON (ADR 0005 / ADR 0017). Alongside the tools it exposes
8
8
  * capability-discovery **resources** and curated analysis **prompts** so agents
9
9
  * can self-orient (ADR 0050 §7).
10
+ *
11
+ * The tool catalog is generated from the `@uptimizr/db` metric registry
12
+ * (ADR 0051 §1), so `tools/list` covers every metric the collector serves on an
13
+ * endpoint. Each tool advertises the registry-derived `outputSchema` and returns
14
+ * both `structuredContent` (the typed `{ rows }` envelope) and the `content`
15
+ * text a client without structured-output support still reads.
10
16
  */
11
17
  export declare function createMcpServer(client: CollectorClient): McpServer;
12
18
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAKvE;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA+BlE"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAsCvE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAqClE"}
package/dist/server.js CHANGED
@@ -1,8 +1,42 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
2
3
  import { readTools } from "@uptimizr/agent-core";
3
4
  import { registerResources } from "./resources.js";
4
5
  import { registerPrompts } from "./prompts.js";
5
6
  import { version } from "./version.js";
7
+ /**
8
+ * Normalise a collector response into the `{ rows }` envelope every tool's
9
+ * `outputSchema` declares. Aggregate endpoints already return an array; the
10
+ * single-object reads (a session descriptor, a scene representation, a one-row
11
+ * summary) become a one-element array so a client can treat every tool's
12
+ * structured result the same way. A `204`-style empty body becomes no rows.
13
+ */
14
+ function toRows(data) {
15
+ if (Array.isArray(data))
16
+ return data;
17
+ if (data == null)
18
+ return [];
19
+ return [data];
20
+ }
21
+ /**
22
+ * Validate the rows against the tool's registry-derived output schema and
23
+ * return the **parsed** result as the structured payload.
24
+ *
25
+ * The registry's numeric columns are strict `z.number()`: since ADR 0051 §2 the
26
+ * *collector* guarantees numbers, coercing each dialect's wire format
27
+ * (ClickHouse's string-encoded 64-bit integers, `pg`'s `int8`) at the single
28
+ * point rows leave its driver. So this is a check, not a repair — the advertised
29
+ * schema describes the API, and normalising here would hide a store regression.
30
+ *
31
+ * If the rows do not match the schema the raw rows are passed through; the SDK's
32
+ * own output validation then reports the offending column by name, which is the
33
+ * honest outcome for a collector that is out of contract.
34
+ */
35
+ function structuredRows(outputSchema, data) {
36
+ const rows = toRows(data);
37
+ const parsed = z.object(outputSchema).safeParse({ rows });
38
+ return parsed.success ? parsed.data : { rows };
39
+ }
6
40
  /**
7
41
  * Build the Uptimizr MCP server: a read-only `McpServer` whose tools each wrap
8
42
  * one collector query endpoint via the injected `CollectorClient`. The server
@@ -10,6 +44,12 @@ import { version } from "./version.js";
10
44
  * collector's JSON (ADR 0005 / ADR 0017). Alongside the tools it exposes
11
45
  * capability-discovery **resources** and curated analysis **prompts** so agents
12
46
  * can self-orient (ADR 0050 §7).
47
+ *
48
+ * The tool catalog is generated from the `@uptimizr/db` metric registry
49
+ * (ADR 0051 §1), so `tools/list` covers every metric the collector serves on an
50
+ * endpoint. Each tool advertises the registry-derived `outputSchema` and returns
51
+ * both `structuredContent` (the typed `{ rows }` envelope) and the `content`
52
+ * text a client without structured-output support still reads.
13
53
  */
14
54
  export function createMcpServer(client) {
15
55
  const server = new McpServer({ name: "uptimizr-mcp", version }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
@@ -18,11 +58,18 @@ export function createMcpServer(client) {
18
58
  title: tool.title,
19
59
  description: tool.description,
20
60
  inputSchema: tool.inputSchema,
61
+ ...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),
21
62
  }, async (args) => {
22
63
  try {
23
64
  const { path, params } = tool.buildRequest(args);
24
65
  const data = await client.get(path, params);
25
- return { content: [{ type: "text", text: JSON.stringify(data) }] };
66
+ const text = JSON.stringify(data);
67
+ if (!tool.outputSchema)
68
+ return { content: [{ type: "text", text }] };
69
+ return {
70
+ content: [{ type: "text", text }],
71
+ structuredContent: structuredRows(tool.outputSchema, data),
72
+ };
26
73
  }
27
74
  catch (err) {
28
75
  const message = err instanceof Error ? err.message : String(err);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAwB,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,MAAuB;IACrD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,EACjC,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAC5D,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,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;SAC9B,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;YACb,IAAI,CAAC;gBACH,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,IAA+B,CAAC,CAAC;gBAC5E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC5C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACrE,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACjE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YACnF,CAAC;QACH,CAAC,CACF,CAAC;IACJ,CAAC;IAED,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,eAAe,CAAC,MAAM,CAAC,CAAC;IAExB,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -10,9 +10,33 @@
10
10
  - [Integration & API reference](https://github.com/RaananW/Uptimizr/blob/main/docs/integration.md): the underlying query endpoints.
11
11
  - [Architecture Decision Records](https://github.com/RaananW/Uptimizr/tree/main/docs/adr): privacy model (0003), thin backends (0005), consumer-facing agents (0017).
12
12
 
13
+ ## Tools (read-only)
14
+
15
+ <!-- generated:registry-tool-names:start — generated by `pnpm gen:docs`; edit the metric registry, not this list -->
16
+
17
+ `list_sessions`, `session_meta`, `scene_representation`, `list_scenes`, `timeseries`,
18
+ `event_counts`, `pointer_heatmap`, `mesh_uv_heatmap`, `world_heatmap`, `world_heatmap_stats`,
19
+ `gaze_heatmap`, `gaze_heatmap_stats`, `camera_heatmap`, `view_coverage_histogram`,
20
+ `position_heatmap`, `session_trajectory`, `aggregate_paths`, `scene_coverage`, `camera_distance`,
21
+ `click_rays`, `flow_links`, `top_meshes`, `mesh_sources`, `mesh_trend`, `mesh_dwell`,
22
+ `mesh_blind_spots`, `mesh_interaction_kinds`, `mesh_reachability`, `dead_clicks`, `rage_clicks`,
23
+ `hover_dwell`, `interaction_sources`, `top_input_actions`, `camera_gestures`, `navigation_stats`,
24
+ `backtrack_ratio`, `perf_summary`, `render_scale_truth`, `perf_distribution`, `fps_histogram`,
25
+ `frame_time_percentiles`, `jank_rate`, `perf_churn`, `perf_by_device`, `perf_by_scene`,
26
+ `perf_heatmap`, `compile_stalls`, `resource_summary`, `resource_percentiles`, `stability_counts`,
27
+ `graphics_diagnostics`, `error_heatmap`, `rendering_technology`, `capability_changes`,
28
+ `xr_rotation`, `xr_sources`, `xr_abandonment`, `xr_locomotion`, `xr_tracking_quality`,
29
+ `boundary_heatmap`, `boundary_heatmap_stats`, `xr_boundary_contacts`,
30
+ `ar_placement_time_to_place`, `ar_placement_attempts`, `ar_placement_surfaces`, `funnel`,
31
+ `scene_retention`, `load_bounce_funnel`, `variant_leaderboard`
32
+
33
+ <!-- generated:registry-tool-names:end -->
34
+
13
35
  ## Key exports
14
36
 
15
37
  - `createMcpServer(client)` — build the read-only MCP server.
16
38
  - `createCollectorClient(config)` — the thin GET-only collector client.
17
39
  - `readMcpConfig()` — read `UPTIMIZR_COLLECTOR_URL` + `UPTIMIZR_API_KEY` from the environment.
18
- - `readTools` — the read-only tool catalog (one entry per query endpoint).
40
+ - `readTools` — the read-only tool catalog (one entry per query endpoint), generated from the
41
+ `@uptimizr/metrics` metric registry: 69 tools, each with an input schema, an MCP `outputSchema` and the
42
+ metric's interpretation notes and caveats in its description (ADR 0051).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uptimizr/mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Read-only Model Context Protocol (MCP) server over an Uptimizr collector's query API — let an agent ask questions of your own 3D analytics, on your own infrastructure.",
5
5
  "keywords": [
6
6
  "uptimizr",
@@ -49,11 +49,12 @@
49
49
  "dependencies": {
50
50
  "@modelcontextprotocol/sdk": "^1.30.0",
51
51
  "zod": "^4.5.4",
52
- "@uptimizr/agent-core": "1.0.0",
53
- "@uptimizr/schema": "1.0.0"
52
+ "@uptimizr/agent-core": "1.1.0",
53
+ "@uptimizr/metrics": "0.1.0",
54
+ "@uptimizr/schema": "1.1.0"
54
55
  },
55
56
  "devDependencies": {
56
- "@types/node": "^26.4.0",
57
+ "@types/node": "^26.4.1",
57
58
  "tsx": "^4.23.13",
58
59
  "vitest": "^4.1.11"
59
60
  },
@@ -68,6 +69,6 @@
68
69
  "typecheck": "tsc -p tsconfig.json --noEmit",
69
70
  "test": "vitest run",
70
71
  "lint": "eslint .",
71
- "clean": "rm -rf dist *.tsbuildinfo"
72
+ "clean": "rimraf -g dist *.tsbuildinfo"
72
73
  }
73
74
  }