within-sdk 1.0.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 (69) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +77 -0
  3. package/dist/index.d.ts +16 -0
  4. package/dist/index.js +243 -0
  5. package/dist/middleware.d.ts +40 -0
  6. package/dist/middleware.js +562 -0
  7. package/dist/modules/compatibility.d.ts +18 -0
  8. package/dist/modules/compatibility.js +99 -0
  9. package/dist/modules/constants.d.ts +12 -0
  10. package/dist/modules/constants.js +12 -0
  11. package/dist/modules/context-parameters.d.ts +13 -0
  12. package/dist/modules/context-parameters.js +74 -0
  13. package/dist/modules/diagnostics.d.ts +7 -0
  14. package/dist/modules/diagnostics.js +12 -0
  15. package/dist/modules/eventQueue.d.ts +32 -0
  16. package/dist/modules/eventQueue.js +234 -0
  17. package/dist/modules/exceptions.d.ts +13 -0
  18. package/dist/modules/exceptions.js +730 -0
  19. package/dist/modules/exporters/datadog.d.ts +17 -0
  20. package/dist/modules/exporters/datadog.js +166 -0
  21. package/dist/modules/exporters/otlp.d.ts +13 -0
  22. package/dist/modules/exporters/otlp.js +140 -0
  23. package/dist/modules/exporters/posthog.d.ts +32 -0
  24. package/dist/modules/exporters/posthog.js +272 -0
  25. package/dist/modules/exporters/sentry.d.ts +27 -0
  26. package/dist/modules/exporters/sentry.js +386 -0
  27. package/dist/modules/exporters/trace-context.d.ts +8 -0
  28. package/dist/modules/exporters/trace-context.js +27 -0
  29. package/dist/modules/index.d.ts +8 -0
  30. package/dist/modules/index.js +8 -0
  31. package/dist/modules/internal.d.ts +33 -0
  32. package/dist/modules/internal.js +195 -0
  33. package/dist/modules/logging.d.ts +2 -0
  34. package/dist/modules/logging.js +74 -0
  35. package/dist/modules/mcp-sdk-compat.d.ts +32 -0
  36. package/dist/modules/mcp-sdk-compat.js +111 -0
  37. package/dist/modules/privacy.d.ts +15 -0
  38. package/dist/modules/privacy.js +179 -0
  39. package/dist/modules/redaction.d.ts +11 -0
  40. package/dist/modules/redaction.js +81 -0
  41. package/dist/modules/sanitization.d.ts +9 -0
  42. package/dist/modules/sanitization.js +111 -0
  43. package/dist/modules/session.d.ts +22 -0
  44. package/dist/modules/session.js +109 -0
  45. package/dist/modules/telemetry.d.ts +8 -0
  46. package/dist/modules/telemetry.js +53 -0
  47. package/dist/modules/tools.d.ts +25 -0
  48. package/dist/modules/tools.js +119 -0
  49. package/dist/modules/tracing.d.ts +4 -0
  50. package/dist/modules/tracing.js +263 -0
  51. package/dist/modules/tracingV2.d.ts +2 -0
  52. package/dist/modules/tracingV2.js +300 -0
  53. package/dist/modules/truncation.d.ts +24 -0
  54. package/dist/modules/truncation.js +303 -0
  55. package/dist/modules/validation.d.ts +6 -0
  56. package/dist/modules/validation.js +51 -0
  57. package/dist/network.d.ts +83 -0
  58. package/dist/network.js +411 -0
  59. package/dist/proxy.d.ts +31 -0
  60. package/dist/proxy.js +158 -0
  61. package/dist/thirdparty/ksuid/base-convert-int-array.js +49 -0
  62. package/dist/thirdparty/ksuid/base62.js +24 -0
  63. package/dist/thirdparty/ksuid/index.d.ts +30 -0
  64. package/dist/thirdparty/ksuid/index.js +201 -0
  65. package/dist/types.d.ts +197 -0
  66. package/dist/types.js +5 -0
  67. package/dist/validate.d.ts +53 -0
  68. package/dist/validate.js +139 -0
  69. package/package.json +43 -0
