@posthog/mcp 0.18.1 → 0.20.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/dist/extensions/constants.d.ts +2 -0
- package/dist/extensions/constants.d.ts.map +1 -1
- package/dist/extensions/constants.js +2 -0
- package/dist/extensions/constants.mjs +2 -0
- package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
- package/dist/extensions/instrument-highlevel.js +1 -0
- package/dist/extensions/instrument-highlevel.mjs +1 -0
- package/dist/extensions/instrumentation.d.ts +1 -0
- package/dist/extensions/instrumentation.d.ts.map +1 -1
- package/dist/extensions/instrumentation.js +37 -2
- package/dist/extensions/instrumentation.mjs +38 -3
- package/dist/extensions/internal.d.ts +9 -8
- package/dist/extensions/internal.d.ts.map +1 -1
- package/dist/extensions/internal.js +17 -12
- package/dist/extensions/internal.mjs +15 -13
- package/dist/extensions/mcp-sdk-compat.d.ts +7 -0
- package/dist/extensions/mcp-sdk-compat.d.ts.map +1 -1
- package/dist/extensions/mcp-sdk-compat.js +21 -3
- package/dist/extensions/mcp-sdk-compat.mjs +17 -2
- package/dist/extensions/tool-input.d.ts +9 -0
- package/dist/extensions/tool-input.d.ts.map +1 -0
- package/dist/extensions/tool-input.js +117 -0
- package/dist/extensions/tool-input.mjs +79 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.mjs +3 -1
- package/dist/types.d.ts +38 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +2 -2
- package/src/extensions/constants.ts +2 -0
- package/src/extensions/instrument-highlevel.ts +1 -0
- package/src/extensions/instrumentation.ts +49 -1
- package/src/extensions/internal.ts +18 -17
- package/src/extensions/mcp-sdk-compat.ts +38 -1
- package/src/extensions/tool-input.ts +92 -0
- package/src/index.ts +6 -1
- package/src/types.ts +39 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { InputAliasMap, JsonRecord, ShouldRecordInputKeyFn, ToolInputOptions } from '../types'
|
|
2
|
+
import { PostHogMCPAnalyticsProperty } from './constants'
|
|
3
|
+
import { getObjectShape, isZodRawShapeCompat, unwrapInputSchema } from './mcp-sdk-compat'
|
|
4
|
+
|
|
5
|
+
const MAX_INPUT_KEYS = 20
|
|
6
|
+
const MAX_KEY_LENGTH = 64
|
|
7
|
+
const ANALYTICS_KEYS = new Set(['context', 'llm_model', 'conversation_id'])
|
|
8
|
+
|
|
9
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
10
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function declaredProperties(schema: unknown): Record<string, unknown> | undefined {
|
|
14
|
+
if (!isRecord(schema)) return undefined
|
|
15
|
+
const properties = schema.properties
|
|
16
|
+
if (isZodRawShapeCompat(schema)) return schema
|
|
17
|
+
return getObjectShape(unwrapInputSchema(schema)) ?? (isRecord(properties) ? properties : undefined)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const recordDeclaredOnly: ShouldRecordInputKeyFn = (_key, { declared }) => declared
|
|
21
|
+
|
|
22
|
+
function shouldRecord(fn: ShouldRecordInputKeyFn, key: string, declared: boolean): boolean {
|
|
23
|
+
try {
|
|
24
|
+
return fn(key, { declared }) === true
|
|
25
|
+
} catch {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function aliasNames(aliases: InputAliasMap | undefined): string[] {
|
|
31
|
+
if (!isRecord(aliases)) return []
|
|
32
|
+
return Object.values(aliases).flatMap((names) =>
|
|
33
|
+
Array.isArray(names) ? names.filter((name): name is string => typeof name === 'string') : []
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Each alias the server needed: the canonical name is absent and this is the first of its
|
|
39
|
+
* aliases present, the same order a normalizer that fills the canonical from its aliases uses.
|
|
40
|
+
*/
|
|
41
|
+
function describeAliasesUsed(aliases: InputAliasMap | undefined, input: Record<string, unknown>): string[] {
|
|
42
|
+
if (!isRecord(aliases)) return []
|
|
43
|
+
const used: string[] = []
|
|
44
|
+
for (const [canonical, names] of Object.entries(aliases)) {
|
|
45
|
+
if (!Array.isArray(names) || Object.prototype.hasOwnProperty.call(input, canonical)) continue
|
|
46
|
+
const alias = names.find((name) => typeof name === 'string' && Object.prototype.hasOwnProperty.call(input, name))
|
|
47
|
+
if (alias && alias.length <= MAX_KEY_LENGTH && canonical.length <= MAX_KEY_LENGTH) {
|
|
48
|
+
used.push(`${alias}:${canonical}`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return used.sort().slice(0, MAX_INPUT_KEYS)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Describe the original arguments without reading their values.
|
|
56
|
+
* Pass a server-owned JSON Schema or Zod object schema, never a schema from the caller.
|
|
57
|
+
* By default unknown names become `[redacted]` because an argument name can contain private data;
|
|
58
|
+
* `shouldRecordInputKey` replaces that rule.
|
|
59
|
+
*/
|
|
60
|
+
export function getToolInputProperties(input: unknown, inputSchema?: unknown, options?: ToolInputOptions): JsonRecord {
|
|
61
|
+
try {
|
|
62
|
+
if (!isRecord(input)) return {}
|
|
63
|
+
const prototype = Object.getPrototypeOf(input)
|
|
64
|
+
if (prototype !== null && prototype !== Object.prototype) return {}
|
|
65
|
+
const properties = declaredProperties(inputSchema)
|
|
66
|
+
const known = new Set([...Object.keys(properties ?? {}), ...aliasNames(options?.inputAliases)])
|
|
67
|
+
const keys = Object.keys(input).filter((key) => known.has(key) || !ANALYTICS_KEYS.has(key))
|
|
68
|
+
const record = options?.shouldRecordInputKey ?? recordDeclaredOnly
|
|
69
|
+
const declared: string[] = []
|
|
70
|
+
const undeclared: string[] = []
|
|
71
|
+
let hasRedacted = false
|
|
72
|
+
for (const key of keys) {
|
|
73
|
+
const isDeclared = known.has(key)
|
|
74
|
+
if (key.length <= MAX_KEY_LENGTH && shouldRecord(record, key, isDeclared)) {
|
|
75
|
+
;(isDeclared ? declared : undeclared).push(key)
|
|
76
|
+
} else {
|
|
77
|
+
hasRedacted = true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const visibleKeys = [...declared.sort(), ...undeclared.sort()].slice(0, MAX_INPUT_KEYS)
|
|
81
|
+
if (hasRedacted && visibleKeys.length < MAX_INPUT_KEYS) {
|
|
82
|
+
visibleKeys.push('[redacted]')
|
|
83
|
+
}
|
|
84
|
+
const aliasesUsed = describeAliasesUsed(options?.inputAliases, input)
|
|
85
|
+
return {
|
|
86
|
+
[PostHogMCPAnalyticsProperty.InputKeys]: visibleKeys,
|
|
87
|
+
...(aliasesUsed.length > 0 ? { [PostHogMCPAnalyticsProperty.InputAliasesUsed]: aliasesUsed } : {}),
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
return {}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { PostHog } from 'posthog-node'
|
|
|
7
7
|
import { isCompatibleServerType, isHighLevelServer } from './extensions/compatibility'
|
|
8
8
|
import { McpEventSink } from './extensions/sink'
|
|
9
9
|
import { MCPAnalyticsEventType } from './extensions/event-types'
|
|
10
|
-
import { IdentityCache, getServerTrackingData, setServerTrackingData } from './extensions/internal'
|
|
10
|
+
import { BoundedCache, IdentityCache, getServerTrackingData, setServerTrackingData } from './extensions/internal'
|
|
11
11
|
import { createLogger } from './extensions/logger'
|
|
12
12
|
import { captureEvent } from './extensions/capture'
|
|
13
13
|
import { applyMcpLibIdentity } from './extensions/lib-identity'
|
|
@@ -161,6 +161,7 @@ function buildTrackingData(
|
|
|
161
161
|
toolAnalyticsParameterOwnership: new Map(),
|
|
162
162
|
toolCategories: new Map<string, string>(),
|
|
163
163
|
toolDescriptions: new Map<string, string>(),
|
|
164
|
+
toolInputSchemas: new BoundedCache(),
|
|
164
165
|
sessionInfo: getSessionInfo(lowLevelServer, undefined),
|
|
165
166
|
options: {
|
|
166
167
|
...DEFAULT_OPTIONS,
|
|
@@ -230,6 +231,7 @@ export {
|
|
|
230
231
|
// Host callbacks receive the SDK's `extra`/`ctx` unchanged, and the two SDK
|
|
231
232
|
// majors carry HTTP headers in different places and shapes. This reads either.
|
|
232
233
|
export { getRequestHeaders } from './extensions/request-headers'
|
|
234
|
+
export { getToolInputProperties } from './extensions/tool-input'
|
|
233
235
|
export { PostHogMCP, type PostHogMCPOptions } from './extensions/posthog-mcp'
|
|
234
236
|
export { getMoreToolsResult } from './extensions/tools'
|
|
235
237
|
export { sendFeedbackResult, SEND_FEEDBACK_TOOL_NAME } from './extensions/feedback'
|
|
@@ -249,6 +251,7 @@ export type {
|
|
|
249
251
|
CaptureEventData,
|
|
250
252
|
CollectFeedbackConfig,
|
|
251
253
|
InitializeCaptureData,
|
|
254
|
+
InputAliasMap,
|
|
252
255
|
McpAnalytics,
|
|
253
256
|
McpCaptureCommon,
|
|
254
257
|
MCPAnalyticsContextOptions,
|
|
@@ -262,7 +265,9 @@ export type {
|
|
|
262
265
|
PrepareToolCallOptions,
|
|
263
266
|
PrepareToolListOptions,
|
|
264
267
|
RequestHeaderBag,
|
|
268
|
+
ShouldRecordInputKeyFn,
|
|
265
269
|
ToolCallCaptureData,
|
|
270
|
+
ToolInputOptions,
|
|
266
271
|
ToolsListCaptureData,
|
|
267
272
|
UserIdentity,
|
|
268
273
|
} from './types'
|
package/src/types.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { ErrorTracking } from '@posthog/core'
|
|
7
7
|
import type { AnalyticsInjectableJsonSchema } from './extensions/analytics-parameters'
|
|
8
8
|
import type { MCPAnalyticsEventType } from './extensions/event-types'
|
|
9
|
-
import type { IdentityCache } from './extensions/internal'
|
|
9
|
+
import type { BoundedCache, IdentityCache } from './extensions/internal'
|
|
10
10
|
import type { PostHogCaptureEvent } from './extensions/posthog-events'
|
|
11
11
|
import type { McpEventSink } from './extensions/sink'
|
|
12
12
|
import type { LoggerFn } from './extensions/logger'
|
|
@@ -186,6 +186,19 @@ export interface MCPAnalyticsOptions {
|
|
|
186
186
|
* suppress specific events. A throw drops that event.
|
|
187
187
|
*/
|
|
188
188
|
beforeSend?: BeforeSendFn
|
|
189
|
+
/**
|
|
190
|
+
* Decide which argument names `$mcp_input_keys` records on tool-call events.
|
|
191
|
+
* By default only names the tool's input schema declares are recorded; every
|
|
192
|
+
* other name becomes one `[redacted]` entry, because a name can carry private data.
|
|
193
|
+
*/
|
|
194
|
+
shouldRecordInputKey?: ShouldRecordInputKeyFn
|
|
195
|
+
/**
|
|
196
|
+
* Return the alternative argument names accepted by one tool. The map is
|
|
197
|
+
* canonical name to aliases in the order the server tries them. Automatic
|
|
198
|
+
* instrumentation uses it for `$mcp_input_keys` and
|
|
199
|
+
* `$mcp_input_aliases_used`; it never changes the tool arguments.
|
|
200
|
+
*/
|
|
201
|
+
resolveInputAliases?: (toolName: string) => InputAliasMap | undefined
|
|
189
202
|
/**
|
|
190
203
|
* Attach extra event properties on every auto-captured event. Spread into the PostHog
|
|
191
204
|
* event properties as-is; values must be JSON-serializable.
|
|
@@ -304,6 +317,30 @@ export type RegisteredTool = {
|
|
|
304
317
|
*/
|
|
305
318
|
export type BeforeSendFn = (event: PostHogCaptureEvent) => MaybePromise<PostHogCaptureEvent | null | undefined>
|
|
306
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Decides whether one top-level argument name appears in `$mcp_input_keys`.
|
|
322
|
+
* `declared` is true when the server's input schema declares the name.
|
|
323
|
+
* Return `true` to record the name; any other result, or a throw, records `[redacted]`.
|
|
324
|
+
*/
|
|
325
|
+
export type ShouldRecordInputKeyFn = (key: string, details: { declared: boolean }) => boolean
|
|
326
|
+
|
|
327
|
+
export interface ToolInputOptions {
|
|
328
|
+
/**
|
|
329
|
+
* Replace the default rule, which records only declared names. The SDK still
|
|
330
|
+
* drops names longer than 64 characters and records at most 20 names.
|
|
331
|
+
*/
|
|
332
|
+
shouldRecordInputKey?: ShouldRecordInputKeyFn
|
|
333
|
+
/**
|
|
334
|
+
* The alternative argument names the server accepts, as canonical name to aliases in the
|
|
335
|
+
* order the server tries them, for example `{ id: ['experimentId'] }`. Must be owned by the
|
|
336
|
+
* server, never taken from the caller. Alias names count as declared in `$mcp_input_keys`,
|
|
337
|
+
* and `$mcp_input_aliases_used` records each alias the server needed, as `alias:canonical`.
|
|
338
|
+
*/
|
|
339
|
+
inputAliases?: InputAliasMap
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export type InputAliasMap = Readonly<Record<string, readonly string[]>>
|
|
343
|
+
|
|
307
344
|
export interface Event {
|
|
308
345
|
actorId?: string
|
|
309
346
|
clientName?: string
|
|
@@ -515,6 +552,7 @@ export interface MCPAnalyticsData {
|
|
|
515
552
|
toolAnalyticsParameterOwnership: Map<string, AnalyticsParameterOwnership>
|
|
516
553
|
toolCategories: Map<string, string>
|
|
517
554
|
toolDescriptions: Map<string, string>
|
|
555
|
+
toolInputSchemas: BoundedCache<Map<string, unknown>>
|
|
518
556
|
}
|
|
519
557
|
|
|
520
558
|
export interface CaptureEventData {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.
|
|
1
|
+
export const version = '0.20.0'
|