@posthog/mcp 0.14.1 → 0.16.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.
Files changed (65) hide show
  1. package/LICENSE +64 -0
  2. package/README.md +64 -0
  3. package/dist/extensions/constants.d.ts +9 -0
  4. package/dist/extensions/constants.d.ts.map +1 -1
  5. package/dist/extensions/constants.js +9 -0
  6. package/dist/extensions/constants.mjs +9 -0
  7. package/dist/extensions/event-types.d.ts +1 -0
  8. package/dist/extensions/event-types.d.ts.map +1 -1
  9. package/dist/extensions/event-types.js +1 -0
  10. package/dist/extensions/event-types.mjs +1 -0
  11. package/dist/extensions/feedback.d.ts +41 -0
  12. package/dist/extensions/feedback.d.ts.map +1 -0
  13. package/dist/extensions/feedback.js +259 -0
  14. package/dist/extensions/feedback.mjs +200 -0
  15. package/dist/extensions/instrument-highlevel.d.ts.map +1 -1
  16. package/dist/extensions/instrument-highlevel.js +23 -2
  17. package/dist/extensions/instrument-highlevel.mjs +25 -4
  18. package/dist/extensions/instrument-lowlevel.d.ts +4 -3
  19. package/dist/extensions/instrument-lowlevel.d.ts.map +1 -1
  20. package/dist/extensions/instrument-lowlevel.js +23 -2
  21. package/dist/extensions/instrument-lowlevel.mjs +25 -4
  22. package/dist/extensions/instrumentation.d.ts +33 -13
  23. package/dist/extensions/instrumentation.d.ts.map +1 -1
  24. package/dist/extensions/instrumentation.js +103 -9
  25. package/dist/extensions/instrumentation.mjs +99 -8
  26. package/dist/extensions/internal.js +9 -2
  27. package/dist/extensions/internal.mjs +9 -2
  28. package/dist/extensions/mcp-payloads.d.ts +19 -0
  29. package/dist/extensions/mcp-payloads.d.ts.map +1 -1
  30. package/dist/extensions/mcp-payloads.js +217 -7
  31. package/dist/extensions/mcp-payloads.mjs +210 -6
  32. package/dist/extensions/posthog-events.d.ts.map +1 -1
  33. package/dist/extensions/posthog-events.js +1 -0
  34. package/dist/extensions/posthog-events.mjs +1 -0
  35. package/dist/extensions/posthog-mcp.d.ts +23 -3
  36. package/dist/extensions/posthog-mcp.d.ts.map +1 -1
  37. package/dist/extensions/posthog-mcp.js +27 -1
  38. package/dist/extensions/posthog-mcp.mjs +27 -1
  39. package/dist/extensions/sanitization.d.ts.map +1 -1
  40. package/dist/extensions/sanitization.js +2 -1
  41. package/dist/extensions/sanitization.mjs +3 -2
  42. package/dist/index.d.ts +5 -4
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +10 -0
  45. package/dist/index.mjs +5 -1
  46. package/dist/types.d.ts +114 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/dist/version.mjs +1 -1
  51. package/package.json +4 -4
  52. package/src/extensions/constants.ts +9 -0
  53. package/src/extensions/event-types.ts +1 -0
  54. package/src/extensions/feedback.ts +349 -0
  55. package/src/extensions/instrument-highlevel.ts +45 -2
  56. package/src/extensions/instrument-lowlevel.ts +49 -5
  57. package/src/extensions/instrumentation.ts +170 -17
  58. package/src/extensions/internal.ts +13 -2
  59. package/src/extensions/mcp-payloads.ts +443 -7
  60. package/src/extensions/posthog-events.ts +1 -0
  61. package/src/extensions/posthog-mcp.ts +74 -3
  62. package/src/extensions/sanitization.ts +14 -6
  63. package/src/index.ts +24 -3
  64. package/src/types.ts +120 -0
  65. package/src/version.ts +1 -1
@@ -4,7 +4,7 @@
4
4
  // Licensed under the MIT License: https://github.com/agentcathq/agentcat-typescript-sdk/blob/main/LICENSE
5
5
 
6
6
  import type { ErrorProperties, Event, McpEvent } from '../types'
7
- import { redactPii, sanitizeCapturedValue } from './mcp-payloads'
7
+ import { sanitizeCapturedValue, sanitizeFreeText } from './mcp-payloads'
8
8
 
9
9
  type SanitizedRecord = Record<string, unknown>
10
10
 
@@ -32,14 +32,22 @@ export function sanitizeEvent<T extends Event | McpEvent>(event: T): T {
32
32
  result.parameters = sanitizeParameters(result.parameters)
33
33
  }
