@gomcp/analytics 0.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/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/analytics.d.ts +68 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +113 -0
- package/dist/analytics.js.map +1 -0
- package/dist/collector.d.ts +44 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/collector.js +132 -0
- package/dist/collector.js.map +1 -0
- package/dist/exporters/console.d.ts +6 -0
- package/dist/exporters/console.d.ts.map +1 -0
- package/dist/exporters/console.js +18 -0
- package/dist/exporters/console.js.map +1 -0
- package/dist/exporters/custom.d.ts +7 -0
- package/dist/exporters/custom.d.ts.map +1 -0
- package/dist/exporters/custom.js +15 -0
- package/dist/exporters/custom.js.map +1 -0
- package/dist/exporters/json.d.ts +6 -0
- package/dist/exporters/json.d.ts.map +1 -0
- package/dist/exporters/json.js +13 -0
- package/dist/exporters/json.js.map +1 -0
- package/dist/exporters/otlp.d.ts +9 -0
- package/dist/exporters/otlp.d.ts.map +1 -0
- package/dist/exporters/otlp.js +83 -0
- package/dist/exporters/otlp.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +14 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +184 -0
- package/dist/middleware.js.map +1 -0
- package/dist/tracing.d.ts +37 -0
- package/dist/tracing.d.ts.map +1 -0
- package/dist/tracing.js +67 -0
- package/dist/tracing.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +44 -0
- package/dist/utils.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mouaad Aallam
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# @gomcp/analytics
|
|
2
|
+
|
|
3
|
+
Lightweight analytics and observability for [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) servers. Zero required dependencies, framework-agnostic, works at the JSON-RPC transport level.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Transport-level interception** — works with any MCP server (official SDK, FastMCP, custom)
|
|
8
|
+
- **Handler wrapping** — instrument individual tool handlers for granular control
|
|
9
|
+
- **Multiple exporters** — console, JSON file, OpenTelemetry OTLP, or custom functions
|
|
10
|
+
- **In-memory stats** — p50/p95/p99 latencies, error rates, call counts per tool
|
|
11
|
+
- **Sampling** — configurable sample rate to control overhead
|
|
12
|
+
- **Zero required deps** — only `@modelcontextprotocol/sdk` as a peer dependency
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @gomcp/analytics
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
25
|
+
import { McpAnalytics } from "@gomcp/analytics";
|
|
26
|
+
|
|
27
|
+
// 1. Create analytics instance
|
|
28
|
+
const analytics = new McpAnalytics({
|
|
29
|
+
exporter: "console",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// 2. Create your server and transport
|
|
33
|
+
const server = new McpServer({ name: "my-server", version: "1.0.0" });
|
|
34
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => crypto.randomUUID() });
|
|
35
|
+
|
|
36
|
+
// 3. Instrument the transport (intercepts all tool calls automatically)
|
|
37
|
+
const trackedTransport = analytics.instrument(transport);
|
|
38
|
+
await server.connect(trackedTransport);
|
|
39
|
+
|
|
40
|
+
// 4. Access stats at any time
|
|
41
|
+
console.log(analytics.getStats());
|
|
42
|
+
// { totalCalls: 42, errorRate: 0.02, tools: { search: { count: 30, p50Ms: 120, ... } } }
|
|
43
|
+
|
|
44
|
+
// 5. Clean shutdown
|
|
45
|
+
await analytics.shutdown();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## API
|
|
49
|
+
|
|
50
|
+
### `new McpAnalytics(config)`
|
|
51
|
+
|
|
52
|
+
Create an analytics instance.
|
|
53
|
+
|
|
54
|
+
| Option | Type | Default | Description |
|
|
55
|
+
|-------------------|----------------------------------------------------------|---------|-----------------------------------------------------------|
|
|
56
|
+
| `exporter` | `"console" \| "json" \| "otlp" \| Function` | — | Where to send metrics (required) |
|
|
57
|
+
| `json` | `{ path: string }` | — | JSON file config (required when `exporter: "json"`) |
|
|
58
|
+
| `otlp` | `{ endpoint: string, headers?: Record<string, string> }` | — | OTLP config (required when `exporter: "otlp"`) |
|
|
59
|
+
| `sampleRate` | `number` | `1.0` | Fraction of calls to sample (0.0 to 1.0) |
|
|
60
|
+
| `flushIntervalMs` | `number` | `5000` | How often to flush events to the exporter |
|
|
61
|
+
| `maxBufferSize` | `number` | `10000` | Max events in the ring buffer |
|
|
62
|
+
| `metadata` | `Record<string, string>` | — | Metadata added to every event |
|
|
63
|
+
| `tracing` | `boolean` | `false` | Create OpenTelemetry spans via the global tracer provider |
|
|
64
|
+
|
|
65
|
+
### `analytics.instrument(transport)`
|
|
66
|
+
|
|
67
|
+
Wrap an MCP transport to automatically intercept all `tools/call` requests and responses. Returns a proxy transport that can be used in place of the original.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const trackedTransport = analytics.instrument(transport);
|
|
71
|
+
await server.connect(trackedTransport);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `analytics.track(handler, toolName?)`
|
|
75
|
+
|
|
76
|
+
Wrap a tool handler function to record metrics. Use this when you want per-handler control instead of transport-level interception.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
server.tool("search", schema, analytics.track(async (params) => {
|
|
80
|
+
return await doSearch(params);
|
|
81
|
+
}, "search"));
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `analytics.getStats()`
|
|
85
|
+
|
|
86
|
+
Returns an `AnalyticsSnapshot` with aggregated metrics:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
interface AnalyticsSnapshot {
|
|
90
|
+
totalCalls: number;
|
|
91
|
+
totalErrors: number;
|
|
92
|
+
errorRate: number;
|
|
93
|
+
uptimeMs: number;
|
|
94
|
+
tools: Record<string, ToolStats>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface ToolStats {
|
|
98
|
+
count: number;
|
|
99
|
+
errorCount: number;
|
|
100
|
+
errorRate: number;
|
|
101
|
+
p50Ms: number;
|
|
102
|
+
p95Ms: number;
|
|
103
|
+
p99Ms: number;
|
|
104
|
+
avgMs: number;
|
|
105
|
+
lastCalledAt: number; // Unix timestamp ms
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `analytics.getToolStats(toolName)`
|
|
110
|
+
|
|
111
|
+
Get stats for a specific tool. Returns `undefined` if the tool hasn't been called.
|
|
112
|
+
|
|
113
|
+
### `analytics.flush()`
|
|
114
|
+
|
|
115
|
+
Force-flush all pending events to the exporter.
|
|
116
|
+
|
|
117
|
+
### `analytics.reset()`
|
|
118
|
+
|
|
119
|
+
Clear all collected data.
|
|
120
|
+
|
|
121
|
+
### `analytics.shutdown()`
|
|
122
|
+
|
|
123
|
+
Stop the flush timer and flush remaining events. Call this on process exit.
|
|
124
|
+
|
|
125
|
+
## Exporters
|
|
126
|
+
|
|
127
|
+
### Console
|
|
128
|
+
|
|
129
|
+
Pretty-prints batches to stdout:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
new McpAnalytics({ exporter: "console" });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### JSON File
|
|
136
|
+
|
|
137
|
+
Appends events as JSONL (one JSON object per line):
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
new McpAnalytics({
|
|
141
|
+
exporter: "json",
|
|
142
|
+
json: { path: "./analytics.jsonl" },
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### OpenTelemetry OTLP
|
|
147
|
+
|
|
148
|
+
Sends events as OpenTelemetry spans. Requires `@opentelemetry/api`, `@opentelemetry/sdk-trace-base`, and `@opentelemetry/exporter-trace-otlp-http` as peer dependencies (dynamically imported only when used):
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install @opentelemetry/api @opentelemetry/sdk-trace-base @opentelemetry/exporter-trace-otlp-http
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
new McpAnalytics({
|
|
156
|
+
exporter: "otlp",
|
|
157
|
+
otlp: {
|
|
158
|
+
endpoint: "http://localhost:4318/v1/traces",
|
|
159
|
+
headers: { "Authorization": "Bearer ..." },
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Custom Function
|
|
165
|
+
|
|
166
|
+
Provide your own export function:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
new McpAnalytics({
|
|
170
|
+
exporter: async (events) => {
|
|
171
|
+
await fetch("https://my-analytics.example.com/ingest", {
|
|
172
|
+
method: "POST",
|
|
173
|
+
body: JSON.stringify(events),
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Tracing (dd-trace / OpenTelemetry)
|
|
180
|
+
|
|
181
|
+
When you use an APM like [dd-trace](https://github.com/DataDog/dd-trace-js) that registers itself as the global OpenTelemetry provider, you can make MCP tool calls appear as spans in your existing traces with zero extra configuration:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import "dd-trace/init"; // sets up dd-trace as global OTel provider
|
|
185
|
+
|
|
186
|
+
import { McpAnalytics } from "@gomcp/analytics";
|
|
187
|
+
|
|
188
|
+
const analytics = new McpAnalytics({
|
|
189
|
+
exporter: "console",
|
|
190
|
+
tracing: true, // creates spans via the global tracer provider
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const tracked = analytics.instrument(transport);
|
|
194
|
+
await server.connect(tracked);
|
|
195
|
+
// Tool calls now appear as "mcp.tool_call" spans in Datadog
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
This works with any OTel-compatible provider (Datadog, New Relic, Honeycomb, etc.). The `tracing` flag dynamically imports `@opentelemetry/api` and uses the global tracer — no OTLP exporter setup needed.
|
|
199
|
+
|
|
200
|
+
When using `analytics.track()` (handler wrapping), the handler executes inside the span context, so any downstream OTel-instrumented calls (HTTP, DB, etc.) become children of the MCP tool span.
|
|
201
|
+
|
|
202
|
+
### OTLP exporter with global provider
|
|
203
|
+
|
|
204
|
+
If you're already using the OTLP exporter and want it to send spans through your global provider instead of creating an isolated one:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
new McpAnalytics({
|
|
208
|
+
exporter: "otlp",
|
|
209
|
+
otlp: {
|
|
210
|
+
endpoint: "unused-when-global", // ignored when useGlobalProvider is true
|
|
211
|
+
useGlobalProvider: true,
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Span attributes
|
|
217
|
+
|
|
218
|
+
Each `mcp.tool_call` span includes these attributes:
|
|
219
|
+
|
|
220
|
+
| Attribute | Description |
|
|
221
|
+
|--------------------------|-------------------------------------------------|
|
|
222
|
+
| `mcp.tool.name` | Tool name |
|
|
223
|
+
| `mcp.tool.input_size` | Input size in bytes |
|
|
224
|
+
| `mcp.tool.duration_ms` | Duration (OTLP exporter only) |
|
|
225
|
+
| `mcp.tool.success` | Whether the call succeeded (OTLP exporter only) |
|
|
226
|
+
| `mcp.tool.output_size` | Output size in bytes (OTLP exporter only) |
|
|
227
|
+
| `mcp.tool.error_message` | Error message if failed (OTLP exporter only) |
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
MIT
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
2
|
+
import type { AnalyticsConfig, AnalyticsSnapshot, InstrumentedTransport, ToolStats } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* MCP Analytics — lightweight observability for MCP servers.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const analytics = new McpAnalytics({ exporter: "console" });
|
|
9
|
+
*
|
|
10
|
+
* // Instrument a transport
|
|
11
|
+
* const tracked = analytics.instrument(transport);
|
|
12
|
+
* await server.connect(tracked);
|
|
13
|
+
*
|
|
14
|
+
* // Or wrap individual handlers
|
|
15
|
+
* server.tool("search", schema, analytics.track(handler));
|
|
16
|
+
*
|
|
17
|
+
* // Get stats
|
|
18
|
+
* console.log(analytics.getStats());
|
|
19
|
+
*
|
|
20
|
+
* // Shutdown
|
|
21
|
+
* await analytics.flush();
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class McpAnalytics {
|
|
25
|
+
private readonly collector;
|
|
26
|
+
private readonly sampleRate;
|
|
27
|
+
private readonly metadata?;
|
|
28
|
+
private readonly tracing;
|
|
29
|
+
constructor(config: AnalyticsConfig);
|
|
30
|
+
/**
|
|
31
|
+
* Instrument an MCP transport to automatically track all tool calls.
|
|
32
|
+
* Returns proxy transport that can be used in place of the original.
|
|
33
|
+
*/
|
|
34
|
+
instrument(transport: Transport): InstrumentedTransport;
|
|
35
|
+
/**
|
|
36
|
+
* Wrap a tool handler function to track its execution.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* server.tool("search", schema, analytics.track(async (params) => {
|
|
41
|
+
* return await doSearch(params);
|
|
42
|
+
* }, "search"));
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
track<TArgs extends unknown[], TResult>(handler: (...args: TArgs) => TResult | Promise<TResult>, toolName?: string): (...args: TArgs) => Promise<TResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Get a snapshot of all analytics data.
|
|
48
|
+
*/
|
|
49
|
+
getStats(): AnalyticsSnapshot;
|
|
50
|
+
/**
|
|
51
|
+
* Get stats for a specific tool.
|
|
52
|
+
*/
|
|
53
|
+
getToolStats(toolName: string): ToolStats | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Flush all pending events to the exporter.
|
|
56
|
+
*/
|
|
57
|
+
flush(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Reset all collected data.
|
|
60
|
+
*/
|
|
61
|
+
reset(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Stop the analytics instance (clears flush timer and flushes remaining events).
|
|
64
|
+
*/
|
|
65
|
+
shutdown(): Promise<void>;
|
|
66
|
+
private resolveExporter;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAQ/E,OAAO,KAAK,EACV,eAAe,EACf,iBAAiB,EAEjB,qBAAqB,EACrB,SAAS,EACV,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAyB;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,MAAM,EAAE,eAAe;IAanC;;;OAGG;IACH,UAAU,CAAC,SAAS,EAAE,SAAS,GAAG,qBAAqB;IAUvD;;;;;;;;;OASG;IACH,KAAK,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACpC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACvD,QAAQ,CAAC,EAAE,MAAM,GAChB,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC;IAYvC;;OAEG;IACH,QAAQ,IAAI,iBAAiB;IAI7B;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIrD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,OAAO,CAAC,eAAe;CA0BxB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Collector } from "./collector.js";
|
|
2
|
+
import { createConsoleExporter } from "./exporters/console.js";
|
|
3
|
+
import { createCustomExporter } from "./exporters/custom.js";
|
|
4
|
+
import { createJsonExporter } from "./exporters/json.js";
|
|
5
|
+
import { createOtlpExporter } from "./exporters/otlp.js";
|
|
6
|
+
import { instrumentTransport, wrapToolHandler } from "./middleware.js";
|
|
7
|
+
/**
|
|
8
|
+
* MCP Analytics — lightweight observability for MCP servers.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const analytics = new McpAnalytics({ exporter: "console" });
|
|
13
|
+
*
|
|
14
|
+
* // Instrument a transport
|
|
15
|
+
* const tracked = analytics.instrument(transport);
|
|
16
|
+
* await server.connect(tracked);
|
|
17
|
+
*
|
|
18
|
+
* // Or wrap individual handlers
|
|
19
|
+
* server.tool("search", schema, analytics.track(handler));
|
|
20
|
+
*
|
|
21
|
+
* // Get stats
|
|
22
|
+
* console.log(analytics.getStats());
|
|
23
|
+
*
|
|
24
|
+
* // Shutdown
|
|
25
|
+
* await analytics.flush();
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class McpAnalytics {
|
|
29
|
+
collector;
|
|
30
|
+
sampleRate;
|
|
31
|
+
metadata;
|
|
32
|
+
tracing;
|
|
33
|
+
constructor(config) {
|
|
34
|
+
this.sampleRate = config.sampleRate ?? 1;
|
|
35
|
+
this.metadata = config.metadata;
|
|
36
|
+
this.tracing = config.tracing ?? false;
|
|
37
|
+
const exporter = this.resolveExporter(config);
|
|
38
|
+
this.collector = new Collector(config.maxBufferSize ?? 10_000, exporter, config.flushIntervalMs ?? 5_000);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Instrument an MCP transport to automatically track all tool calls.
|
|
42
|
+
* Returns proxy transport that can be used in place of the original.
|
|
43
|
+
*/
|
|
44
|
+
instrument(transport) {
|
|
45
|
+
return instrumentTransport(transport, this.collector, this.sampleRate, this.metadata, this.tracing);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Wrap a tool handler function to track its execution.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* server.tool("search", schema, analytics.track(async (params) => {
|
|
53
|
+
* return await doSearch(params);
|
|
54
|
+
* }, "search"));
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
track(handler, toolName) {
|
|
58
|
+
const name = toolName ?? (handler.name || "anonymous");
|
|
59
|
+
return wrapToolHandler(name, handler, this.collector, this.sampleRate, this.metadata, this.tracing);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get a snapshot of all analytics data.
|
|
63
|
+
*/
|
|
64
|
+
getStats() {
|
|
65
|
+
return this.collector.getStats();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get stats for a specific tool.
|
|
69
|
+
*/
|
|
70
|
+
getToolStats(toolName) {
|
|
71
|
+
return this.collector.getToolStats(toolName);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Flush all pending events to the exporter.
|
|
75
|
+
*/
|
|
76
|
+
async flush() {
|
|
77
|
+
await this.collector.flush();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reset all collected data.
|
|
81
|
+
*/
|
|
82
|
+
reset() {
|
|
83
|
+
this.collector.reset();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Stop the analytics instance (clears flush timer and flushes remaining events).
|
|
87
|
+
*/
|
|
88
|
+
async shutdown() {
|
|
89
|
+
await this.collector.destroy();
|
|
90
|
+
}
|
|
91
|
+
resolveExporter(config) {
|
|
92
|
+
if (typeof config.exporter === "function") {
|
|
93
|
+
return createCustomExporter(config.exporter);
|
|
94
|
+
}
|
|
95
|
+
switch (config.exporter) {
|
|
96
|
+
case "console":
|
|
97
|
+
return createConsoleExporter();
|
|
98
|
+
case "json": {
|
|
99
|
+
if (!config.json) {
|
|
100
|
+
throw new Error('McpAnalytics: "json" exporter requires a "json" config with "path"');
|
|
101
|
+
}
|
|
102
|
+
return createJsonExporter(config.json);
|
|
103
|
+
}
|
|
104
|
+
case "otlp": {
|
|
105
|
+
if (!config.otlp) {
|
|
106
|
+
throw new Error('McpAnalytics: "otlp" exporter requires an "otlp" config with "endpoint"');
|
|
107
|
+
}
|
|
108
|
+
return createOtlpExporter(config.otlp);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../src/analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AnalyticsSnapshot, ExporterFn, ToolCallEvent, ToolStats } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory ring buffer that collects ToolCallEvents, computes stats,
|
|
4
|
+
* and periodically flushes to an exporter.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Collector {
|
|
7
|
+
private readonly maxBufferSize;
|
|
8
|
+
private readonly exporter;
|
|
9
|
+
private readonly buffer;
|
|
10
|
+
private readonly accumulators;
|
|
11
|
+
private totalCalls;
|
|
12
|
+
private totalErrors;
|
|
13
|
+
private readonly startTime;
|
|
14
|
+
private flushTimer;
|
|
15
|
+
/** Events accumulated since last flush, to be sent to the exporter */
|
|
16
|
+
private pending;
|
|
17
|
+
constructor(maxBufferSize: number, exporter: ExporterFn, flushIntervalMs: number);
|
|
18
|
+
/**
|
|
19
|
+
* Record a new tool call event.
|
|
20
|
+
*/
|
|
21
|
+
record(event: ToolCallEvent): void;
|
|
22
|
+
/**
|
|
23
|
+
* Get aggregated stats for all tools.
|
|
24
|
+
*/
|
|
25
|
+
getStats(): AnalyticsSnapshot;
|
|
26
|
+
/**
|
|
27
|
+
* Get stats for a single tool.
|
|
28
|
+
*/
|
|
29
|
+
getToolStats(toolName: string): ToolStats | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Flush pending events to the exporter.
|
|
32
|
+
*/
|
|
33
|
+
flush(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Reset all collected data.
|
|
36
|
+
*/
|
|
37
|
+
reset(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Stop the flush timer and flush remaining events.
|
|
40
|
+
*/
|
|
41
|
+
destroy(): Promise<void>;
|
|
42
|
+
private accToStats;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=collector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC;AAepB;;;GAGG;AACH,qBAAa,SAAS;IAYlB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAZ3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsC;IACnE,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IAExC,OAAO,CAAC,UAAU,CAA6C;IAC/D,sEAAsE;IACtE,OAAO,CAAC,OAAO,CAAuB;gBAGnB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,UAAU,EACrC,eAAe,EAAE,MAAM;IAiBzB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IA8BlC;;OAEG;IACH,QAAQ,IAAI,iBAAiB;IAc7B;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAMrD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B,OAAO,CAAC,UAAU;CAYnB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { percentile, sortedInsert } from "./utils.js";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory ring buffer that collects ToolCallEvents, computes stats,
|
|
4
|
+
* and periodically flushes to an exporter.
|
|
5
|
+
*/
|
|
6
|
+
export class Collector {
|
|
7
|
+
maxBufferSize;
|
|
8
|
+
exporter;
|
|
9
|
+
buffer = [];
|
|
10
|
+
accumulators = new Map();
|
|
11
|
+
totalCalls = 0;
|
|
12
|
+
totalErrors = 0;
|
|
13
|
+
startTime = Date.now();
|
|
14
|
+
flushTimer;
|
|
15
|
+
/** Events accumulated since last flush, to be sent to the exporter */
|
|
16
|
+
pending = [];
|
|
17
|
+
constructor(maxBufferSize, exporter, flushIntervalMs) {
|
|
18
|
+
this.maxBufferSize = maxBufferSize;
|
|
19
|
+
this.exporter = exporter;
|
|
20
|
+
if (flushIntervalMs > 0) {
|
|
21
|
+
this.flushTimer = setInterval(() => {
|
|
22
|
+
void this.flush();
|
|
23
|
+
}, flushIntervalMs);
|
|
24
|
+
// Don't hold the process open for analytics flushing
|
|
25
|
+
if (this.flushTimer &&
|
|
26
|
+
typeof this.flushTimer === "object" &&
|
|
27
|
+
"unref" in this.flushTimer) {
|
|
28
|
+
this.flushTimer.unref();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Record a new tool call event.
|
|
34
|
+
*/
|
|
35
|
+
record(event) {
|
|
36
|
+
// Ring buffer: drop oldest when full
|
|
37
|
+
if (this.buffer.length >= this.maxBufferSize) {
|
|
38
|
+
this.buffer.shift();
|
|
39
|
+
}
|
|
40
|
+
this.buffer.push(event);
|
|
41
|
+
this.pending.push(event);
|
|
42
|
+
this.totalCalls++;
|
|
43
|
+
if (!event.success)
|
|
44
|
+
this.totalErrors++;
|
|
45
|
+
// Update per-tool accumulator
|
|
46
|
+
let acc = this.accumulators.get(event.toolName);
|
|
47
|
+
if (!acc) {
|
|
48
|
+
acc = {
|
|
49
|
+
count: 0,
|
|
50
|
+
errorCount: 0,
|
|
51
|
+
totalMs: 0,
|
|
52
|
+
durations: [],
|
|
53
|
+
lastCalledAt: 0,
|
|
54
|
+
};
|
|
55
|
+
this.accumulators.set(event.toolName, acc);
|
|
56
|
+
}
|
|
57
|
+
acc.count++;
|
|
58
|
+
if (!event.success)
|
|
59
|
+
acc.errorCount++;
|
|
60
|
+
acc.totalMs += event.durationMs;
|
|
61
|
+
sortedInsert(acc.durations, event.durationMs);
|
|
62
|
+
acc.lastCalledAt = event.timestamp;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get aggregated stats for all tools.
|
|
66
|
+
*/
|
|
67
|
+
getStats() {
|
|
68
|
+
const tools = {};
|
|
69
|
+
for (const [name, acc] of this.accumulators) {
|
|
70
|
+
tools[name] = this.accToStats(acc);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
totalCalls: this.totalCalls,
|
|
74
|
+
totalErrors: this.totalErrors,
|
|
75
|
+
errorRate: this.totalCalls > 0 ? this.totalErrors / this.totalCalls : 0,
|
|
76
|
+
uptimeMs: Date.now() - this.startTime,
|
|
77
|
+
tools,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get stats for a single tool.
|
|
82
|
+
*/
|
|
83
|
+
getToolStats(toolName) {
|
|
84
|
+
const acc = this.accumulators.get(toolName);
|
|
85
|
+
if (!acc)
|
|
86
|
+
return undefined;
|
|
87
|
+
return this.accToStats(acc);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Flush pending events to the exporter.
|
|
91
|
+
*/
|
|
92
|
+
async flush() {
|
|
93
|
+
if (this.pending.length === 0)
|
|
94
|
+
return;
|
|
95
|
+
const batch = this.pending;
|
|
96
|
+
this.pending = [];
|
|
97
|
+
await this.exporter(batch);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Reset all collected data.
|
|
101
|
+
*/
|
|
102
|
+
reset() {
|
|
103
|
+
this.buffer.length = 0;
|
|
104
|
+
this.pending.length = 0;
|
|
105
|
+
this.accumulators.clear();
|
|
106
|
+
this.totalCalls = 0;
|
|
107
|
+
this.totalErrors = 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Stop the flush timer and flush remaining events.
|
|
111
|
+
*/
|
|
112
|
+
async destroy() {
|
|
113
|
+
if (this.flushTimer) {
|
|
114
|
+
clearInterval(this.flushTimer);
|
|
115
|
+
this.flushTimer = undefined;
|
|
116
|
+
}
|
|
117
|
+
await this.flush();
|
|
118
|
+
}
|
|
119
|
+
accToStats(acc) {
|
|
120
|
+
return {
|
|
121
|
+
count: acc.count,
|
|
122
|
+
errorCount: acc.errorCount,
|
|
123
|
+
errorRate: acc.count > 0 ? acc.errorCount / acc.count : 0,
|
|
124
|
+
p50Ms: percentile(acc.durations, 50),
|
|
125
|
+
p95Ms: percentile(acc.durations, 95),
|
|
126
|
+
p99Ms: percentile(acc.durations, 99),
|
|
127
|
+
avgMs: acc.count > 0 ? acc.totalMs / acc.count : 0,
|
|
128
|
+
lastCalledAt: acc.lastCalledAt,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector.js","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/exporters/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,CACvC,MAAM,EAAE,aAAa,EAAE,KACpB,OAAO,CAAC,IAAI,CAAC,CAiBjB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console exporter: pretty-prints each batch of events to stdout.
|
|
3
|
+
*/
|
|
4
|
+
export function createConsoleExporter() {
|
|
5
|
+
return async (events) => {
|
|
6
|
+
if (events.length === 0)
|
|
7
|
+
return;
|
|
8
|
+
const lines = ["[McpAnalytics] Flushing batch:"];
|
|
9
|
+
for (const e of events) {
|
|
10
|
+
const errorSuffix = e.errorCode ? ` (${e.errorCode})` : "";
|
|
11
|
+
const status = e.success ? "OK" : `ERR${errorSuffix}`;
|
|
12
|
+
const meta = e.sessionId ? ` session=${e.sessionId}` : "";
|
|
13
|
+
lines.push(` ${e.toolName} ${status} ${e.durationMs}ms in=${e.inputSize}B out=${e.outputSize}B${meta}`);
|
|
14
|
+
}
|
|
15
|
+
console.log(lines.join("\n"));
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/exporters/console.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,qBAAqB;IAGnC,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE;QACtB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEhC,MAAM,KAAK,GAAa,CAAC,gCAAgC,CAAC,CAAC;QAE3D,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,WAAW,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,WAAW,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,QAAQ,IAAI,MAAM,IAAI,CAAC,CAAC,UAAU,SAAS,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,UAAU,IAAI,IAAI,EAAE,CAC7F,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ExporterFn, ToolCallEvent } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a user-provided export function, catching errors to prevent
|
|
4
|
+
* exporter failures from disrupting the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createCustomExporter(fn: ExporterFn): (events: ToolCallEvent[]) => Promise<void>;
|
|
7
|
+
//# sourceMappingURL=custom.d.ts.map
|