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,17 @@
1
+ import { Event, Exporter } from "../../types.js";
2
+ export interface DatadogExporterConfig {
3
+ type: "datadog";
4
+ apiKey: string;
5
+ site: string;
6
+ service: string;
7
+ env?: string;
8
+ }
9
+ export declare class DatadogExporter implements Exporter {
10
+ private logsUrl;
11
+ private metricsUrl;
12
+ private config;
13
+ constructor(config: DatadogExporterConfig);
14
+ export(event: Event): Promise<void>;
15
+ private eventToLog;
16
+ private eventToMetrics;
17
+ }
@@ -0,0 +1,166 @@
1
+ import { writeToLog } from "../logging.js";
2
+ import { traceContext } from "./trace-context.js";
3
+ import { WITHIN_SOURCE } from "../constants.js";
4
+ export class DatadogExporter {
5
+ logsUrl;
6
+ metricsUrl;
7
+ config;
8
+ constructor(config) {
9
+ this.config = config;
10
+ // Build API endpoints based on site
11
+ const site = config.site.replace(/^https?:\/\//, "").replace(/\/$/, "");
12
+ this.logsUrl = `https://http-intake.logs.${site}/api/v2/logs`;
13
+ this.metricsUrl = `https://api.${site}/api/v1/series`;
14
+ }
15
+ async export(event) {
16
+ writeToLog("DatadogExporter: Sending event immediately to Datadog");
17
+ // Convert event to log and metrics
18
+ const log = this.eventToLog(event);
19
+ const metrics = this.eventToMetrics(event);
20
+ // Debug: Log the metrics URL and count (never the serialized payload)
21
+ writeToLog(`DatadogExporter: Metrics URL: ${this.metricsUrl}`);
22
+ writeToLog(`DatadogExporter: Sending ${metrics.length} metric series`);
23
+ // Send logs with response checking
24
+ const logsPromise = fetch(this.logsUrl, {
25
+ method: "POST",
26
+ headers: {
27
+ "DD-API-KEY": this.config.apiKey,
28
+ "Content-Type": "application/json",
29
+ },
30
+ body: JSON.stringify([log]),
31
+ })
32
+ .then(async (response) => {
33
+ if (!response.ok) {
34
+ writeToLog(`Datadog logs failed - Status: ${response.status}`);
35
+ }
36
+ else {
37
+ writeToLog(`Datadog logs success - Status: ${response.status}`);
38
+ }
39
+ return response;
40
+ })
41
+ .catch((err) => {
42
+ writeToLog(`Datadog logs network error: ${err}`);
43
+ });
44
+ // Send metrics with response checking
45
+ const metricsPromise = fetch(this.metricsUrl, {
46
+ method: "POST",
47
+ headers: {
48
+ "DD-API-KEY": this.config.apiKey,
49
+ "Content-Type": "application/json",
50
+ },
51
+ body: JSON.stringify({ series: metrics }),
52
+ })
53
+ .then(async (response) => {
54
+ if (!response.ok) {
55
+ writeToLog(`Datadog metrics failed - Status: ${response.status}`);
56
+ }
57
+ else {
58
+ writeToLog(`Datadog metrics success - Status: ${response.status}`);
59
+ }
60
+ return response;
61
+ })
62
+ .catch((err) => {
63
+ writeToLog(`Datadog metrics network error: ${err}`);
64
+ });
65
+ // Wait for both to complete
66
+ await Promise.all([logsPromise, metricsPromise]);
67
+ }
68
+ eventToLog(event) {
69
+ const tags = [];
70
+ // Add basic tags
71
+ if (this.config.env)
72
+ tags.push(`env:${this.config.env}`);
73
+ if (event.eventType)
74
+ tags.push(`event_type:${event.eventType.replace(/\//g, ".")}`);
75
+ if (event.resourceName)
76
+ tags.push(`resource:${event.resourceName}`);
77
+ if (event.isError)
78
+ tags.push("error:true");
79
+ tags.push(`source:${WITHIN_SOURCE}`);
80
+ // Add customer-defined tags to ddtags (namespaced to avoid collisions with reserved Datadog tags)
81
+ if (event.tags) {
82
+ for (const [key, value] of Object.entries(event.tags)) {
83
+ const sanitizedKey = key.toLowerCase().replace(/[\s:,]+/g, "_");
84
+ const sanitizedValue = value.replace(/,/g, "_");
85
+ tags.push(`within.${sanitizedKey}:${sanitizedValue}`);
86
+ }
87
+ }
88
+ const log = {
89
+ message: `${event.eventType || "unknown"} - ${event.resourceName || "unknown"}`,
90
+ service: this.config.service,
91
+ ddsource: WITHIN_SOURCE,
92
+ ddtags: tags.join(","),
93
+ timestamp: event.timestamp ? event.timestamp.getTime() : Date.now(),
94
+ status: event.isError ? "error" : "info",
95
+ dd: {
96
+ trace_id: traceContext.getDatadogTraceId(event.sessionId),
97
+ span_id: traceContext.getDatadogSpanId(event.id),
98
+ },
99
+ mcp: {
100
+ session_id: event.sessionId,
101
+ event_id: event.id,
102
+ event_type: event.eventType,
103
+ resource: event.resourceName,
104
+ duration_ms: event.duration,
105
+ user_intent: event.userIntent,
106
+ actor_id: event.identifyActorGivenId,
107
+ actor_name: event.identifyActorName,
108
+ client_name: event.clientName,
109
+ client_version: event.clientVersion,
110
+ server_name: event.serverName,
111
+ server_version: event.serverVersion,
112
+ is_error: event.isError,
113
+ error: event.error,
114
+ tags: event.tags,
115
+ properties: event.properties,
116
+ },
117
+ };
118
+ // Add error at root level if it exists
119
+ if (event.isError && event.error) {
120
+ log.error = {
121
+ message: typeof event.error === "string"
122
+ ? event.error
123
+ : JSON.stringify(event.error),
124
+ };
125
+ }
126
+ return log;
127
+ }
128
+ eventToMetrics(event) {
129
+ const metrics = [];
130
+ const timestamp = Math.floor((event.timestamp?.getTime() || Date.now()) / 1000);
131
+ const tags = [`service:${this.config.service}`];
132
+ // Add optional tags
133
+ if (this.config.env)
134
+ tags.push(`env:${this.config.env}`);
135
+ if (event.eventType)
136
+ tags.push(`event_type:${event.eventType.replace(/\//g, ".")}`);
137
+ if (event.resourceName)
138
+ tags.push(`resource:${event.resourceName}`);
139
+ // Event count metric
140
+ metrics.push({
141
+ metric: "mcp.events.count",
142
+ type: "count",
143
+ points: [[timestamp, 1]],
144
+ tags,
145
+ });
146
+ // Duration metric (only if duration exists)
147
+ if (event.duration) {
148
+ metrics.push({
149
+ metric: "mcp.event.duration",
150
+ type: "gauge",
151
+ points: [[timestamp, event.duration]],
152
+ tags,
153
+ });
154
+ }
155
+ // Error count metric
156
+ if (event.isError) {
157
+ metrics.push({
158
+ metric: "mcp.errors.count",
159
+ type: "count",
160
+ points: [[timestamp, 1]],
161
+ tags,
162
+ });
163
+ }
164
+ return metrics;
165
+ }
166
+ }
@@ -0,0 +1,13 @@
1
+ import { Event, Exporter } from "../../types.js";
2
+ export interface OTLPExporterConfig {
3
+ type: "otlp";
4
+ endpoint: string;
5
+ headers?: Record<string, string>;
6
+ }
7
+ export declare class OTLPExporter implements Exporter {
8
+ private endpoint;
9
+ private headers;
10
+ constructor(config: OTLPExporterConfig);
11
+ export(event: Event): Promise<void>;
12
+ private convertToOTLPSpan;
13
+ }
@@ -0,0 +1,140 @@
1
+ import { writeToLog } from "../logging.js";
2
+ import { traceContext } from "./trace-context.js";
3
+ import { WITHIN_SOURCE } from "../constants.js";
4
+ export class OTLPExporter {
5
+ endpoint;
6
+ headers;
7
+ constructor(config) {
8
+ // Auto-append /v1/traces per OTLP spec if not already present
9
+ const url = config.endpoint.replace(/\/+$/, "");
10
+ this.endpoint = url.endsWith("/v1/traces") ? url : `${url}/v1/traces`;
11
+ this.headers = {
12
+ "Content-Type": "application/json", // Using JSON for now for easier debugging
13
+ ...config.headers,
14
+ };
15
+ }
16
+ async export(event) {
17
+ try {
18
+ // Convert Within event to OTLP trace format
19
+ const span = this.convertToOTLPSpan(event);
20
+ // Create OTLP JSON format
21
+ const otlpRequest = {
22
+ resourceSpans: [
23
+ {
24
+ resource: {
25
+ attributes: [
26
+ {
27
+ key: "service.name",
28
+ value: { stringValue: event.serverName || "mcp-server" },
29
+ },
30
+ {
31
+ key: "service.version",
32
+ value: { stringValue: event.serverVersion || "unknown" },
33
+ },
34
+ ],
35
+ },
36
+ scopeSpans: [
37
+ {
38
+ scope: {
39
+ name: "within-sdk",
40
+ version: event.sdkVersion || "unknown",
41
+ },
42
+ spans: [span],
43
+ },
44
+ ],
45
+ },
46
+ ],
47
+ };
48
+ // Use JSON format for now
49
+ const body = JSON.stringify(otlpRequest);
50
+ // Use fetch to send the data
51
+ const response = await fetch(this.endpoint, {
52
+ method: "POST",
53
+ headers: this.headers,
54
+ body,
55
+ });
56
+ if (!response.ok) {
57
+ throw new Error(`OTLP export failed: ${response.status} ${response.statusText}`);
58
+ }
59
+ writeToLog(`Successfully exported event to OTLP: ${event.id}`);
60
+ }
61
+ catch (error) {
62
+ throw new Error(`OTLP export error: ${error}`);
63
+ }
64
+ }
65
+ convertToOTLPSpan(event) {
66
+ const startTimeNanos = event.timestamp
67
+ ? BigInt(event.timestamp.getTime()) * BigInt(1_000_000)
68
+ : BigInt(Date.now()) * BigInt(1_000_000);
69
+ const endTimeNanos = event.duration
70
+ ? startTimeNanos + BigInt(event.duration) * BigInt(1_000_000)
71
+ : startTimeNanos;
72
+ return {
73
+ traceId: traceContext.getTraceId(event.sessionId),
74
+ spanId: traceContext.getSpanId(event.id),
75
+ name: event.eventType || "mcp.event",
76
+ kind: 2, // SPAN_KIND_SERVER
77
+ startTimeUnixNano: startTimeNanos.toString(),
78
+ endTimeUnixNano: endTimeNanos.toString(),
79
+ attributes: [
80
+ {
81
+ key: "source",
82
+ value: { stringValue: WITHIN_SOURCE },
83
+ },
84
+ {
85
+ key: "mcp.event_type",
86
+ value: { stringValue: event.eventType || "" },
87
+ },
88
+ {
89
+ key: "mcp.session_id",
90
+ value: { stringValue: event.sessionId || "" },
91
+ },
92
+ {
93
+ key: "mcp.vendor_slug",
94
+ value: { stringValue: event.vendorSlug || event.projectId || "" },
95
+ },
96
+ {
97
+ key: "mcp.resource_name",
98
+ value: { stringValue: event.resourceName || "" },
99
+ },
100
+ {
101
+ key: "mcp.user_intent",
102
+ value: { stringValue: event.userIntent || "" },
103
+ },
104
+ {
105
+ key: "mcp.actor_id",
106
+ value: { stringValue: event.identifyActorGivenId || "" },
107
+ },
108
+ {
109
+ key: "mcp.actor_name",
110
+ value: { stringValue: event.identifyActorName || "" },
111
+ },
112
+ {
113
+ key: "mcp.client_name",
114
+ value: { stringValue: event.clientName || "" },
115
+ },
116
+ {
117
+ key: "mcp.client_version",
118
+ value: { stringValue: event.clientVersion || "" },
119
+ },
120
+ // Add customer-defined tags as individual attributes
121
+ ...Object.entries(event.tags || {}).map(([key, value]) => ({
122
+ key: `within.tag.${key}`,
123
+ value: { stringValue: value },
124
+ })),
125
+ // Add customer-defined properties as JSON
126
+ ...(event.properties
127
+ ? [
128
+ {
129
+ key: "within.properties",
130
+ value: { stringValue: JSON.stringify(event.properties) },
131
+ },
132
+ ]
133
+ : []),
134
+ ].filter((attr) => attr.value.stringValue), // Remove empty attributes
135
+ status: {
136
+ code: event.isError ? 2 : 1, // ERROR : OK
137
+ },
138
+ };
139
+ }
140
+ }
@@ -0,0 +1,32 @@
1
+ import { Event, Exporter } from "../../types.js";
2
+ /**
3
+ * Generates a deterministic UUIDv7 from a prefixed KSUID (e.g. ses_xxx).
4
+ * Uses the KSUID's embedded timestamp for the UUIDv7 timestamp portion
5
+ * and a SHA-256 hash of the full ID for the random bits.
6
+ */
7
+ export declare function toUUIDv7(prefixedId: string): string;
8
+ export interface PostHogExporterConfig {
9
+ type: "posthog";
10
+ apiKey: string;
11
+ host?: string;
12
+ /**
13
+ * Emits `$ai_span` events for tool calls alongside regular capture events,
14
+ * integrating with PostHog's AI observability views. Each tool call is its own
15
+ * trace (`$ai_trace_id`), grouped into sessions via `$ai_session_id`.
16
+ * Customer-defined `eventTags` are spread directly onto `$ai_span` properties
17
+ * and can override any default, including reserved `$ai_*` fields.
18
+ * @default false
19
+ */
20
+ enableAITracing?: boolean;
21
+ }
22
+ export declare class PostHogExporter implements Exporter {
23
+ private batchUrl;
24
+ private apiKey;
25
+ private config;
26
+ constructor(config: PostHogExporterConfig);
27
+ export(event: Event): Promise<void>;
28
+ private buildCaptureEvent;
29
+ private buildExceptionEvent;
30
+ private buildAISpanEvent;
31
+ private mapEventType;
32
+ }
@@ -0,0 +1,272 @@
1
+ import { createHash } from "crypto";
2
+ import { writeToLog } from "../logging.js";
3
+ import { WITHIN_SOURCE, WithinEventType } from "../constants.js";
4
+ import KSUID from "../../thirdparty/ksuid/index.js";
5
+ /**
6
+ * Generates a deterministic UUIDv7 from a prefixed KSUID (e.g. ses_xxx).
7
+ * Uses the KSUID's embedded timestamp for the UUIDv7 timestamp portion
8
+ * and a SHA-256 hash of the full ID for the random bits.
9
+ */
10
+ export function toUUIDv7(prefixedId) {
11
+ // Strip prefix (ses_, evt_, etc.) and parse KSUID
12
+ const ksuidStr = prefixedId.replace(/^[a-z]+_/, "");
13
+ let timestampMs;
14
+ try {
15
+ const ksuid = KSUID.parse(ksuidStr);
16
+ timestampMs = ksuid.date.getTime();
17
+ }
18
+ catch {
19
+ // Fallback: if KSUID parsing fails, use current time
20
+ timestampMs = Date.now();
21
+ }
22
+ // Hash the full ID for deterministic random bits
23
+ const hash = createHash("sha256").update(prefixedId).digest();
24
+ const buf = Buffer.alloc(16);
25
+ // Bytes 0-5: 48-bit Unix timestamp in milliseconds
26
+ buf.writeUIntBE(timestampMs, 0, 6);
27
+ // Byte 6: version 7 (0111) + high 4 bits of rand_a from hash
28
+ buf[6] = 0x70 | (hash[0] & 0x0f);
29
+ // Byte 7: low 8 bits of rand_a from hash
30
+ buf[7] = hash[1];
31
+ // Byte 8: variant 10 + high 6 bits of rand_b from hash
32
+ buf[8] = 0x80 | (hash[2] & 0x3f);
33
+ // Bytes 9-15: remaining rand_b from hash
34
+ buf[9] = hash[3];
35
+ buf[10] = hash[4];
36
+ buf[11] = hash[5];
37
+ buf[12] = hash[6];
38
+ buf[13] = hash[7];
39
+ buf[14] = hash[8];
40
+ buf[15] = hash[9];
41
+ const hex = buf.toString("hex");
42
+ return [
43
+ hex.substring(0, 8),
44
+ hex.substring(8, 12),
45
+ hex.substring(12, 16),
46
+ hex.substring(16, 20),
47
+ hex.substring(20, 32),
48
+ ].join("-");
49
+ }
50
+ function getDistinctId(event) {
51
+ return event.identifyActorGivenId || event.sessionId || "anonymous";
52
+ }
53
+ function getTimestamp(event) {
54
+ return event.timestamp
55
+ ? event.timestamp.toISOString()
56
+ : new Date().toISOString();
57
+ }
58
+ export class PostHogExporter {
59
+ batchUrl;
60
+ apiKey;
61
+ config;
62
+ constructor(config) {
63
+ this.config = config;
64
+ const host = (config.host || "https://us.i.posthog.com").replace(/\/$/, "");
65
+ this.batchUrl = `${host}/batch`;
66
+ this.apiKey = config.apiKey;
67
+ writeToLog(`PostHogExporter: Initialized with endpoint ${this.batchUrl}`);
68
+ }
69
+ async export(event) {
70
+ try {
71
+ const batch = [];
72
+ // Always send the regular event
73
+ batch.push(this.buildCaptureEvent(event));
74
+ // Send $exception event alongside if this is an error
75
+ if (event.isError && event.error) {
76
+ batch.push(this.buildExceptionEvent(event));
77
+ }
78
+ // Send $ai_span for tool calls when AI tracing is enabled
79
+ if (this.config.enableAITracing &&
80
+ event.eventType === WithinEventType.mcpToolsCall) {
81
+ batch.push(this.buildAISpanEvent(event));
82
+ }
83
+ writeToLog(`PostHogExporter: Sending ${batch.length} event(s) for ${event.id}`);
84
+ const response = await fetch(this.batchUrl, {
85
+ method: "POST",
86
+ headers: {
87
+ "Content-Type": "application/json",
88
+ },
89
+ body: JSON.stringify({
90
+ api_key: this.apiKey,
91
+ batch,
92
+ }),
93
+ });
94
+ if (!response.ok) {
95
+ const errorBody = await response.text();
96
+ writeToLog(`PostHog export failed - Status: ${response.status}, Body: ${errorBody}`);
97
+ }
98
+ else {
99
+ writeToLog(`PostHog export success - Event: ${event.id}`);
100
+ }
101
+ }
102
+ catch (error) {
103
+ writeToLog(`PostHog export error: ${error}`);
104
+ }
105
+ }
106
+ buildCaptureEvent(event) {
107
+ const distinctId = getDistinctId(event);
108
+ const eventName = this.mapEventType(event.eventType);
109
+ const timestamp = getTimestamp(event);
110
+ const properties = {
111
+ $session_id: toUUIDv7(event.sessionId),
112
+ source: WITHIN_SOURCE,
113
+ };
114
+ if (event.resourceName) {
115
+ properties.resource_name = event.resourceName;
116
+ if (event.eventType === WithinEventType.mcpToolsCall) {
117
+ properties.tool_name = event.resourceName;
118
+ }
119
+ }
120
+ if (event.duration !== undefined) {
121
+ properties.duration_ms = event.duration;
122
+ }
123
+ if (event.serverName)
124
+ properties.server_name = event.serverName;
125
+ if (event.serverVersion)
126
+ properties.server_version = event.serverVersion;
127
+ if (event.clientName)
128
+ properties.client_name = event.clientName;
129
+ if (event.clientVersion)
130
+ properties.client_version = event.clientVersion;
131
+ if (event.projectId)
132
+ properties.project_id = event.projectId;
133
+ if (event.userIntent)
134
+ properties.user_intent = event.userIntent;
135
+ if (event.isError !== undefined)
136
+ properties.is_error = event.isError;
137
+ if (event.parameters !== undefined) {
138
+ properties.parameters = event.parameters;
139
+ }
140
+ if (event.response !== undefined) {
141
+ properties.response = event.response;
142
+ }
143
+ // Set person properties from identity data
144
+ const $set = {};
145
+ if (event.identifyActorName)
146
+ $set.name = event.identifyActorName;
147
+ if (event.identifyActorData) {
148
+ Object.assign($set, event.identifyActorData);
149
+ }
150
+ if (Object.keys($set).length > 0) {
151
+ properties.$set = $set;
152
+ }
153
+ // Spread customer-defined tags directly (can override Within defaults)
154
+ if (event.tags) {
155
+ for (const [key, value] of Object.entries(event.tags)) {
156
+ properties[key] = value;
157
+ }
158
+ }
159
+ // Spread customer-defined properties directly (can override Within defaults)
160
+ if (event.properties) {
161
+ for (const [key, value] of Object.entries(event.properties)) {
162
+ properties[key] = value;
163
+ }
164
+ }
165
+ return {
166
+ event: eventName,
167
+ distinct_id: distinctId,
168
+ properties,
169
+ timestamp,
170
+ type: "capture",
171
+ };
172
+ }
173
+ buildExceptionEvent(event) {
174
+ const distinctId = getDistinctId(event);
175
+ const timestamp = getTimestamp(event);
176
+ const properties = {
177
+ $exception_source: "backend",
178
+ $session_id: toUUIDv7(event.sessionId),
179
+ };
180
+ if (event.error) {
181
+ if (event.error.message) {
182
+ properties.$exception_message = event.error.message;
183
+ }
184
+ if (event.error.type) {
185
+ properties.$exception_type = event.error.type;
186
+ }
187
+ if (event.error.stack) {
188
+ properties.$exception_stacktrace = event.error.stack;
189
+ }
190
+ }
191
+ // Add tool/resource context
192
+ if (event.resourceName) {
193
+ properties.resource_name = event.resourceName;
194
+ if (event.eventType === WithinEventType.mcpToolsCall) {
195
+ properties.tool_name = event.resourceName;
196
+ }
197
+ }
198
+ if (event.serverName)
199
+ properties.server_name = event.serverName;
200
+ if (event.serverVersion)
201
+ properties.server_version = event.serverVersion;
202
+ if (event.clientName)
203
+ properties.client_name = event.clientName;
204
+ if (event.clientVersion)
205
+ properties.client_version = event.clientVersion;
206
+ return {
207
+ event: "$exception",
208
+ distinct_id: distinctId,
209
+ properties,
210
+ timestamp,
211
+ type: "capture",
212
+ };
213
+ }
214
+ buildAISpanEvent(event) {
215
+ const distinctId = getDistinctId(event);
216
+ const timestamp = getTimestamp(event);
217
+ const properties = {
218
+ $ai_session_id: `within_${event.sessionId}`,
219
+ $ai_trace_id: toUUIDv7(event.sessionId),
220
+ $ai_span_id: toUUIDv7(event.id),
221
+ $ai_span_name: event.resourceName || "unknown_tool",
222
+ $ai_is_error: event.isError || false,
223
+ $session_id: toUUIDv7(event.sessionId),
224
+ source: WITHIN_SOURCE,
225
+ };
226
+ if (event.duration !== undefined) {
227
+ properties.$ai_latency = event.duration / 1000;
228
+ }
229
+ if (event.isError && event.error) {
230
+ properties.$ai_error = event.error;
231
+ }
232
+ if (event.parameters !== undefined) {
233
+ properties.$ai_input_state = event.parameters;
234
+ }
235
+ if (event.response !== undefined) {
236
+ properties.$ai_output_state = event.response;
237
+ }
238
+ if (event.serverName)
239
+ properties.server_name = event.serverName;
240
+ if (event.clientName)
241
+ properties.client_name = event.clientName;
242
+ // Spread customer tags directly (can override Within defaults)
243
+ if (event.tags) {
244
+ for (const [key, value] of Object.entries(event.tags)) {
245
+ properties[key] = value;
246
+ }
247
+ }
248
+ // Spread customer properties directly (can override Within defaults)
249
+ if (event.properties) {
250
+ for (const [key, value] of Object.entries(event.properties)) {
251
+ properties[key] = value;
252
+ }
253
+ }
254
+ return {
255
+ event: "$ai_span",
256
+ distinct_id: distinctId,
257
+ properties,
258
+ timestamp,
259
+ type: "capture",
260
+ };
261
+ }
262
+ mapEventType(eventType) {
263
+ // Map Within event types to PostHog event names
264
+ const mapping = {
265
+ [WithinEventType.mcpToolsCall]: "mcp_tool_call",
266
+ [WithinEventType.mcpToolsList]: "mcp_tools_list",
267
+ [WithinEventType.mcpInitialize]: "mcp_initialize",
268
+ };
269
+ return (mapping[eventType] ||
270
+ `mcp_${eventType.replace(/^mcp:/, "").replace(/\//g, "_")}`);
271
+ }
272
+ }
@@ -0,0 +1,27 @@
1
+ import { Event, Exporter } from "../../types.js";
2
+ export interface SentryExporterConfig {
3
+ type: "sentry";
4
+ dsn: string;
5
+ environment?: string;
6
+ release?: string;
7
+ enableTracing?: boolean;
8
+ }
9
+ export declare class SentryExporter implements Exporter {
10
+ private endpoint;
11
+ private authHeader;
12
+ private config;
13
+ private parsedDSN;
14
+ constructor(config: SentryExporterConfig);
15
+ private parseDSN;
16
+ export(event: Event): Promise<void>;
17
+ private eventToLog;
18
+ private buildLogAttributes;
19
+ private createLogEnvelope;
20
+ private eventToTransaction;
21
+ private buildTags;
22
+ private buildExtra;
23
+ private buildContexts;
24
+ private eventToErrorEvent;
25
+ private createTransactionEnvelope;
26
+ private createErrorEnvelope;
27
+ }