@@ -0,0 +1,109 @@
1
+ import { getServerTrackingData, setServerTrackingData } from "./internal.js";
2
+ import KSUID from "../thirdparty/ksuid/index.js";
3
+ import { createHash } from "crypto";
4
+ import { INACTIVITY_TIMEOUT_IN_MINUTES, SDK_VERSION } from "./constants.js";
5
+ export function newSessionId() {
6
+ return KSUID.withPrefix("ses").randomSync();
7
+ }
8
+ /**
9
+ * Creates a deterministic KSUID session ID from an MCP sessionId and optional projectId.
10
+ * The same inputs will always produce the same session ID, enabling correlation across server restarts.
11
+ *
12
+ * @param mcpSessionId - The session ID from the MCP protocol
13
+ * @param projectId - Optional Within project ID to include in the hash
14
+ * @returns A KSUID with "ses" prefix derived deterministically from the inputs
15
+ */
16
+ export function deriveSessionIdFromMCPSession(mcpSessionId, projectId) {
17
+ // Create input string for hashing
18
+ const input = projectId ? `${mcpSessionId}:${projectId}` : mcpSessionId;
19
+ // Hash the input with SHA-256
20
+ const hash = createHash("sha256").update(input).digest();
21
+ // Extract timestamp from first 4 bytes of hash (for deterministic but reasonable timestamp)
22
+ // We'll use a fixed epoch (2024-01-01) plus the hash value to get a deterministic but valid timestamp
23
+ const EPOCH_2024 = new Date("2024-01-01T00:00:00Z").getTime();
24
+ const timestampOffset = hash.readUInt32BE(0) % (365 * 24 * 60 * 60 * 1000); // Max 1 year offset
25
+ const timestamp = EPOCH_2024 + timestampOffset;
26
+ // Use the remaining 16 bytes of hash as the KSUID payload
27
+ const payload = hash.subarray(4, 20);
28
+ // Create deterministic KSUID with prefix
29
+ return KSUID.withPrefix("ses").fromParts(timestamp, payload);
30
+ }
31
+ /**
32
+ * Gets or generates a session ID for the server.
33
+ * Prioritizes MCP protocol sessionId over Within-generated sessionId.
34
+ *
35
+ * @param server - The MCP server instance
36
+ * @param extra - Optional extra data containing MCP sessionId
37
+ * @returns The session ID to use for events
38
+ */
39
+ export function getServerSessionId(server, extra) {
40
+ const data = getServerTrackingData(server);
41
+ if (!data) {
42
+ throw new Error("Server tracking data not found");
43
+ }
44
+ const mcpSessionId = extra?.sessionId;
45
+ // If MCP sessionId is provided
46
+ if (mcpSessionId) {
47
+ // Derive deterministic KSUID from MCP sessionId
48
+ data.sessionId = deriveSessionIdFromMCPSession(mcpSessionId, data.projectId || undefined);
49
+ data.lastMcpSessionId = mcpSessionId;
50
+ data.sessionSource = "mcp";
51
+ setServerTrackingData(server, data);
52
+ // If MCP sessionId hasn't changed, continue using the existing derived KSUID
53
+ setLastActivity(server);
54
+ return data.sessionId;
55
+ }
56
+ // No MCP sessionId provided - handle Within-generated sessions
57
+ // If we had an MCP sessionId before but it disappeared, keep using the last derived ID
58
+ if (data.sessionSource === "mcp" && data.lastMcpSessionId) {
59
+ setLastActivity(server);
60
+ return data.sessionId;
61
+ }
62
+ // For Within-generated sessions, apply timeout logic
63
+ const now = Date.now();
64
+ const timeoutMs = INACTIVITY_TIMEOUT_IN_MINUTES * 60 * 1000;
65
+ // If last activity timed out
66
+ if (now - data.lastActivity.getTime() > timeoutMs) {
67
+ data.sessionId = newSessionId();
68
+ data.sessionSource = "within";
69
+ setServerTrackingData(server, data);
70
+ }
71
+ setLastActivity(server);
72
+ return data.sessionId;
73
+ }
74
+ export function setLastActivity(server) {
75
+ const data = getServerTrackingData(server);
76
+ if (!data) {
77
+ throw new Error("Server tracking data not found");
78
+ }
79
+ data.lastActivity = new Date();
80
+ setServerTrackingData(server, data);
81
+ }
82
+ export function getSessionInfo(server, data) {
83
+ let clientInfo = {
84
+ name: undefined,
85
+ version: undefined,
86
+ };
87
+ if (!data?.sessionInfo.clientName) {
88
+ clientInfo = server.getClientVersion();
89
+ }
90
+ const actorInfo = data?.identifiedSessions.get(data.sessionId);
91
+ const sessionInfo = {
92
+ ipAddress: undefined, // grab from django
93
+ sdkLanguage: "TypeScript", // hardcoded for now
94
+ sdkVersion: SDK_VERSION,
95
+ serverName: server._serverInfo?.name,
96
+ serverVersion: server._serverInfo?.version,
97
+ clientName: clientInfo?.name,
98
+ clientVersion: clientInfo?.version,
99
+ identifyActorGivenId: actorInfo?.userId ?? data?.sessionInfo.identifyActorGivenId,
100
+ identifyActorName: actorInfo?.userName,
101
+ identifyActorData: actorInfo?.userData || data?.sessionInfo.identifyActorData || {},
102
+ };
103
+ if (!data) {
104
+ return sessionInfo;
105
+ }
106
+ data.sessionInfo = sessionInfo;
107
+ setServerTrackingData(server, data);
108
+ return data.sessionInfo;
109
+ }
@@ -0,0 +1,8 @@
1
+ import { Event, ExporterConfig } from "../types.js";
2
+ export declare class TelemetryManager {
3
+ private exporters;
4
+ constructor(exporterConfigs?: Record<string, ExporterConfig>);
5
+ private createExporter;
6
+ export(event: Event): Promise<void>;
7
+ getExporterCount(): number;
8
+ }
@@ -0,0 +1,53 @@
1
+ import { writeToLog } from "./logging.js";
2
+ import { OTLPExporter } from "./exporters/otlp.js";
3
+ import { DatadogExporter } from "./exporters/datadog.js";
4
+ import { SentryExporter } from "./exporters/sentry.js";
5
+ import { PostHogExporter } from "./exporters/posthog.js";
6
+ export class TelemetryManager {
7
+ exporters = new Map();
8
+ constructor(exporterConfigs) {
9
+ if (!exporterConfigs)
10
+ return;
11
+ for (const [name, config] of Object.entries(exporterConfigs)) {
12
+ try {
13
+ const exporter = this.createExporter(name, config);
14
+ if (exporter) {
15
+ this.exporters.set(name, exporter);
16
+ writeToLog(`Initialized telemetry exporter: ${name}`);
17
+ }
18
+ }
19
+ catch (error) {
20
+ writeToLog(`Failed to initialize exporter ${name}: ${error}`);
21
+ }
22
+ }
23
+ }
24
+ createExporter(name, config) {
25
+ switch (config.type) {
26
+ case "otlp":
27
+ return new OTLPExporter(config);
28
+ case "datadog":
29
+ return new DatadogExporter(config);
30
+ case "sentry":
31
+ return new SentryExporter(config);
32
+ case "posthog":
33
+ return new PostHogExporter(config);
34
+ default:
35
+ writeToLog(`Unknown exporter type: ${config.type}`);
36
+ return null;
37
+ }
38
+ }
39
+ async export(event) {
40
+ if (this.exporters.size === 0)
41
+ return;
42
+ // Telemetry errors should be logged but not propagated
43
+ for (const [name, exporter] of this.exporters) {
44
+ exporter.export(event).catch((error) => {
45
+ const errorMessage = error instanceof Error ? error.message : String(error);
46
+ writeToLog(`Telemetry export failed for ${name}: ${errorMessage}`);
47
+ });
48
+ }
49
+ }
50
+ getExporterCount() {
51
+ return this.exporters.size;
52
+ }
53
+ }
@@ -0,0 +1,25 @@
1
+ import { MCPServerLike } from "../types.js";
2
+ export declare const GET_MORE_TOOLS_NAME: "get_more_tools";
3
+ export declare function getReportMissingToolDescriptor(): {
4
+ readonly name: "get_more_tools";
5
+ readonly description: "Check for additional tools whenever your task might benefit from specialized capabilities - even if existing tools could work as a fallback.";
6
+ readonly inputSchema: {
7
+ readonly type: "object";
8
+ readonly properties: {
9
+ readonly context: {
10
+ readonly type: "string";
11
+ readonly description: "A description of your goal and what kind of tool would help accomplish it.";
12
+ };
13
+ };
14
+ readonly required: readonly ["context"];
15
+ };
16
+ };
17
+ export declare function handleReportMissing(args: {
18
+ context: string;
19
+ }): {
20
+ content: {
21
+ type: "text";
22
+ text: string;
23
+ }[];
24
+ };
25
+ export declare function setupWithinFeedbackTools(server: MCPServerLike): void;
@@ -0,0 +1,119 @@
1
+ import { ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
2
+ import { writeToLog } from "./logging.js";
3
+ import { getServerTrackingData, handleIdentify } from "./internal.js";
4
+ import { addContextParameterToTools } from "./context-parameters.js";
5
+ import { publishEvent } from "./eventQueue.js";
6
+ import { getServerSessionId } from "./session.js";
7
+ import { WithinEventType } from "./constants.js";
8
+ import { getMCPCompatibleErrorMessage } from "./compatibility.js";
9
+ export const GET_MORE_TOOLS_NAME = "get_more_tools";
10
+ export function getReportMissingToolDescriptor() {
11
+ return {
12
+ name: GET_MORE_TOOLS_NAME,
13
+ description: "Check for additional tools whenever your task might benefit from specialized capabilities - even if existing tools could work as a fallback.",
14
+ inputSchema: {
15
+ type: "object",
16
+ properties: {
17
+ context: {
18
+ type: "string",
19
+ description: "A description of your goal and what kind of tool would help accomplish it.",
20
+ },
21
+ },
22
+ required: ["context"],
23
+ },
24
+ };
25
+ }
26
+ export function handleReportMissing(args) {
27
+ writeToLog(`Missing tool reported (context length: ${args?.context?.length ?? 0})`);
28
+ return {
29
+ content: [
30
+ {
31
+ type: "text",
32
+ text: `Unfortunately, we have shown you the full tool list. We have noted your feedback and will work to improve the tool list in the future.`,
33
+ },
34
+ ],
35
+ };
36
+ }
37
+ export function setupWithinFeedbackTools(server) {
38
+ // Store reference to original handlers - need to use the method name, not the schema
39
+ const handlers = server._requestHandlers;
40
+ const originalListToolsHandler = handlers.get("tools/list");
41
+ const originalCallToolHandler = handlers.get("tools/call");
42
+ if (!originalListToolsHandler || !originalCallToolHandler) {
43
+ writeToLog("Warning: Original tool handlers not found. Your tools may not be setup before Within track().");
44
+ return;
45
+ }
46
+ // Override tools list to include get_more_tools and add context parameter
47
+ try {
48
+ server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
49
+ let tools = [];
50
+ const data = getServerTrackingData(server);
51
+ let event = {
52
+ sessionId: getServerSessionId(server, extra),
53
+ parameters: {
54
+ request: request,
55
+ extra: extra,
56
+ },
57
+ eventType: WithinEventType.mcpToolsList,
58
+ timestamp: new Date(),
59
+ redactionFn: data?.options.redactSensitiveInformation,
60
+ };
61
+ if (data) {
62
+ await handleIdentify(server, data, request, extra);
63
+ event.sessionId = data.sessionId;
64
+ }
65
+ try {
66
+ const originalResponse = (await originalListToolsHandler(request, extra));
67
+ tools = originalResponse.tools || [];
68
+ }
69
+ catch (error) {
70
+ // If original handler fails, start with empty tools
71
+ writeToLog(`Warning: Original list tools handler failed, this suggests an error Within did not cause - ${error}`);
72
+ event.error = { message: getMCPCompatibleErrorMessage(error) };
73
+ event.isError = true;
74
+ event.duration =
75
+ (event.timestamp &&
76
+ new Date().getTime() - event.timestamp.getTime()) ||
77
+ 0;
78
+ publishEvent(server, event);
79
+ throw error;
80
+ }
81
+ if (!data) {
82
+ writeToLog("Warning: Within SDK is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
83
+ return { tools };
84
+ }
85
+ if (tools.length === 0) {
86
+ writeToLog("Warning: No tools found in the original list. This is likely due to the tools not being registered before Within.track().");
87
+ event.error = { message: "No tools were sent to MCP client." };
88
+ event.isError = true;
89
+ event.duration =
90
+ (event.timestamp &&
91
+ new Date().getTime() - event.timestamp.getTime()) ||
92
+ 0;
93
+ publishEvent(server, event);
94
+ return { tools };
95
+ }
96
+ // Add context parameter to all existing tools if enableToolCallContext is true
97
+ if (data.options.enableToolCallContext) {
98
+ tools = addContextParameterToTools(tools, data.options.customContextDescription);
99
+ }
100
+ // Add report_missing tool if enabled
101
+ if (data.options.enableReportMissing) {
102
+ const alreadyPresent = tools.some((t) => t?.name === GET_MORE_TOOLS_NAME);
103
+ if (!alreadyPresent) {
104
+ tools.push(getReportMissingToolDescriptor());
105
+ }
106
+ }
107
+ event.response = { tools };
108
+ event.isError = false;
109
+ event.duration =
110
+ (event.timestamp && new Date().getTime() - event.timestamp.getTime()) ||
111
+ 0;
112
+ publishEvent(server, event);
113
+ return { tools };
114
+ });
115
+ }
116
+ catch (error) {
117
+ writeToLog(`Warning: Failed to override list tools handler - ${error}`);
118
+ }
119
+ }
@@ -0,0 +1,4 @@
1
+ import { HighLevelMCPServerLike, MCPServerLike } from "../types.js";
2
+ export declare function setupListToolsTracing(highLevelServer: HighLevelMCPServerLike): void;
3
+ export declare function setupInitializeTracing(highLevelServer: HighLevelMCPServerLike): void;
4
+ export declare function setupToolCallTracing(server: MCPServerLike): void;
@@ -0,0 +1,263 @@
1
+ import { CallToolRequestSchema, InitializeRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
2
+ import { writeToLog } from "./logging.js";
3
+ import { handleReportMissing } from "./tools.js";
4
+ import { getServerTrackingData, handleIdentify, resolveEventTags, resolveEventProperties, } from "./internal.js";
5
+ import { getServerSessionId } from "./session.js";
6
+ import { WithinEventType } from "./constants.js";
7
+ import { publishEvent } from "./eventQueue.js";
8
+ import { getMCPCompatibleErrorMessage } from "./compatibility.js";
9
+ import { captureException } from "./exceptions.js";
10
+ import { addContextParameterToTools } from "./context-parameters.js";
11
+ import { GET_MORE_TOOLS_NAME, getReportMissingToolDescriptor, } from "./tools.js";
12
+ function isToolResultError(result) {
13
+ return result && typeof result === "object" && result.isError === true;
14
+ }
15
+ // Track if we've already set up list tools tracing per server instance
16
+ const listToolsTracingSetup = new WeakMap();
17
+ export function setupListToolsTracing(highLevelServer) {
18
+ const server = highLevelServer.server;
19
+ // Check if server supports tools capability
20
+ if (!server._capabilities?.tools) {
21
+ // Server doesn't support tools yet, skip setup
22
+ return;
23
+ }
24
+ // Check if we've already set up tracing for this server instance
25
+ if (listToolsTracingSetup.get(server)) {
26
+ return;
27
+ }
28
+ const handlers = server._requestHandlers;
29
+ const originalListToolsHandler = handlers.get("tools/list");
30
+ // No handler to override yet
31
+ if (!originalListToolsHandler) {
32
+ return;
33
+ }
34
+ try {
35
+ server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
36
+ let tools = [];
37
+ const data = getServerTrackingData(server);
38
+ let event = {
39
+ sessionId: getServerSessionId(server, extra),
40
+ parameters: {
41
+ request: request,
42
+ extra: extra,
43
+ },
44
+ eventType: WithinEventType.mcpToolsList,
45
+ timestamp: new Date(),
46
+ redactionFn: data?.options.redactSensitiveInformation,
47
+ };
48
+ if (data) {
49
+ await handleIdentify(server, data, request, extra);
50
+ event.sessionId = data.sessionId;
51
+ const resolvedTags = await resolveEventTags(data, request, extra);
52
+ if (resolvedTags)
53
+ event.tags = resolvedTags;
54
+ const resolvedProperties = await resolveEventProperties(data, request, extra);
55
+ if (resolvedProperties)
56
+ event.properties = resolvedProperties;
57
+ }
58
+ try {
59
+ const originalResponse = (await originalListToolsHandler(request, extra));
60
+ tools = originalResponse.tools || [];
61
+ // Inject context parameters AFTER MCP SDK has converted Zod to JSON Schema
62
+ if (data?.options.enableToolCallContext) {
63
+ tools = addContextParameterToTools(tools, data.options.customContextDescription);
64
+ }
65
+ // Add get_more_tools tool when enabled
66
+ if (data?.options.enableReportMissing) {
67
+ const alreadyPresent = tools.some((t) => t?.name === GET_MORE_TOOLS_NAME);
68
+ if (!alreadyPresent)
69
+ tools.push(getReportMissingToolDescriptor());
70
+ }
71
+ }
72
+ catch (error) {
73
+ // If original handler fails, start with empty tools
74
+ writeToLog(`Warning: Original list tools handler failed, this suggests an error Within did not cause - ${error}`);
75
+ event.error = { message: getMCPCompatibleErrorMessage(error) };
76
+ event.isError = true;
77
+ event.duration =
78
+ (event.timestamp &&
79
+ new Date().getTime() - event.timestamp.getTime()) ||
80
+ 0;
81
+ publishEvent(server, event);
82
+ throw error;
83
+ }
84
+ if (!data) {
85
+ writeToLog("Warning: Within SDK is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
86
+ return { tools };
87
+ }
88
+ if (tools.length === 0) {
89
+ writeToLog("Warning: No tools found in the original list. This is likely due to the tools not being registered before Within.track().");
90
+ event.error = { message: "No tools were sent to MCP client." };
91
+ event.isError = true;
92
+ event.duration =
93
+ (event.timestamp &&
94
+ new Date().getTime() - event.timestamp.getTime()) ||
95
+ 0;
96
+ publishEvent(server, event);
97
+ return { tools };
98
+ }
99
+ event.response = { tools };
100
+ event.isError = false;
101
+ event.duration =
102
+ (event.timestamp && new Date().getTime() - event.timestamp.getTime()) ||
103
+ 0;
104
+ publishEvent(server, event);
105
+ return { tools };
106
+ });
107
+ // Mark as setup successful for this server instance
108
+ listToolsTracingSetup.set(server, true);
109
+ }
110
+ catch (error) {
111
+ writeToLog(`Warning: Failed to override list tools handler - ${error}`);
112
+ }
113
+ }
114
+ export function setupInitializeTracing(highLevelServer) {
115
+ const server = highLevelServer.server;
116
+ const handlers = server._requestHandlers;
117
+ const originalInitializeHandler = handlers.get("initialize");
118
+ if (originalInitializeHandler) {
119
+ server.setRequestHandler(InitializeRequestSchema, async (request, extra) => {
120
+ const data = getServerTrackingData(server);
121
+ if (!data) {
122
+ writeToLog("Warning: Within SDK is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
123
+ return await originalInitializeHandler(request, extra);
124
+ }
125
+ const sessionId = getServerSessionId(server, extra);
126
+ // Try to identify the session
127
+ await handleIdentify(server, data, request, extra);
128
+ let event = {
129
+ sessionId: sessionId,
130
+ resourceName: request.params?.name || "Unknown Tool Name",
131
+ eventType: WithinEventType.mcpInitialize,
132
+ parameters: {
133
+ request: request,
134
+ extra: extra,
135
+ },
136
+ timestamp: new Date(),
137
+ redactionFn: data.options.redactSensitiveInformation,
138
+ };
139
+ const resolvedTags = await resolveEventTags(data, request, extra);
140
+ if (resolvedTags)
141
+ event.tags = resolvedTags;
142
+ const resolvedProperties = await resolveEventProperties(data, request, extra);
143
+ if (resolvedProperties)
144
+ event.properties = resolvedProperties;
145
+ const result = await originalInitializeHandler(request, extra);
146
+ event.response = result;
147
+ publishEvent(server, event);
148
+ return result;
149
+ });
150
+ }
151
+ }
152
+ export function setupToolCallTracing(server) {
153
+ try {
154
+ const handlers = server._requestHandlers;
155
+ const originalCallToolHandler = handlers.get("tools/call");
156
+ const originalInitializeHandler = handlers.get("initialize");
157
+ if (originalInitializeHandler) {
158
+ server.setRequestHandler(InitializeRequestSchema, async (request, extra) => {
159
+ const data = getServerTrackingData(server);
160
+ if (!data) {
161
+ writeToLog("Warning: Within SDK is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
162
+ return await originalInitializeHandler(request, extra);
163
+ }
164
+ const sessionId = getServerSessionId(server, extra);
165
+ // Try to identify the session
166
+ await handleIdentify(server, data, request, extra);
167
+ let event = {
168
+ sessionId: sessionId,
169
+ resourceName: request.params?.name || "Unknown Tool Name",
170
+ eventType: WithinEventType.mcpInitialize,
171
+ parameters: {
172
+ request: request,
173
+ extra: extra,
174
+ },
175
+ timestamp: new Date(),
176
+ redactionFn: data.options.redactSensitiveInformation,
177
+ };
178
+ const resolvedTags = await resolveEventTags(data, request, extra);
179
+ if (resolvedTags)
180
+ event.tags = resolvedTags;
181
+ const resolvedProperties = await resolveEventProperties(data, request, extra);
182
+ if (resolvedProperties)
183
+ event.properties = resolvedProperties;
184
+ const result = await originalInitializeHandler(request, extra);
185
+ event.response = result;
186
+ publishEvent(server, event);
187
+ return result;
188
+ });
189
+ }
190
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
191
+ const data = getServerTrackingData(server);
192
+ if (!data) {
193
+ writeToLog("Warning: Within SDK is unable to find server tracking data. Please ensure you have called track(server, options) before using tool calls.");
194
+ return await originalCallToolHandler?.(request, extra);
195
+ }
196
+ const sessionId = getServerSessionId(server, extra);
197
+ let event = {
198
+ sessionId: sessionId,
199
+ resourceName: request.params?.name || "Unknown Tool Name",
200
+ parameters: {
201
+ request: request,
202
+ extra: extra,
203
+ },
204
+ eventType: WithinEventType.mcpToolsCall,
205
+ timestamp: new Date(),
206
+ redactionFn: data.options.redactSensitiveInformation,
207
+ };
208
+ try {
209
+ // Try to identify the session if we haven't already and identify function is provided
210
+ await handleIdentify(server, data, request, extra);
211
+ const resolvedTags = await resolveEventTags(data, request, extra);
212
+ if (resolvedTags)
213
+ event.tags = resolvedTags;
214
+ const resolvedProperties = await resolveEventProperties(data, request, extra);
215
+ if (resolvedProperties)
216
+ event.properties = resolvedProperties;
217
+ const context = request.params?.arguments?.context;
218
+ if (data.options.enableToolCallContext &&
219
+ typeof context === "string") {
220
+ event.userIntent = context;
221
+ }
222
+ let result;
223
+ if (request.params?.name === "get_more_tools") {
224
+ result = await handleReportMissing(request.params.arguments.context);
225
+ event.userIntent = request.params.arguments.context;
226
+ }
227
+ else if (originalCallToolHandler) {
228
+ result = await originalCallToolHandler(request, extra);
229
+ }
230
+ else {
231
+ event.isError = true;
232
+ event.error = {
233
+ message: `Tool call handler not found for ${request.params?.name || "unknown"}`,
234
+ };
235
+ event.duration =
236
+ (event.timestamp &&
237
+ new Date().getTime() - event.timestamp.getTime()) ||
238
+ undefined;
239
+ publishEvent(server, event);
240
+ throw new Error(`Unknown tool: ${request.params?.name || "unknown"}`);
241
+ }
242
+ // Check if the result indicates an error
243
+ if (isToolResultError(result)) {
244
+ event.isError = true;
245
+ event.error = captureException(result);
246
+ }
247
+ event.response = result;
248
+ publishEvent(server, event);
249
+ return result;
250
+ }
251
+ catch (error) {
252
+ event.isError = true;
253
+ event.error = captureException(error);
254
+ publishEvent(server, event);
255
+ throw error;
256
+ }
257
+ });
258
+ }
259
+ catch (error) {
260
+ writeToLog(`Warning: Failed to setup tool call tracing - ${error}`);
261
+ throw error;
262
+ }
263
+ }
@@ -0,0 +1,2 @@
1
+ import { HighLevelMCPServerLike } from "../types.js";
2
+ export declare function setupTracking(server: HighLevelMCPServerLike): void;