@posthog/mcp 0.11.0 → 0.11.2

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.
Files changed (55) hide show
  1. package/README.md +39 -0
  2. package/dist/extensions/client-identity.d.ts +70 -15
  3. package/dist/extensions/client-identity.d.ts.map +1 -1
  4. package/dist/extensions/client-identity.js +72 -17
  5. package/dist/extensions/client-identity.mjs +59 -13
  6. package/dist/extensions/compatibility.d.ts.map +1 -1
  7. package/dist/extensions/compatibility.js +14 -18
  8. package/dist/extensions/compatibility.mjs +10 -14
  9. package/dist/extensions/detect.d.ts +51 -0
  10. package/dist/extensions/detect.d.ts.map +1 -0
  11. package/dist/extensions/detect.js +96 -0
  12. package/dist/extensions/detect.mjs +38 -0
  13. package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
  14. package/dist/extensions/instrumentation.d.ts +4 -5
  15. package/dist/extensions/instrumentation.d.ts.map +1 -1
  16. package/dist/extensions/instrumentation.js +6 -5
  17. package/dist/extensions/instrumentation.mjs +7 -6
  18. package/dist/extensions/internal.js +1 -1
  19. package/dist/extensions/internal.mjs +2 -2
  20. package/dist/extensions/request-headers.d.ts +36 -0
  21. package/dist/extensions/request-headers.d.ts.map +1 -0
  22. package/dist/extensions/request-headers.js +62 -0
  23. package/dist/extensions/request-headers.mjs +28 -0
  24. package/dist/extensions/session.d.ts.map +1 -1
  25. package/dist/extensions/session.js +2 -1
  26. package/dist/extensions/session.mjs +2 -1
  27. package/dist/extensions/tools.d.ts +4 -4
  28. package/dist/extensions/tools.d.ts.map +1 -1
  29. package/dist/extensions/transport-identity.d.ts +8 -3
  30. package/dist/extensions/transport-identity.d.ts.map +1 -1
  31. package/dist/extensions/transport-identity.js +2 -1
  32. package/dist/extensions/transport-identity.mjs +2 -1
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +11 -7
  36. package/dist/index.mjs +2 -1
  37. package/dist/types.d.ts +77 -2
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/version.d.ts +1 -1
  40. package/dist/version.js +1 -1
  41. package/dist/version.mjs +1 -1
  42. package/package.json +12 -2
  43. package/src/extensions/client-identity.ts +146 -31
  44. package/src/extensions/compatibility.ts +25 -24
  45. package/src/extensions/detect.ts +87 -0
  46. package/src/extensions/instrument-highlevel.ts +4 -4
  47. package/src/extensions/instrumentation.ts +22 -15
  48. package/src/extensions/internal.ts +2 -2
  49. package/src/extensions/request-headers.ts +76 -0
  50. package/src/extensions/session.ts +2 -1
  51. package/src/extensions/tools.ts +4 -4
  52. package/src/extensions/transport-identity.ts +10 -4
  53. package/src/index.ts +4 -0
  54. package/src/types.ts +78 -3
  55. package/src/version.ts +1 -1