34
34
 
35
+ // Every event type, not just `resources/read`: `$identify` and the `$exception`
36
+ // sibling carry the same name, and a tool or prompt name is free text an
37
+ // application can spell as a URL too.
38
+ if (result.resourceName != null) {
39
+ result.resourceName = sanitizeCapturedValue(result.resourceName) as string
40
+ }
41
+
35
42
  // The intent comes straight from an agent-narrated `context` string, so it can
36
43
  // contain a secret the LLM read aloud or personal data it narrated about the
37
- // user. Redact it like any other captured value, then strip structured PII
38
- // (emails, phone numbers, IPs, cards, SSNs) rather than shipping it raw as
39
- // `$mcp_intent`. PII redaction is scoped to the intent only — structured tool
40
- // parameters and responses often hold the same shapes as legitimate data.
44
+ // user. `sanitizeFreeText` adds structured PII redaction (emails, phone numbers,
45
+ // IPs, cards, SSNs) to the passes every captured value gets, in the one order
46
+ // that works — see its doc comment. PII redaction is scoped to the intent only:
47
+ // structured tool parameters and responses often hold the same shapes as
48
+ // legitimate data.
41
49
  if (result.userIntent != null) {
42
- result.userIntent = redactPii(sanitizeCapturedValue(result.userIntent) as string)
50
+ result.userIntent = sanitizeFreeText(result.userIntent)
43
51
  }
44
52
 
