@listo-ai/mcp-observability 0.5.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/client.d.ts +3 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -6
- package/dist/endpoints.d.ts +14 -1
- package/dist/endpoints.d.ts.map +1 -1
- package/dist/endpoints.js +72 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ import { createTelemetryClient } from '@listo-ai/mcp-observability/client';
|
|
|
85
85
|
|
|
86
86
|
const client = createTelemetryClient({
|
|
87
87
|
endpoint: 'https://myapp.com/telemetry',
|
|
88
|
-
|
|
88
|
+
userId: localStorage.getItem('user_id') ?? undefined,
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
// Share the session ID across widget instances
|
|
@@ -240,7 +240,7 @@ observability.recordBusinessEvent('product_search', {
|
|
|
240
240
|
status: 'ok',
|
|
241
241
|
category: 'conversion',
|
|
242
242
|
sessionId: 'sess-abc',
|
|
243
|
-
|
|
243
|
+
userId: 'user-xyz',
|
|
244
244
|
tenantId: 'tenant-1',
|
|
245
245
|
});
|
|
246
246
|
```
|
|
@@ -253,7 +253,7 @@ Options:
|
|
|
253
253
|
| `status` | `'ok' \| 'error'?` | Event status. Error events bypass sampling |
|
|
254
254
|
| `category` | `EventCategory?` | `'conversion'`, `'engagement'`, `'impression'`, `'navigation'`, or `'system'` |
|
|
255
255
|
| `sessionId` | `string?` | Session identifier for journey tracking |
|
|
256
|
-
| `
|
|
256
|
+
| `userId` | `string?` | User identifier for cross-session identification |
|
|
257
257
|
| `tenantId` | `string?` | Tenant identifier for multi-tenant apps |
|
|
258
258
|
|
|
259
259
|
### `observability.recordUiEvent(event)`
|
|
@@ -476,6 +476,7 @@ Browser interaction events via `recordUiEvent()` or the `POST /telemetry/event`
|
|
|
476
476
|
| `NODE_ENV` | No | `development` / `staging` / `production` -- controls defaults |
|
|
477
477
|
| `TELEMETRY_SAMPLE_RATE` | No | Override sampling percentage (0.0 - 1.0) |
|
|
478
478
|
| `TELEMETRY_CAPTURE_PAYLOADS` | No | Enable/disable payload capture |
|
|
479
|
+
| `HMAC_SECRET` | No | Secret key for HMAC-SHA-256 hashing of `sessionId`/`userId` in MCP events. Without it, falls back to SHA-256 |
|
|
479
480
|
|
|
480
481
|
## Security
|
|
481
482
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface TelemetryClientOptions {
|
|
2
2
|
endpoint: string;
|
|
3
3
|
sessionId?: string;
|
|
4
|
-
|
|
4
|
+
userId?: string;
|
|
5
5
|
tenantId?: string;
|
|
6
6
|
locale?: string;
|
|
7
7
|
batchSize?: number;
|
|
@@ -12,7 +12,7 @@ export interface TelemetryClientOptions {
|
|
|
12
12
|
export declare class TelemetryClient {
|
|
13
13
|
private readonly endpoint;
|
|
14
14
|
private readonly sessionId;
|
|
15
|
-
private readonly
|
|
15
|
+
private readonly userId?;
|
|
16
16
|
private readonly batchSize;
|
|
17
17
|
private readonly flushIntervalMs;
|
|
18
18
|
private readonly onError;
|
|
@@ -23,7 +23,7 @@ export declare class TelemetryClient {
|
|
|
23
23
|
private destroyed;
|
|
24
24
|
constructor(options: TelemetryClientOptions);
|
|
25
25
|
getSessionId(): string;
|
|
26
|
-
|
|
26
|
+
getUserId(): string | undefined;
|
|
27
27
|
track(name: string, properties?: Record<string, unknown>, category?: string): void;
|
|
28
28
|
trackUi(name: string, options: {
|
|
29
29
|
action?: string;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAgBD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2B;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,EAAE,sBAAsB;IAuB3C,YAAY,IAAI,MAAM;IAItB,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI;IAcP,OAAO,CACL,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,GACA,IAAI;IAiBP,UAAU,CAAC,GAAG,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAK7D,KAAK,IAAI,IAAI;IAMb,OAAO,IAAI,IAAI;IAef,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,IAAI;IA8BZ,OAAO,CAAC,sBAAsB,CAI5B;CACH;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAEjB"}
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export class TelemetryClient {
|
|
2
2
|
endpoint;
|
|
3
3
|
sessionId;
|
|
4
|
-
|
|
4
|
+
userId;
|
|
5
5
|
batchSize;
|
|
6
6
|
flushIntervalMs;
|
|
7
7
|
onError;
|
|
@@ -13,7 +13,7 @@ export class TelemetryClient {
|
|
|
13
13
|
constructor(options) {
|
|
14
14
|
this.endpoint = options.endpoint.replace(/\/+$/, '');
|
|
15
15
|
this.sessionId = options.sessionId ?? globalThis.crypto.randomUUID();
|
|
16
|
-
this.
|
|
16
|
+
this.userId = options.userId;
|
|
17
17
|
this.batchSize = options.batchSize ?? 20;
|
|
18
18
|
this.flushIntervalMs = options.flushIntervalMs ?? 5000;
|
|
19
19
|
this.onError = options.onError ?? (() => { });
|
|
@@ -30,8 +30,8 @@ export class TelemetryClient {
|
|
|
30
30
|
getSessionId() {
|
|
31
31
|
return this.sessionId;
|
|
32
32
|
}
|
|
33
|
-
|
|
34
|
-
return this.
|
|
33
|
+
getUserId() {
|
|
34
|
+
return this.userId;
|
|
35
35
|
}
|
|
36
36
|
track(name, properties, category) {
|
|
37
37
|
if (this.destroyed)
|
|
@@ -40,7 +40,7 @@ export class TelemetryClient {
|
|
|
40
40
|
name,
|
|
41
41
|
timestamp: Date.now(),
|
|
42
42
|
sessionId: this.sessionId,
|
|
43
|
-
|
|
43
|
+
userId: this.userId,
|
|
44
44
|
tenantId: this.context.tenantId,
|
|
45
45
|
locale: this.context.locale,
|
|
46
46
|
category,
|
|
@@ -54,7 +54,7 @@ export class TelemetryClient {
|
|
|
54
54
|
name,
|
|
55
55
|
timestamp: Date.now(),
|
|
56
56
|
sessionId: this.sessionId,
|
|
57
|
-
|
|
57
|
+
userId: this.userId,
|
|
58
58
|
tenantId: this.context.tenantId,
|
|
59
59
|
locale: this.context.locale,
|
|
60
60
|
action: options.action,
|
package/dist/endpoints.d.ts
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import type { McpObservability } from './index.js';
|
|
2
|
+
import type { McpObservability, Platform } from './index.js';
|
|
3
3
|
declare const TELEMETRY_SINK_KEY: unique symbol;
|
|
4
4
|
declare const OBSERVABILITY_KEY: unique symbol;
|
|
5
5
|
declare function setGlobal<T>(key: symbol, value: T): void;
|
|
6
|
+
/**
|
|
7
|
+
* Infer a `Platform` value from an HTTP `Origin` header.
|
|
8
|
+
*
|
|
9
|
+
* Returns `undefined` when the origin is missing, malformed, or doesn't match
|
|
10
|
+
* any known host pattern — i.e. the helper itself is honest about not
|
|
11
|
+
* recognising a host. The built-in POST handlers default that case to
|
|
12
|
+
* `'other'` before recording, so emitted events always have `platform`
|
|
13
|
+
* populated. If you build your own telemetry router around this helper,
|
|
14
|
+
* apply the same `?? 'other'` fallback to match the convention.
|
|
15
|
+
*
|
|
16
|
+
* Exported so consumers can reuse the same mapping in custom telemetry routers.
|
|
17
|
+
*/
|
|
18
|
+
export declare function platformFromOrigin(origin: string | string[] | undefined): Platform | undefined;
|
|
6
19
|
/**
|
|
7
20
|
* Express middleware for automatic HTTP request tracking
|
|
8
21
|
*
|
package/dist/endpoints.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoints.d.ts","sourceRoot":"","sources":["../src/endpoints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"endpoints.d.ts","sourceRoot":"","sources":["../src/endpoints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAgB,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3E,QAAA,MAAM,kBAAkB,eAAsC,CAAC;AAC/D,QAAA,MAAM,iBAAiB,eAAsC,CAAC;AAe9D,iBAAS,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAEjD;AA+CD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,GACpC,QAAQ,GAAG,SAAS,CAatB;AA4BD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,gBAAgB,IAChD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBAW9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AAEH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,GAAG,OA4TvD;AAED,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/endpoints.js
CHANGED
|
@@ -21,7 +21,8 @@ const UI_EVENT_FIELDS = new Set([
|
|
|
21
21
|
'widgetId',
|
|
22
22
|
'toolName',
|
|
23
23
|
'sessionId',
|
|
24
|
-
'
|
|
24
|
+
'userId',
|
|
25
|
+
'platform',
|
|
25
26
|
'tenantId',
|
|
26
27
|
'locale',
|
|
27
28
|
'properties',
|
|
@@ -33,7 +34,55 @@ const VALID_CATEGORIES = new Set([
|
|
|
33
34
|
'navigation',
|
|
34
35
|
'system',
|
|
35
36
|
]);
|
|
37
|
+
const VALID_PLATFORMS = new Set(['claude', 'chatgpt', 'other']);
|
|
36
38
|
const MAX_EVENT_BODY_SIZE = 8192;
|
|
39
|
+
/**
|
|
40
|
+
* Origin → platform mapping for UI events POSTed from MCP-host iframe sandboxes.
|
|
41
|
+
*
|
|
42
|
+
* MCP hosts serve widget content from a per-binding subdomain on a host-controlled
|
|
43
|
+
* domain (so widget JS runs cross-origin to the consuming MCP server). The Origin
|
|
44
|
+
* header on widget-side fetches identifies the host deterministically:
|
|
45
|
+
*
|
|
46
|
+
* - https://<hash>.claudemcpcontent.com → Claude (web + Desktop both use this)
|
|
47
|
+
* - https://*.oaiusercontent.com → ChatGPT (Apps SDK widget sandbox)
|
|
48
|
+
* - https://*.chatgpt.com → ChatGPT (some widget contexts)
|
|
49
|
+
*
|
|
50
|
+
* Add new entries when supporting additional MCP hosts. First match wins.
|
|
51
|
+
*/
|
|
52
|
+
const PLATFORM_ORIGIN_PATTERNS = [
|
|
53
|
+
[/(?:^|\.)claudemcpcontent\.com$/i, 'claude'],
|
|
54
|
+
[/(?:^|\.)oaiusercontent\.com$/i, 'chatgpt'],
|
|
55
|
+
[/(?:^|\.)chatgpt\.com$/i, 'chatgpt'],
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Infer a `Platform` value from an HTTP `Origin` header.
|
|
59
|
+
*
|
|
60
|
+
* Returns `undefined` when the origin is missing, malformed, or doesn't match
|
|
61
|
+
* any known host pattern — i.e. the helper itself is honest about not
|
|
62
|
+
* recognising a host. The built-in POST handlers default that case to
|
|
63
|
+
* `'other'` before recording, so emitted events always have `platform`
|
|
64
|
+
* populated. If you build your own telemetry router around this helper,
|
|
65
|
+
* apply the same `?? 'other'` fallback to match the convention.
|
|
66
|
+
*
|
|
67
|
+
* Exported so consumers can reuse the same mapping in custom telemetry routers.
|
|
68
|
+
*/
|
|
69
|
+
export function platformFromOrigin(origin) {
|
|
70
|
+
const value = Array.isArray(origin) ? origin[0] : origin;
|
|
71
|
+
if (!value || typeof value !== 'string')
|
|
72
|
+
return undefined;
|
|
73
|
+
let hostname;
|
|
74
|
+
try {
|
|
75
|
+
hostname = new URL(value).hostname;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
for (const [pattern, platform] of PLATFORM_ORIGIN_PATTERNS) {
|
|
81
|
+
if (pattern.test(hostname))
|
|
82
|
+
return platform;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
37
86
|
function validateUiEventBody(body) {
|
|
38
87
|
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
39
88
|
return null;
|
|
@@ -44,6 +93,11 @@ function validateUiEventBody(body) {
|
|
|
44
93
|
(typeof raw.category !== 'string' || !VALID_CATEGORIES.has(raw.category))) {
|
|
45
94
|
return null;
|
|
46
95
|
}
|
|
96
|
+
if (raw.platform !== undefined &&
|
|
97
|
+
(typeof raw.platform !== 'string' ||
|
|
98
|
+
!VALID_PLATFORMS.has(raw.platform))) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
47
101
|
const cleaned = {};
|
|
48
102
|
for (const key of UI_EVENT_FIELDS) {
|
|
49
103
|
if (key in raw) {
|
|
@@ -310,6 +364,16 @@ export function createTelemetryRouter(expressModule) {
|
|
|
310
364
|
.json({ error: 'Invalid event: "name" string required' });
|
|
311
365
|
return;
|
|
312
366
|
}
|
|
367
|
+
// Infer platform from the Origin header when the client didn't supply
|
|
368
|
+
// one. Explicit values from the body always win — the inference only
|
|
369
|
+
// fills the gap. Widgets running in MCP-host iframe sandboxes (Claude's
|
|
370
|
+
// claudemcpcontent.com, ChatGPT's oaiusercontent.com) get correctly
|
|
371
|
+
// tagged with no widget-side change required. Anything else (unknown
|
|
372
|
+
// origin, no Origin header) is bucketed as 'other' so the field is
|
|
373
|
+
// always populated — matches `detectPlatform`'s behavior on mcp_request.
|
|
374
|
+
if (validated.platform === undefined) {
|
|
375
|
+
validated.platform = platformFromOrigin(req.headers.origin) ?? 'other';
|
|
376
|
+
}
|
|
313
377
|
const observability = getGlobal(OBSERVABILITY_KEY);
|
|
314
378
|
if (observability) {
|
|
315
379
|
observability.recordUiEvent(validated);
|
|
@@ -328,12 +392,19 @@ export function createTelemetryRouter(expressModule) {
|
|
|
328
392
|
res.status(400).json({ error: 'Maximum 100 events per batch' });
|
|
329
393
|
return;
|
|
330
394
|
}
|
|
395
|
+
// Compute once per batch — all events in the batch share the same Origin.
|
|
396
|
+
// Defaults to 'other' when the Origin doesn't match a known platform, so
|
|
397
|
+
// every emitted event has the field populated. Matches single /event behavior.
|
|
398
|
+
const fallbackPlatform = platformFromOrigin(req.headers.origin) ?? 'other';
|
|
331
399
|
const observability = getGlobal(OBSERVABILITY_KEY);
|
|
332
400
|
let accepted = 0;
|
|
333
401
|
let rejected = 0;
|
|
334
402
|
for (const raw of events) {
|
|
335
403
|
const validated = validateUiEventBody(raw);
|
|
336
404
|
if (validated) {
|
|
405
|
+
if (validated.platform === undefined) {
|
|
406
|
+
validated.platform = fallbackPlatform;
|
|
407
|
+
}
|
|
337
408
|
if (observability) {
|
|
338
409
|
observability.recordUiEvent(validated);
|
|
339
410
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export type McpRequestEvent = {
|
|
|
44
44
|
latencyMs: number;
|
|
45
45
|
tenantId?: string;
|
|
46
46
|
sessionId?: string;
|
|
47
|
+
userId?: string;
|
|
47
48
|
platform?: Platform;
|
|
48
49
|
locale?: string;
|
|
49
50
|
userAgent?: string;
|
|
@@ -79,7 +80,7 @@ export type BusinessEvent = {
|
|
|
79
80
|
status?: EventStatus;
|
|
80
81
|
category?: EventCategory;
|
|
81
82
|
sessionId?: string;
|
|
82
|
-
|
|
83
|
+
userId?: string;
|
|
83
84
|
tenantId?: string;
|
|
84
85
|
properties?: Record<string, unknown>;
|
|
85
86
|
};
|
|
@@ -95,7 +96,8 @@ export type UiEvent = {
|
|
|
95
96
|
widgetId?: string;
|
|
96
97
|
toolName?: string;
|
|
97
98
|
sessionId?: string;
|
|
98
|
-
|
|
99
|
+
userId?: string;
|
|
100
|
+
platform?: Platform;
|
|
99
101
|
tenantId?: string;
|
|
100
102
|
locale?: string;
|
|
101
103
|
properties?: Record<string, unknown>;
|
|
@@ -121,6 +123,7 @@ type HttpTrackingContext = {
|
|
|
121
123
|
};
|
|
122
124
|
type McpTrackingContext = {
|
|
123
125
|
sessionId?: string;
|
|
126
|
+
userId?: string;
|
|
124
127
|
tenantId?: string;
|
|
125
128
|
platform?: Platform;
|
|
126
129
|
locale?: string;
|
|
@@ -248,7 +251,7 @@ export type BusinessEventOptions = {
|
|
|
248
251
|
status?: EventStatus;
|
|
249
252
|
category?: EventCategory;
|
|
250
253
|
sessionId?: string;
|
|
251
|
-
|
|
254
|
+
userId?: string;
|
|
252
255
|
tenantId?: string;
|
|
253
256
|
};
|
|
254
257
|
export declare class McpObservability {
|
|
@@ -263,7 +266,7 @@ export declare class McpObservability {
|
|
|
263
266
|
constructor(options: ObservabilityOptions);
|
|
264
267
|
recordUiEvent(event: Omit<UiEvent, 'timestamp' | 'service' | 'type'>): void;
|
|
265
268
|
trackHttpRequest(req: IncomingMessage, res: ServerResponse, handler: (ctx: HttpTrackingContext) => Promise<void>): Promise<void>;
|
|
266
|
-
wrapMcpHandler<TRequest, TResponse>(requestKind: string, handler: (request: TRequest) => Promise<TResponse> | TResponse, context?: (request: TRequest) => McpTrackingContext): (request: TRequest) => Promise<TResponse>;
|
|
269
|
+
wrapMcpHandler<TRequest, TResponse, TExtra = unknown>(requestKind: string, handler: (request: TRequest, extra?: TExtra) => Promise<TResponse> | TResponse, context?: (request: TRequest, extra?: TExtra) => McpTrackingContext): (request: TRequest, extra?: TExtra) => Promise<TResponse>;
|
|
267
270
|
recordSession(event: Omit<McpSessionEvent, 'timestamp' | 'service'>): void;
|
|
268
271
|
recordBusinessEvent(name: string, options?: BusinessEventOptions): void;
|
|
269
272
|
private baseMcpFields;
|
|
@@ -285,6 +288,9 @@ export declare function createMcpObservability(options: ObservabilityOptions): M
|
|
|
285
288
|
* 4. Fallback — 'other' when any hints are provided but no known platform matched
|
|
286
289
|
*/
|
|
287
290
|
export declare function detectPlatform(hints?: PlatformHints): Platform;
|
|
291
|
+
/** @internal Reset cached HMAC secret — only for testing. */
|
|
292
|
+
export declare function _resetHmacSecretCache(): void;
|
|
293
|
+
export declare function hashIdentifier(value: string | undefined): string | undefined;
|
|
288
294
|
export { RemoteSink, createRemoteSink, type RemoteSinkOptions, } from './remote-sink.js';
|
|
289
295
|
export { createMcpObservabilityEasy, type EasySetupConfig, } from './easy-setup.js';
|
|
290
296
|
export { createTelemetryRouter, expressTelemetry } from './endpoints.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
1
|
+
import { createHash, createHmac, randomUUID } from 'node:crypto';
|
|
2
2
|
export class InMemorySink {
|
|
3
3
|
events = [];
|
|
4
4
|
limit;
|
|
@@ -187,18 +187,20 @@ export class McpObservability {
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
wrapMcpHandler(requestKind, handler, context) {
|
|
190
|
-
return async (request) => {
|
|
191
|
-
const metadata = context?.(request) ?? {};
|
|
190
|
+
return async (request, extra) => {
|
|
191
|
+
const metadata = context?.(request, extra) ?? {};
|
|
192
192
|
const requestId = metadata.requestId ?? randomUUID();
|
|
193
193
|
const start = performance.now();
|
|
194
194
|
try {
|
|
195
|
-
const result = await handler(request);
|
|
195
|
+
const result = await handler(request, extra);
|
|
196
196
|
const sanitizedArgs = metadata.args
|
|
197
197
|
? this.sanitizePayload(metadata.args)
|
|
198
198
|
: undefined;
|
|
199
199
|
const sanitizedResult = metadata.resultPreview
|
|
200
200
|
? this.sanitizePayload(metadata.resultPreview(result))
|
|
201
201
|
: undefined;
|
|
202
|
+
const hashedSessionId = hashIdentifier(metadata.sessionId);
|
|
203
|
+
const hashedUserId = hashIdentifier(metadata.userId);
|
|
202
204
|
this.safeEmit({
|
|
203
205
|
...this.baseMcpFields(),
|
|
204
206
|
type: 'mcp_request',
|
|
@@ -210,7 +212,8 @@ export class McpObservability {
|
|
|
210
212
|
status: 'ok',
|
|
211
213
|
latencyMs: performance.now() - start,
|
|
212
214
|
tenantId: metadata.tenantId,
|
|
213
|
-
sessionId:
|
|
215
|
+
sessionId: hashedSessionId,
|
|
216
|
+
userId: hashedUserId,
|
|
214
217
|
platform: metadata.platform,
|
|
215
218
|
locale: metadata.locale,
|
|
216
219
|
userAgent: metadata.userAgent,
|
|
@@ -241,6 +244,8 @@ export class McpObservability {
|
|
|
241
244
|
const sanitizedArgs = metadata.args
|
|
242
245
|
? this.sanitizePayload(metadata.args)
|
|
243
246
|
: undefined;
|
|
247
|
+
const hashedSessionId = hashIdentifier(metadata.sessionId);
|
|
248
|
+
const hashedUserId = hashIdentifier(metadata.userId);
|
|
244
249
|
this.safeEmit({
|
|
245
250
|
...this.baseMcpFields(),
|
|
246
251
|
type: 'mcp_request',
|
|
@@ -252,7 +257,8 @@ export class McpObservability {
|
|
|
252
257
|
status: 'error',
|
|
253
258
|
latencyMs: performance.now() - start,
|
|
254
259
|
tenantId: metadata.tenantId,
|
|
255
|
-
sessionId:
|
|
260
|
+
sessionId: hashedSessionId,
|
|
261
|
+
userId: hashedUserId,
|
|
256
262
|
platform: metadata.platform,
|
|
257
263
|
locale: metadata.locale,
|
|
258
264
|
userAgent: metadata.userAgent,
|
|
@@ -289,7 +295,7 @@ export class McpObservability {
|
|
|
289
295
|
status: options?.status,
|
|
290
296
|
category: options?.category,
|
|
291
297
|
sessionId: options?.sessionId,
|
|
292
|
-
|
|
298
|
+
userId: options?.userId,
|
|
293
299
|
tenantId: options?.tenantId,
|
|
294
300
|
};
|
|
295
301
|
this.safeEmit(event);
|
|
@@ -433,6 +439,26 @@ export function detectPlatform(hints) {
|
|
|
433
439
|
// 4. Nothing matched — bucket as 'other'
|
|
434
440
|
return 'other';
|
|
435
441
|
}
|
|
442
|
+
let _hmacSecret;
|
|
443
|
+
function getHmacSecret() {
|
|
444
|
+
if (_hmacSecret === undefined) {
|
|
445
|
+
_hmacSecret = process.env.HMAC_SECRET ?? '';
|
|
446
|
+
}
|
|
447
|
+
return _hmacSecret || undefined;
|
|
448
|
+
}
|
|
449
|
+
/** @internal Reset cached HMAC secret — only for testing. */
|
|
450
|
+
export function _resetHmacSecretCache() {
|
|
451
|
+
_hmacSecret = undefined;
|
|
452
|
+
}
|
|
453
|
+
export function hashIdentifier(value) {
|
|
454
|
+
if (!value)
|
|
455
|
+
return value;
|
|
456
|
+
const secret = getHmacSecret();
|
|
457
|
+
if (secret) {
|
|
458
|
+
return createHmac('sha256', secret).update(value).digest('hex');
|
|
459
|
+
}
|
|
460
|
+
return createHash('sha256').update(value).digest('hex');
|
|
461
|
+
}
|
|
436
462
|
function hashPayload(payload) {
|
|
437
463
|
try {
|
|
438
464
|
const json = JSON.stringify(payload);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@listo-ai/mcp-observability",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Lightweight telemetry SDK for MCP servers and web applications. Captures HTTP requests, MCP tool invocations, business events, and UI interactions with built-in payload sanitization.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|