package/README.md CHANGED
@@ -67,6 +67,45 @@ if (body?.method === 'initialize' && !req.headers[MCP_SESSION_HEADER]) {
67
67
 
68
68
  Details: [docs/ARCHITECTURE.md §4](./docs/ARCHITECTURE.md).
69
69
 
70
+ ## MCP TypeScript SDK v2
71
+
72
+ `instrument()` works on both SDK majors — `@modelcontextprotocol/sdk` v1 and
73
+ `@modelcontextprotocol/{core,server,client}` v2 — and on both the high-level `McpServer` and the
74
+ low-level `Server`. Shapes are detected at runtime, so neither major is a dependency here.
75
+
76
+ ### Reading request headers in a callback
77
+
78
+ If your `identify`, `intentFallback`, `eventProperties` or `beforeSend` reads HTTP headers, it has
79
+ to change. The two majors put the request in different places and in different shapes: v1 attaches
80
+ a plain object at `extra.requestInfo.headers`, v2 attaches the WHATWG `Request` at `extra.http.req`,
81
+ whose `headers` only answers to `.get()`. A v1-shaped read returns `undefined` on v2 — an
82
+ `identify()` written that way returns `null` and every event goes anonymous.
83
+
84
+ The SDK hands your callback whatever the MCP SDK handed it, unchanged; it does **not** fake a v1
85
+ shape on v2, because a partially synthesised `requestInfo` is a more convincing lie than an absent
86
+ one. Read headers through the exported helper instead, which handles both majors, lowercases keys,
87
+ and duck-types `Headers` so it also works on edge runtimes:
88
+
89
+ ```ts
90
+ import { getRequestHeaders } from '@posthog/mcp'
91
+
92
+ identify: async (request, extra) => {
93
+ const auth = getRequestHeaders(extra)?.['authorization'] // v1 and v2
94
+ // ...
95
+ }
96
+ ```
97
+
98
+ ### If you switched to `instrument(server.server)`
99
+
100
+ Before v2 support landed, the compatibility gate rejected high-level v2 servers, and the usual
101
+ workaround was to instrument the underlying low-level server. `instrument(server)` now works, so
102
+ you can go back to the documented call.
103
+
104
+ Whether the workaround costs you anything depends on your stack: instrumenting the low-level server
105
+ skips the high-level tool registry, so tool descriptions and callback-level wrapping come only from
106
+ what is advertised over `tools/list`. If your framework never calls `registerTool()` — `@rekog/mcp-nest`
107
+ does not — the registry is empty and the two calls behave the same.
108
+
70
109
  ## Developing locally
71
110
 
72
111
  To test local changes in a consumer app (e.g. a dummy MCP server), symlink **both**
@@ -1,13 +1,24 @@
1
- import type { McpEvent, MCPRequestLike } from '../types';
1
+ import type { CompatibleRequestHandlerExtra, McpEvent, MCPRequestLike, MCPServerLike } from '../types';
2
2
  /**
3
- * Client identity under the MCP 2026-07-28 (stateless) revision.
3
+ * Client identity — who is calling, and which spec revision they speak.
4
4
  *
5
- * That revision removes the `initialize` handshake and the `Mcp-Session-Id`
6
- * header (SEP-2575 / SEP-2567). Client name/version and the protocol version no
7
- * longer arrive once at `initialize` — they travel in every request's
8
- * `params._meta` under these reverse-DNS keys. We mirror the literal key strings
9
- * here rather than depend on the (still beta) v2 SDK, which is not a dependency
10
- * of this package.
5
+ * Where it lives depends on the revision the *request* declares, so this is a
6
+ * fallback chain and never a branch: one server serves both eras, request by
7
+ * request.
8
+ *
9
+ * 1. `ctx.mcpReq.envelope` — MCP SDK v2 lifts the reserved
10
+ * `io.modelcontextprotocol/*` keys out of `_meta` before dispatch, so by
11
+ * the time a handler runs they are here and `params._meta` is empty.
12
+ * 2. `request.params._meta` — where 2026-07-28 puts them on the wire, and
13
+ * where they still are on any server that does not lift them.
14
+ * 3. the server's own accessors — `getClientVersion()` from a legacy
15
+ * `initialize` handshake, `getNegotiatedProtocolVersion()` on v2.
16
+ *
17
+ * The 2026-07-28 revision removed the `initialize` handshake and the
18
+ * `Mcp-Session-Id` header (SEP-2575 / SEP-2567), so client name/version and the
19
+ * protocol version no longer arrive once per connection — they travel with
20
+ * every request. The key strings are mirrored here rather than imported,
21
+ * because no `@modelcontextprotocol/*` package is a dependency of this one.
11
22
  */
12
23
  export declare const META_CLIENT_INFO_KEY = "io.modelcontextprotocol/clientInfo";
13
24
  export declare const META_PROTOCOL_VERSION_KEY = "io.modelcontextprotocol/protocolVersion";
@@ -16,6 +27,18 @@ export interface MetaClientInfo {
16
27
  clientVersion?: string;
17
28
  protocolVersion?: string;
18
29
  }
30
+ /** Everything a request can be asked about who is calling. */
31
+ export interface ClientIdentitySources {
32
+ request: MCPRequestLike;
33
+ extra?: CompatibleRequestHandlerExtra;
34
+ /**
35
+ * Typed `unknown` on purpose: every accessor on it is reached through a
36
+ * structural probe, never a declared type, because the two SDK majors expose
37
+ * different ones. Call sites pass a real `MCPServerLike`; the shape checks
38
+ * here are what decide whether it can answer.
39
+ */
40
+ server?: unknown;
41
+ }
19
42
  /**
20
43
  * Reads the client name/version and protocol version a modern client puts in
21
44
  * `params._meta`. Returns `undefined` when the request carries none (e.g. a
@@ -23,17 +46,49 @@ export interface MetaClientInfo {
23
46
  */
24
47
  export declare function readMetaClientInfo(request: MCPRequestLike): MetaClientInfo | undefined;
25
48
  /**
26
- * Stamps any client identity found in `params._meta` directly onto the event
27
- * being built for *this* request, so it carries `$mcp_client_name`,
28
- * `$mcp_client_version`, and `$mcp_protocol_version` even when there was no
29
- * `initialize` to learn them from (the modern stateless case).
49
+ * Reads the same keys from MCP SDK v2's request envelope. v2 strips the reserved
50
+ * `io.modelcontextprotocol/*` keys from `_meta` while parsing, so on exactly the
51
+ * traffic this matters for, `readMetaClientInfo` finds nothing and this finds
52
+ * everything.
53
+ */
54
+ export declare function readEnvelopeClientInfo(extra: CompatibleRequestHandlerExtra | undefined): MetaClientInfo | undefined;
55
+ /**
56
+ * Asks the server itself. `getClientVersion()` answers on any server that
57
+ * handled an `initialize` (and v2 hosts such as `createMcpHandler` backfill it
58
+ * from the envelope); `getNegotiatedProtocolVersion()` exists on v2 only, which
59
+ * is why it is a link in the chain rather than a branch. Never throws — a
60
+ * server that dislikes being asked must not fail the tool call.
61
+ */
62
+ export declare function readServerClientIdentity(server: unknown): MetaClientInfo | undefined;
63
+ /**
64
+ * Resolves client identity through the whole chain, field by field: the first
65
+ * source that answers a field wins, and a source that answers nothing simply
66
+ * does not participate. Field-by-field rather than source-by-source because a
67
+ * request may carry its protocol version in the envelope while the client's
68
+ * name is only known to the server from a handshake.
69
+ *
70
+ * The first three links are **per request**, so they cannot describe anyone but
71
+ * the caller. Only the last — the server's own accessors — is connection-scoped,
72
+ * and on a server that multiplexes several clients through one connection it
73
+ * answers for whichever client completed the handshake. That is the best answer
74
+ * available on that connection, and it is the same scope `capture.ts` has always
75
+ * fallen back to (`eventInput.clientName ?? sessionInfo.clientName`), so the
76
+ * chain neither introduces that sharing nor widens it. Ordering it last is what
77
+ * keeps it a fallback: any request that identifies itself wins outright.
78
+ */
79
+ export declare function resolveClientIdentity({ request, extra, server }: ClientIdentitySources): MetaClientInfo | undefined;
80
+ /**
81
+ * Stamps whatever the chain resolved onto the event being built for *this*
82
+ * request, so it carries `$mcp_client_name`, `$mcp_client_version` and
83
+ * `$mcp_protocol_version` even when there was no `initialize` to learn them
84
+ * from (the modern stateless case).
30
85
  *
31
86
  * Writing to the event — a per-request object — rather than the server-wide
32
87
  * `sessionInfo` keeps identity correct when one instrumented server multiplexes
33
88
  * concurrent requests from different clients (which the stateless spec allows):
34
89
  * a sibling request can't clobber this event's attribution between now and when
35
- * it's captured. Only fields the request actually carries are set, so a request
36
- * without `_meta` leaves the event's existing values untouched.
90
+ * it's captured. Only fields actually resolved are set, so a request that
91
+ * carries nothing leaves the event's existing values untouched.
37
92
  */
38
- export declare function stampMetaClientInfo(event: McpEvent, request: MCPRequestLike): void;
93
+ export declare function stampClientIdentity(event: McpEvent, request: MCPRequestLike, extra?: CompatibleRequestHandlerExtra, server?: MCPServerLike): void;
39
94
  //# sourceMappingURL=client-identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-identity.d.ts","sourceRoot":"","sources":["../../src/extensions/client-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAExD;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,uCAAuC,CAAA;AACxE,eAAO,MAAM,yBAAyB,4CAA4C,CAAA;AAElF,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS,CAyBtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAclF"}
1
+ {"version":3,"file":"client-identity.d.ts","sourceRoot":"","sources":["../../src/extensions/client-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,6BAA6B,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGtG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,uCAAuC,CAAA;AACxE,eAAO,MAAM,yBAAyB,4CAA4C,CAAA;AAElF,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,cAAc,CAAA;IACvB,KAAK,CAAC,EAAE,6BAA6B,CAAA;IACrC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAuCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS,CAEtF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,6BAA6B,GAAG,SAAS,GAAG,cAAc,GAAG,SAAS,CAEnH;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAsBpF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,qBAAqB,GAAG,cAAc,GAAG,SAAS,CAYnH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,cAAc,EACvB,KAAK,CAAC,EAAE,6BAA6B,EACrC,MAAM,CAAC,EAAE,aAAa,GACrB,IAAI,CAcN"}
@@ -24,30 +24,79 @@ var __webpack_require__ = {};
24
24
  var __webpack_exports__ = {};
25
25
  __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
- META_CLIENT_INFO_KEY: ()=>META_CLIENT_INFO_KEY,
28
- META_PROTOCOL_VERSION_KEY: ()=>META_PROTOCOL_VERSION_KEY,
27
+ resolveClientIdentity: ()=>resolveClientIdentity,
29
28
  readMetaClientInfo: ()=>readMetaClientInfo,
30
- stampMetaClientInfo: ()=>stampMetaClientInfo
29
+ readServerClientIdentity: ()=>readServerClientIdentity,
30
+ readEnvelopeClientInfo: ()=>readEnvelopeClientInfo,
31
+ META_PROTOCOL_VERSION_KEY: ()=>META_PROTOCOL_VERSION_KEY,
32
+ stampClientIdentity: ()=>stampClientIdentity,
33
+ META_CLIENT_INFO_KEY: ()=>META_CLIENT_INFO_KEY
31
34
  });