45
53
  if (result.llmModel != null) {
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ import { applyMcpLibIdentity } from './extensions/lib-identity'
14
14
  import { deriveSessionIdFromMCPSession, getSessionInfo, newSessionId } from './extensions/session'
15
15
  import { instrumentLowLevelServer } from './extensions/instrument-lowlevel'
16
16
  import { instrumentHighLevelServer } from './extensions/instrument-highlevel'
17
+ import { getFeedbackToolDescriptor, resolveCollectFeedbackOptions } from './extensions/feedback'
17
18
  import type {
18
19
  CaptureEventData,
19
20
  HighLevelMCPServerLike,
@@ -25,9 +26,9 @@ import type {
25
26
  } from './types'
26
27
 
27
28
  /**
28
- * Instruments an MCP server so PostHog auto-captures tool calls, tool listings, initialize
29
- * requests, identity, and exceptions. Returns a handle whose `capture()` method records
30
- * custom events, so you don't pass the server around after wiring it up.
29
+ * Instruments an MCP server so PostHog auto-captures tool calls, tool and resource listings,
30
+ * resource reads, initialize requests, identity, and exceptions. Returns a handle whose
31
+ * `capture()` method records custom events, so you don't pass the server around after wiring it up.
31
32
  *
32
33
  * **Idempotent per server instance.** Per-server tracking state lives in a module-level
33
34
  * `WeakMap<MCPServerLike, MCPAnalyticsData>` (`internal.ts`); a second `instrument()` call
@@ -52,6 +53,17 @@ import type {
52
53
  */
53
54
  function instrument(server: unknown, posthog: PostHog, options: MCPAnalyticsOptions = {}): McpAnalytics {
54
55
  const logger = createLogger(options?.logger)
56
+
57
+ // Fail fast on a `collectFeedback` config error (reserved extra key,
58
+ // undeclared extraRequired). Above the graceful-degradation try so it throws
59
+ // out of instrument() like PostHogMCP's constructor does — inside the catch it
60
+ // would silently disable ALL analytics for the server, not just feedback.
61
+ // `options?.` — untyped JavaScript can pass null options (see logger-isolation.test.ts).
62
+ const feedbackOptions = resolveCollectFeedbackOptions(options?.collectFeedback)
63
+ if (feedbackOptions) {
64
+ getFeedbackToolDescriptor(feedbackOptions)
65
+ }
66
+
55
67
  try {
56
68
  if (!posthog) {
57
69
  logger('Warning: No PostHog client passed to instrument(). Events will not be sent anywhere.')
@@ -129,6 +141,7 @@ function getLowLevelServer(server: MCPServerLike | HighLevelMCPServerLike): MCPS
129
141
  */
130
142
  const DEFAULT_OPTIONS = {
131
143
  reportMissing: false,
144
+ collectFeedback: false,
132
145
  enableConversationId: false,
133
146
  } satisfies Partial<MCPAnalyticsOptions>
134
147
 
@@ -215,14 +228,22 @@ export {
215
228
  export { getRequestHeaders } from './extensions/request-headers'
216
229
  export { PostHogMCP, type PostHogMCPOptions } from './extensions/posthog-mcp'
217
230
  export { getMoreToolsResult } from './extensions/tools'
231
+ export { sendFeedbackResult, SEND_FEEDBACK_TOOL_NAME } from './extensions/feedback'
218
232
  export { setLogger } from './extensions/logger'
219
233
  // Re-export the posthog-node client so a single import works:
220
234
  // import { PostHog, instrument } from "@posthog/mcp"
221
235
  // posthog-node stays a peer dependency, so this resolves the host app's installed copy.
222
236
  export { PostHog, type PostHogOptions } from 'posthog-node'
223
237
  export type {
238
+ FeedbackCaptureData,
239
+ FeedbackExtraPropertySchema,
240
+ CollectFeedbackOptions,
241
+ FeedbackReport,
242
+ FeedbackSentiment,
243
+ FeedbackType,
224
244
  BeforeSendFn,
225
245
  CaptureEventData,
246
+ CollectFeedbackConfig,
226
247
  InitializeCaptureData,
227
248
  McpAnalytics,
228
249
  McpCaptureCommon,
package/src/types.ts CHANGED
@@ -70,6 +70,7 @@ export interface MCPRequestParamsLike {
70
70
  _meta?: JsonRecord
71
71
  arguments?: JsonRecord
72
72
  name?: string
73
+ uri?: string
73
74
  [key: string]: unknown
74
75
  }
75
76
 
@@ -100,6 +101,20 @@ export interface MCPAnalyticsOptions {
100
101
  logger?: LoggerFn
101
102
  /** Enable the `get_more_tools` virtual tool so agents can report missing functionality. */
102
103
  reportMissing?: boolean
104
+ /**
105
+ * Inject the `send_feedback` virtual tool so agents can send feedback about
106
+ * this server to its developers — a missing capability (the priority
107
+ * category), a tool that failed or confused them, or praise. Calls to it emit
108
+ * `$mcp_feedback` (never a `$mcp_tool_call`). Off by default.
109
+ *
110
+ * `true` uses the defaults; the object form renames the tool, replaces its
111
+ * description, declares host-specific `extraProperties`, or wires an
112
+ * `onFeedback` handler that routes reports to a real backend.
113
+ *
114
+ * Covers what `reportMissing` covers (as `feedback_type: "missing_capability"`),
115
+ * so new integrations should enable only one of the two.
116
+ */
117
+ collectFeedback?: CollectFeedbackConfig
103
118
  /**
104
119
  * Rename the `get_more_tools` virtual tool (the `reportMissing` feature).
105
120
  * Defaults to `get_more_tools`. Set once here so the tool is advertised and
@@ -185,6 +200,78 @@ export interface MCPAnalyticsContextOptions {
185
200
  description?: string
186
201
  }
187
202
 
203
+ export type FeedbackType = 'missing_capability' | 'issue' | 'praise' | 'other'
204
+ export type FeedbackSentiment = 'positive' | 'neutral' | 'negative' | 'mixed'
205
+
206
+ /** The `collectFeedback` option: `true` for the defaults, or the object form. */
207
+ export type CollectFeedbackConfig = boolean | CollectFeedbackOptions
208
+
209
+ /**
210
+ * A host-declared input-schema fragment for one `send_feedback` extra property —
211
+ * plain JSON Schema, the same shape the MCP `tools/list` wire format uses.
212
+ */
213
+ export interface FeedbackExtraPropertySchema {
214
+ type: string
215
+ description?: string
216
+ enum?: string[]
217
+ [key: string]: unknown
218
+ }
219
+
220
+ /** Object form of {@link CollectFeedbackConfig}. */
221
+ export interface CollectFeedbackOptions {
222
+ /**
223
+ * Rename the `send_feedback` virtual tool. Set once so the tool is advertised
224
+ * and detected under the same name. Defaults to `send_feedback`.
225
+ */
226
+ toolName?: string
227
+ /** Replace the default tool description. */
228
+ description?: string
229
+ /**
230
+ * Host-specific fields merged into the tool's advertised input schema. Each
231
+ * declared key is captured as a `$mcp_feedback_<key>` event property (through
232
+ * the standard sanitize/truncate pipeline); arguments the agent invents beyond
233
+ * the schema are never captured. A key that collides with a core field or an
234
+ * SDK-injected argument throws at configuration time.
235
+ */
236
+ extraProperties?: Record<string, FeedbackExtraPropertySchema>
237
+ /** Keys of `extraProperties` to advertise as required. */
238
+ extraRequired?: string[]
239
+ /**
240
+ * Route each report to a real backend (`instrument()` path only — a custom
241
+ * dispatcher routes reports itself, see {@link PreparedToolCall.isFeedback}).
242
+ * Return a string to replace the default acknowledgement text. A throw is
243
+ * logged and falls back to the default reply; the `$mcp_feedback` event is
244
+ * captured either way. The returned string is captured as `$mcp_response`
245
+ * through the generic sanitize pipeline only — unlike `$mcp_feedback_summary`
246
+ * / `details`, it does not get structured-PII redaction, so avoid echoing
247
+ * the agent's raw report text back in it.
248
+ */
249
+ onFeedback?: (report: FeedbackReport) => MaybePromise<string | void>
250
+ }
251
+
252
+ /** One parsed `send_feedback` call, as handed to `onFeedback` and the dispatcher. */
253
+ export interface FeedbackReport {
254
+ /** Invalid or missing values fall back to `other`. */
255
+ feedbackType: FeedbackType
256
+ /** One-sentence summary; empty string when the agent omitted it. */
257
+ summary: string
258
+ sentiment?: FeedbackSentiment
259
+ frictionPoints?: string
260
+ suggestedImprovement?: string
261
+ details?: string
262
+ /** The existing tool the feedback is about (`tool_name` argument). */
263
+ toolName?: string
264
+ taskCompleted?: boolean
265
+ /**
266
+ * Values of the declared `extraProperties` fields that match their declared
267
+ * `type`/`enum`. A value the agent sent with the wrong shape is left out
268
+ * (find it in `raw` if you need it), so these are safe to trust as declared.
269
+ */
270
+ extras: JsonRecord
271
+ /** The full raw arguments, for the handler only — never captured. */
272
+ raw: JsonRecord
273
+ }
274
+
188
275
  export interface MCPAnalyticsModelOptions {
189
276
  description?: string
190
277
  }
@@ -589,6 +676,14 @@ export interface PrepareToolListOptions {
589
676
  * {@link PostHogMCP.captureMissingCapability} and reply with `getMoreToolsResult()`.
590
677
  */
591
678
  reportMissing?: boolean
679
+ /**
680
+ * Append the `send_feedback` virtual tool so agents can send feedback.
681
+ * Defaults to `false`, and requires the `PostHogMCP` constructor's
682
+ * `collectFeedback` option (the enable switch that also gates detection).
683
+ * When the agent calls it, route the call to
684
+ * {@link PostHogMCP.captureFeedback} and reply with `sendFeedbackResult()`.
685
+ */
686
+ collectFeedback?: boolean
592
687
  }
593
688
 
594
689
  /** Options for {@link PostHogMCP.prepareToolCall}. */
@@ -620,6 +715,18 @@ export interface PreparedToolCall {
620
715
  args?: Record<string, unknown>
621
716
  /** True when `name` is the `get_more_tools` virtual tool. */
622
717
  isMissingCapability: boolean
718
+ /**
719
+ * True when `name` is the `send_feedback` virtual tool AND the constructor's
720
+ * `collectFeedback` option is set. Always false without that opt-in, so a real
721
+ * tool that happens to use the name is never shadowed.
722
+ */
723
+ isFeedback: boolean
724
+ /**
725
+ * The parsed feedback report, set only when {@link PreparedToolCall.isFeedback}
726
+ * is true. Pass it to {@link PostHogMCP.captureFeedback} and to your own
727
+ * feedback backend, then reply with `sendFeedbackResult()` or a custom text.
728
+ */
729
+ feedbackReport?: FeedbackReport
623
730
  }
624
731
 
625
732
  /** Payload for {@link PostHogMCP.captureMissingCapability}. Emits `$mcp_missing_capability`. */
@@ -636,3 +743,16 @@ export interface MissingCapabilityCaptureData extends McpCaptureCommon {
636
743
  /** Captured call arguments → `$mcp_parameters` (sanitized + truncated). */
637
744
  parameters?: unknown
638
745
  }
746
+
747
+ /** Payload for {@link PostHogMCP.captureFeedback}. Emits `$mcp_feedback`. */
748
+ export interface FeedbackCaptureData extends McpCaptureCommon {
749
+ /**
750
+ * The parsed report (from {@link PreparedToolCall.feedbackReport}) →
751
+ * `$mcp_feedback_*` properties, with the summary and details as `$mcp_intent`.
752
+ */
753
+ report: FeedbackReport
754
+ /** The calling model id -> `$mcp_llm_model`. */
755
+ llmModel?: string
756
+ /** How the model id was obtained -> `$mcp_llm_model_source`. */
757
+ llmModelSource?: MCPAnalyticsModelSource
758
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.14.1'
1
+ export const version = '0.16.0'