35
+ const external_detect_js_namespaceObject = require("./detect.js");
32
36
  const META_CLIENT_INFO_KEY = 'io.modelcontextprotocol/clientInfo';
33
37
  const META_PROTOCOL_VERSION_KEY = 'io.modelcontextprotocol/protocolVersion';
38
+ function nonEmptyString(value) {
39
+ return 'string' == typeof value && value.length > 0 ? value : void 0;
40
+ }
41
+ function readClientInfoValue(value) {
42
+ if (!value || 'object' != typeof value) return;
43
+ const { name, version } = value;
44
+ const result = {};
45
+ const clientName = nonEmptyString(name);
46
+ const clientVersion = nonEmptyString(version);
47
+ if (clientName) result.clientName = clientName;
48
+ if (clientVersion) result.clientVersion = clientVersion;
49
+ return result.clientName || result.clientVersion ? result : void 0;
50
+ }
51
+ function readReservedKeys(bag) {
52
+ if (!bag || 'object' != typeof bag) return;
53
+ const record = bag;
54
+ const result = {
55
+ ...readClientInfoValue(record[META_CLIENT_INFO_KEY])
56
+ };
57
+ const protocolVersion = nonEmptyString(record[META_PROTOCOL_VERSION_KEY]);
58
+ if (protocolVersion) result.protocolVersion = protocolVersion;
59
+ return result.clientName || result.clientVersion || result.protocolVersion ? result : void 0;
60
+ }
34
61
  function readMetaClientInfo(request) {
35
- const meta = request.params?._meta;
36
- if (!meta || 'object' != typeof meta) return;
37
- const record = meta;
62
+ return readReservedKeys(request.params?._meta);
63
+ }
64
+ function readEnvelopeClientInfo(extra) {
65
+ return readReservedKeys(extra?.mcpReq?.envelope);
66
+ }
67
+ function readServerClientIdentity(server) {
68
+ if (!server) return;
69
+ const result = {};
70
+ try {
71
+ if ((0, external_detect_js_namespaceObject.hasClientVersionAccessor)(server)) Object.assign(result, readClientInfoValue(server.getClientVersion()));
72
+ if ((0, external_detect_js_namespaceObject.hasNegotiatedProtocolVersionAccessor)(server)) {
73
+ const negotiated = server.getNegotiatedProtocolVersion();
74
+ const protocolVersion = nonEmptyString(negotiated);
75
+ if (protocolVersion) result.protocolVersion = protocolVersion;
76
+ }
77
+ } catch {}
78
+ return result.clientName || result.clientVersion || result.protocolVersion ? result : void 0;
79
+ }
80
+ function resolveClientIdentity({ request, extra, server }) {
81
+ const chain = [
82
+ readEnvelopeClientInfo(extra),
83
+ readMetaClientInfo(request),
84
+ readServerClientIdentity(server)
85
+ ];
38
86
  const result = {};
39
- const clientInfo = record[META_CLIENT_INFO_KEY];
40
- if (clientInfo && 'object' == typeof clientInfo) {
41
- const { name, version } = clientInfo;
42
- if ('string' == typeof name && name.length > 0) result.clientName = name;
43
- if ('string' == typeof version && version.length > 0) result.clientVersion = version;
87
+ for (const source of chain)if (source) {
88
+ result.clientName ??= source.clientName;
89
+ result.clientVersion ??= source.clientVersion;
90
+ result.protocolVersion ??= source.protocolVersion;
44
91
  }
45
- const protocolVersion = record[META_PROTOCOL_VERSION_KEY];
46
- if ('string' == typeof protocolVersion && protocolVersion.length > 0) result.protocolVersion = protocolVersion;
47
92
  return result.clientName || result.clientVersion || result.protocolVersion ? result : void 0;
48
93
  }
49
- function stampMetaClientInfo(event, request) {
50
- const info = readMetaClientInfo(request);
94
+ function stampClientIdentity(event, request, extra, server) {
95
+ const info = resolveClientIdentity({
96
+ request,
97
+ extra,
98
+ server
99
+ });
51
100
  if (!info) return;
52
101
  if (info.clientName) event.clientName = info.clientName;
53
102
  if (info.clientVersion) event.clientVersion = info.clientVersion;
@@ -55,13 +104,19 @@ function stampMetaClientInfo(event, request) {
55
104
  }
56
105
  exports.META_CLIENT_INFO_KEY = __webpack_exports__.META_CLIENT_INFO_KEY;
57
106
  exports.META_PROTOCOL_VERSION_KEY = __webpack_exports__.META_PROTOCOL_VERSION_KEY;
107
+ exports.readEnvelopeClientInfo = __webpack_exports__.readEnvelopeClientInfo;
58
108
  exports.readMetaClientInfo = __webpack_exports__.readMetaClientInfo;
59
- exports.stampMetaClientInfo = __webpack_exports__.stampMetaClientInfo;
109
+ exports.readServerClientIdentity = __webpack_exports__.readServerClientIdentity;
110
+ exports.resolveClientIdentity = __webpack_exports__.resolveClientIdentity;
111
+ exports.stampClientIdentity = __webpack_exports__.stampClientIdentity;
60
112
  for(var __webpack_i__ in __webpack_exports__)if (-1 === [
61
113
  "META_CLIENT_INFO_KEY",
62
114
  "META_PROTOCOL_VERSION_KEY",
115
+ "readEnvelopeClientInfo",
63
116
  "readMetaClientInfo",
64
- "stampMetaClientInfo"
117
+ "readServerClientIdentity",
118
+ "resolveClientIdentity",
119
+ "stampClientIdentity"
65
120
  ].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
66
121
  Object.defineProperty(exports, '__esModule', {
67
122
  value: true
@@ -1,25 +1,71 @@
1
+ import { hasClientVersionAccessor, hasNegotiatedProtocolVersionAccessor } from "./detect.mjs";
1
2
  const META_CLIENT_INFO_KEY = 'io.modelcontextprotocol/clientInfo';
2
3
  const META_PROTOCOL_VERSION_KEY = 'io.modelcontextprotocol/protocolVersion';
4
+ function nonEmptyString(value) {
5
+ return 'string' == typeof value && value.length > 0 ? value : void 0;
6
+ }
7
+ function readClientInfoValue(value) {
8
+ if (!value || 'object' != typeof value) return;
9
+ const { name, version } = value;
10
+ const result = {};
11
+ const clientName = nonEmptyString(name);
12
+ const clientVersion = nonEmptyString(version);
13
+ if (clientName) result.clientName = clientName;
14
+ if (clientVersion) result.clientVersion = clientVersion;
15
+ return result.clientName || result.clientVersion ? result : void 0;
16
+ }
17
+ function readReservedKeys(bag) {
18
+ if (!bag || 'object' != typeof bag) return;
19
+ const record = bag;
20
+ const result = {
21
+ ...readClientInfoValue(record[META_CLIENT_INFO_KEY])
22
+ };
23
+ const protocolVersion = nonEmptyString(record[META_PROTOCOL_VERSION_KEY]);
24
+ if (protocolVersion) result.protocolVersion = protocolVersion;
25
+ return result.clientName || result.clientVersion || result.protocolVersion ? result : void 0;
26
+ }
3
27
  function readMetaClientInfo(request) {
4
- const meta = request.params?._meta;
5
- if (!meta || 'object' != typeof meta) return;
6
- const record = meta;
28
+ return readReservedKeys(request.params?._meta);
29
+ }
30
+ function readEnvelopeClientInfo(extra) {
31
+ return readReservedKeys(extra?.mcpReq?.envelope);
32
+ }
33
+ function readServerClientIdentity(server) {
34
+ if (!server) return;
35
+ const result = {};
36
+ try {
37
+ if (hasClientVersionAccessor(server)) Object.assign(result, readClientInfoValue(server.getClientVersion()));
38
+ if (hasNegotiatedProtocolVersionAccessor(server)) {
39
+ const negotiated = server.getNegotiatedProtocolVersion();
40
+ const protocolVersion = nonEmptyString(negotiated);
41
+ if (protocolVersion) result.protocolVersion = protocolVersion;
42
+ }
43
+ } catch {}
44
+ return result.clientName || result.clientVersion || result.protocolVersion ? result : void 0;
45
+ }
46
+ function resolveClientIdentity({ request, extra, server }) {
47
+ const chain = [
48
+ readEnvelopeClientInfo(extra),
49
+ readMetaClientInfo(request),
50
+ readServerClientIdentity(server)
51
+ ];
7
52
  const result = {};
8
- const clientInfo = record[META_CLIENT_INFO_KEY];
9
- if (clientInfo && 'object' == typeof clientInfo) {
10
- const { name, version } = clientInfo;
11
- if ('string' == typeof name && name.length > 0) result.clientName = name;
12
- if ('string' == typeof version && version.length > 0) result.clientVersion = version;
53
+ for (const source of chain)if (source) {
54
+ result.clientName ??= source.clientName;
55
+ result.clientVersion ??= source.clientVersion;
56
+ result.protocolVersion ??= source.protocolVersion;
13
57
  }
14
- const protocolVersion = record[META_PROTOCOL_VERSION_KEY];
15
- if ('string' == typeof protocolVersion && protocolVersion.length > 0) result.protocolVersion = protocolVersion;
16
58
  return result.clientName || result.clientVersion || result.protocolVersion ? result : void 0;
17
59
  }
18
- function stampMetaClientInfo(event, request) {
19
- const info = readMetaClientInfo(request);
60
+ function stampClientIdentity(event, request, extra, server) {
61
+ const info = resolveClientIdentity({
62
+ request,
63
+ extra,
64
+ server
65
+ });
20
66
  if (!info) return;
21
67
  if (info.clientName) event.clientName = info.clientName;
22
68
  if (info.clientVersion) event.clientVersion = info.clientVersion;
23
69
  if (info.protocolVersion) event.protocolVersion = info.protocolVersion;
24
70
  }
25
- export { META_CLIENT_INFO_KEY, META_PROTOCOL_VERSION_KEY, readMetaClientInfo, stampMetaClientInfo };
71
+ export { META_CLIENT_INFO_KEY, META_PROTOCOL_VERSION_KEY, readEnvelopeClientInfo, readMetaClientInfo, readServerClientIdentity, resolveClientIdentity, stampClientIdentity };
@@ -1 +1 @@
1
- {"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/extensions/compatibility.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE3C;;;;;;;;;;;GAWG;AAEH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAI9D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,YAAY,GAAG;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAIpG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,YAAY,CAExE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAG,aAAa,GAAG,sBAAsB,CA6BhH;AAoDD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAanE"}
1
+ {"version":3,"file":"compatibility.d.ts","sourceRoot":"","sources":["../../src/extensions/compatibility.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAWrE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE3C;;;;;;;;;;;GAWG;AAEH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAI9D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,YAAY,GAAG;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAEpG;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,YAAY,CAExE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAG,aAAa,GAAG,sBAAsB,CAiChH;AAyCD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAanE"}
@@ -24,20 +24,21 @@ var __webpack_require__ = {};
24
24
  var __webpack_exports__ = {};
25
25
  __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
- getMCPCompatibleErrorMessage: ()=>getMCPCompatibleErrorMessage,
28
- isCompatibleServerType: ()=>isCompatibleServerType,
29
- isHighLevelServer: ()=>isHighLevelServer,
30
27
  isLowLevelServer: ()=>isLowLevelServer,
31
- logCompatibilityWarning: ()=>logCompatibilityWarning
28
+ isHighLevelServer: ()=>isHighLevelServer,
29
+ logCompatibilityWarning: ()=>logCompatibilityWarning,
30
+ isCompatibleServerType: ()=>isCompatibleServerType,
31
+ getMCPCompatibleErrorMessage: ()=>getMCPCompatibleErrorMessage
32
32
  });
33
+ const external_detect_js_namespaceObject = require("./detect.js");
33
34
  function logCompatibilityWarning(logger) {
34
35
  logger('PostHog MCP analytics SDK Compatibility: This version only supports Model Context Protocol TypeScript SDK v1.11 and above. Please upgrade if using an older version.');
35
36
  }
36
37
  function isHighLevelServer(server) {
37
- return !!server && 'object' == typeof server && 'server' in server && !!server.server && 'object' == typeof server.server;
38
+ return (0, external_detect_js_namespaceObject.hasNestedLowLevelServer)(server);
38
39
  }
39
40
  function isLowLevelServer(server) {
40
- return !!server && 'object' == typeof server && !('server' in server);
41
+ return (0, external_detect_js_namespaceObject.isBareServerShape)(server);
41
42
  }
42
43
  function isCompatibleServerType(server, logger) {
43
44
  if (!server || 'object' != typeof server) {
@@ -45,13 +46,13 @@ function isCompatibleServerType(server, logger) {
45
46
  throw new Error("PostHog MCP analytics SDK compatibility error: Server must be an object. Ensure you're using MCP SDK v1.11 or higher.");
46
47
  }
47
48
  if (isHighLevelServer(server)) {
48
- if (!server._registeredTools || 'object' != typeof server._registeredTools) {
49
+ if (!(0, external_detect_js_namespaceObject.hasToolRegistry)(server)) {
49
50
  logCompatibilityWarning(logger);
50
51
  throw new Error('PostHog MCP analytics SDK compatibility error: High-level server must have _registeredTools object. This requires MCP SDK v1.11 or higher.');
51
52
  }
52
- if ('function' != typeof server.tool) {
53
+ if (!(0, external_detect_js_namespaceObject.canRegisterTools)(server)) {
53
54
  logCompatibilityWarning(logger);
54
- throw new Error('PostHog MCP analytics SDK compatibility error: High-level server must have tool() method. This requires MCP SDK v1.11 or higher.');
55
+ throw new Error('PostHog MCP analytics SDK compatibility error: High-level server must have a registerTool() or tool() method. This requires MCP SDK v1.11 or higher.');
55
56
  }
56
57
  const targetServer = server.server;
57
58
  validateLowLevelServer(targetServer, logger);
@@ -65,24 +66,19 @@ function validateLowLevelServer(server, logger) {
65
66
  logCompatibilityWarning(logger);
66
67
  throw new Error("PostHog MCP analytics SDK compatibility error: Server must be an object. Ensure you're using MCP SDK v1.11 or higher.");
67
68
  }
68
- const serverRecord = server;
69
- if ('function' != typeof serverRecord.setRequestHandler) {
69
+ if (!(0, external_detect_js_namespaceObject.canSetRequestHandler)(server)) {
70
70
  logCompatibilityWarning(logger);
71
71
  throw new Error('PostHog MCP analytics SDK compatibility error: Server must have a setRequestHandler method. This requires MCP SDK v1.11 or higher.');
72
72
  }
73
- if (!(serverRecord._requestHandlers && serverRecord._requestHandlers instanceof Map)) {
73
+ if (!(0, external_detect_js_namespaceObject.hasRequestHandlerMap)(server)) {
74
74
  logCompatibilityWarning(logger);
75
75
  throw new Error('PostHog MCP analytics SDK compatibility error: Server._requestHandlers is not accessible. This requires MCP SDK v1.11 or higher.');
76
76
  }
77
- if ('function' != typeof serverRecord._requestHandlers.get) {
78
- logCompatibilityWarning(logger);
79
- throw new Error('PostHog MCP analytics SDK compatibility error: Server._requestHandlers must be a Map with a get method. This requires MCP SDK v1.11 or higher.');
80
- }
81
- if ('function' != typeof serverRecord.getClientVersion) {
77
+ if (!(0, external_detect_js_namespaceObject.hasClientVersionAccessor)(server)) {
82
78
  logCompatibilityWarning(logger);
83
79
  throw new Error('PostHog MCP analytics SDK compatibility error: Server.getClientVersion must be a function. This requires MCP SDK v1.11 or higher.');
84
80
  }
85
- if (!serverRecord._serverInfo || 'object' != typeof serverRecord._serverInfo || !('name' in serverRecord._serverInfo)) {
81
+ if (!(0, external_detect_js_namespaceObject.hasServerInfo)(server)) {
86
82
  logCompatibilityWarning(logger);
87
83
  throw new Error('PostHog MCP analytics SDK compatibility error: Server._serverInfo is not accessible or missing name. This requires MCP SDK v1.11 or higher.');
88
84
  }
@@ -1,11 +1,12 @@
1
+ import { canRegisterTools, canSetRequestHandler, hasClientVersionAccessor, hasNestedLowLevelServer, hasRequestHandlerMap, hasServerInfo, hasToolRegistry, isBareServerShape } from "./detect.mjs";
1
2
  function logCompatibilityWarning(logger) {
2
3
  logger('PostHog MCP analytics SDK Compatibility: This version only supports Model Context Protocol TypeScript SDK v1.11 and above. Please upgrade if using an older version.');
3
4
  }
4
5
  function isHighLevelServer(server) {
5
- return !!server && 'object' == typeof server && 'server' in server && !!server.server && 'object' == typeof server.server;
6
+ return hasNestedLowLevelServer(server);
6
7
  }
7
8
  function isLowLevelServer(server) {
8
- return !!server && 'object' == typeof server && !('server' in server);
9
+ return isBareServerShape(server);
9
10
  }
10
11
  function isCompatibleServerType(server, logger) {
11
12
  if (!server || 'object' != typeof server) {
@@ -13,13 +14,13 @@ function isCompatibleServerType(server, logger) {
13
14
  throw new Error("PostHog MCP analytics SDK compatibility error: Server must be an object. Ensure you're using MCP SDK v1.11 or higher.");
14
15
  }
15
16
  if (isHighLevelServer(server)) {
16
- if (!server._registeredTools || 'object' != typeof server._registeredTools) {
17
+ if (!hasToolRegistry(server)) {
17
18
  logCompatibilityWarning(logger);
18
19
  throw new Error('PostHog MCP analytics SDK compatibility error: High-level server must have _registeredTools object. This requires MCP SDK v1.11 or higher.');
19
20
  }
20
- if ('function' != typeof server.tool) {
21
+ if (!canRegisterTools(server)) {
21
22
  logCompatibilityWarning(logger);
22
- throw new Error('PostHog MCP analytics SDK compatibility error: High-level server must have tool() method. This requires MCP SDK v1.11 or higher.');
23
+ throw new Error('PostHog MCP analytics SDK compatibility error: High-level server must have a registerTool() or tool() method. This requires MCP SDK v1.11 or higher.');
23
24
  }
24
25
  const targetServer = server.server;
25
26
  validateLowLevelServer(targetServer, logger);
@@ -33,24 +34,19 @@ function validateLowLevelServer(server, logger) {
33
34
  logCompatibilityWarning(logger);
34
35
  throw new Error("PostHog MCP analytics SDK compatibility error: Server must be an object. Ensure you're using MCP SDK v1.11 or higher.");
35
36
  }
36
- const serverRecord = server;
37
- if ('function' != typeof serverRecord.setRequestHandler) {
37
+ if (!canSetRequestHandler(server)) {
38
38
  logCompatibilityWarning(logger);
39
39
  throw new Error('PostHog MCP analytics SDK compatibility error: Server must have a setRequestHandler method. This requires MCP SDK v1.11 or higher.');
40
40
  }
41
- if (!(serverRecord._requestHandlers && serverRecord._requestHandlers instanceof Map)) {
41
+ if (!hasRequestHandlerMap(server)) {
42
42
  logCompatibilityWarning(logger);
43
43
  throw new Error('PostHog MCP analytics SDK compatibility error: Server._requestHandlers is not accessible. This requires MCP SDK v1.11 or higher.');
44
44
  }
45
- if ('function' != typeof serverRecord._requestHandlers.get) {
46
- logCompatibilityWarning(logger);
47
- throw new Error('PostHog MCP analytics SDK compatibility error: Server._requestHandlers must be a Map with a get method. This requires MCP SDK v1.11 or higher.');
48
- }
49
- if ('function' != typeof serverRecord.getClientVersion) {
45
+ if (!hasClientVersionAccessor(server)) {
50
46
  logCompatibilityWarning(logger);
51
47
  throw new Error('PostHog MCP analytics SDK compatibility error: Server.getClientVersion must be a function. This requires MCP SDK v1.11 or higher.');
52
48
  }
53
- if (!serverRecord._serverInfo || 'object' != typeof serverRecord._serverInfo || !('name' in serverRecord._serverInfo)) {
49
+ if (!hasServerInfo(server)) {
54
50
  logCompatibilityWarning(logger);
55
51
  throw new Error('PostHog MCP analytics SDK compatibility error: Server._serverInfo is not accessible or missing name. This requires MCP SDK v1.11 or higher.');
56
52
  }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Structural feature probes.
3
+ *
4
+ * Every question about the shape of a server object is answered here, by
5
+ * duck-typing the object in front of us — never by reading a package version or
6
+ * a protocol constant. Two reasons this is a rule and not a preference:
7
+ *
8
+ * - The two SDK majors expose the same capability under different names (v1's
9
+ * `.tool()` vs v2's `.registerTool()`), so "which SDK is installed" answers a
10
+ * different question than "can this object register a tool".
11
+ * - A single v2 server serves both protocol eras, request by request, so no
12
+ * module-level constant describes it. v2's exported `LATEST_PROTOCOL_VERSION`
13
+ * is `2025-11-25`.
14
+ *
15
+ * Nothing here imports `@modelcontextprotocol/*`; that boundary is pinned by
16
+ * `sdk-import-boundary.test.ts`.
17
+ */
18
+ type UnknownRecord = Record<string, unknown>;
19
+ /** A high-level `McpServer` wrapper: it holds the low-level server on `.server`. */
20
+ export declare function hasNestedLowLevelServer(server: unknown): server is UnknownRecord & {
21
+ server: UnknownRecord;
22
+ };
23
+ /** No `.server` property, so the object is itself the low-level `Server`. */
24
+ export declare function isBareServerShape(server: unknown): boolean;
25
+ /** The high-level registry we read tool metadata from and wrap callbacks in. */
26
+ export declare function hasToolRegistry(server: unknown): boolean;
27
+ /**
28
+ * The object can register a tool.
29
+ *
30
+ * v1 offers both `tool()` and `registerTool()`; v2 dropped the deprecated
31
+ * `tool()` and keeps only `registerTool()`. Requiring `tool()` specifically is
32
+ * what made `instrument()` reject every v2 high-level server — and, because
33
+ * `instrument()` swallows compatibility errors, reject it silently.
34
+ */
35
+ export declare function canRegisterTools(server: unknown): boolean;
36
+ /** Handler registration, which we patch to wrap late registrations. */
37
+ export declare function canSetRequestHandler(server: unknown): boolean;
38
+ /** The live handler map we read and write directly. A real `Map` on both majors. */
39
+ export declare function hasRequestHandlerMap(server: unknown): boolean;
40
+ /** `getClientVersion()` — still present on v2, and a link in B1's identity chain. */
41
+ export declare function hasClientVersionAccessor(server: unknown): boolean;
42
+ /**
43
+ * `getNegotiatedProtocolVersion()` — v2 only, and the last link in the protocol
44
+ * version chain. A v1 server does not have it, which is why the chain probes for
45
+ * it instead of branching on which SDK is installed.
46
+ */
47
+ export declare function hasNegotiatedProtocolVersionAccessor(server: unknown): boolean;
48
+ /** `_serverInfo`, which names the server on every event. */
49
+ export declare function hasServerInfo(server: unknown): boolean;
50
+ export {};
51
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/extensions/detect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAU5C,oFAAoF;AACpF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,aAAa,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAG5G;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAG1D;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAExD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAE7D;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAG7D;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEjE;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAE7E;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAGtD"}