Skip to content

Commit b30cdbc

Browse files
authored
feat: add telemetry exporters for observability (#2)
* feat: add telemetry exporters for observability - Add TelemetryManager to coordinate multiple exporters - Implement OTLP exporter for OpenTelemetry compatibility - Implement Datadog exporter with StatsD metrics and APM tracing - Implement Sentry exporter for error tracking and performance monitoring - Support telemetry-only mode with null projectId - Enable dual mode to send events to both MCPCat and telemetry exporters - Update eventQueue to support telemetry manager integration
1 parent 004604c commit b30cdbc

File tree

11 files changed

+1365
-48
lines changed

11 files changed

+1365
-48
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ import * as mcpcat from "mcpcat";
5858

5959
const mcpServer = new Server({ name: "echo-mcp", version: "0.1.0" });
6060

61-
// Register tools
62-
63-
// NOTE: track() must be called *after* tools are setup
61+
// Track the server with MCPCat
6462
mcpcat.track(mcpServer, "proj_0000000");
63+
64+
// Register your tools
6565
```
6666

6767
### Identifying users

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"@modelcontextprotocol/sdk": ">=1.3.1"
7373
},
7474
"dependencies": {
75+
"@opentelemetry/otlp-transformer": "^0.203.0",
7576
"mcpcat-api": "0.1.3",
7677
"redact-pii": "3.4.0",
7778
"zod": "3.25.30"

pnpm-lock.yaml

Lines changed: 132 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,28 @@ import { setupToolCallTracing } from "./modules/tracing.js";
1818
import { getSessionInfo, newSessionId } from "./modules/session.js";
1919
import { setServerTrackingData } from "./modules/internal.js";
2020
import { setupTracking } from "./modules/tracingV2.js";
21+
import { TelemetryManager } from "./modules/telemetry.js";
22+
import { setTelemetryManager } from "./modules/eventQueue.js";
2123

2224
/**
2325
* Integrates MCPCat analytics into an MCP server to track tool usage patterns and user interactions.
2426
*
2527
* @param server - The MCP server instance to track. Must be a compatible MCP server implementation.
26-
* @param projectId - Your MCPCat project ID obtained from mcpcat.io when creating an account.
28+
* @param projectId - Your MCPCat project ID obtained from mcpcat.io when creating an account. Pass null for telemetry-only mode.
2729
* @param options - Optional configuration to customize tracking behavior.
2830
* @param options.enableReportMissing - Adds a "get_more_tools" tool that allows LLMs to automatically report missing functionality.
2931
* @param options.enableTracing - Enables tracking of tool calls and usage patterns.
3032
* @param options.enableToolCallContext - Injects a "context" parameter to existing tools to capture user intent.
3133
* @param options.identify - Async function to identify users and attach custom data to their sessions.
3234
* @param options.redactSensitiveInformation - Function to redact sensitive data before sending to MCPCat.
35+
* @param options.exporters - Configure telemetry exporters to send events to external systems. Available exporters:
36+
* - `otlp`: OpenTelemetry Protocol exporter (see {@link ../modules/exporters/otlp.OTLPExporter})
37+
* - `datadog`: Datadog APM exporter (see {@link ../modules/exporters/datadog.DatadogExporter})
38+
* - `sentry`: Sentry Monitoring exporter (see {@link ../modules/exporters/sentry.SentryExporter})
3339
*
3440
* @returns The tracked server instance.
3541
*
3642
* @remarks
37-
* **IMPORTANT**: The `track()` function must be called AFTER all tools have been registered on the server.
38-
* Calling it before tool registration will result in those tools not being tracked.
39-
*
4043
* Analytics data and debug information are logged to `~/mcpcat.log` since console logs interfere
4144
* with STDIO-based MCP servers.
4245
*
@@ -48,13 +51,13 @@ import { setupTracking } from "./modules/tracingV2.js";
4851
*
4952
* const mcpServer = new Server({ name: "my-mcp-server", version: "1.0.0" });
5053
*
51-
* // Register your tools first
54+
* // Track the server with MCPCat
55+
* mcpcat.track(mcpServer, "proj_abc123xyz");
56+
*
57+
* // Register your tools
5258
* mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
5359
* tools: [{ name: "my_tool", description: "Does something useful" }]
5460
* }));
55-
*
56-
* // Then call track() after all tools are registered
57-
* mcpcat.track(mcpServer, "proj_abc123xyz");
5861
* ```
5962
*
6063
* @example
@@ -80,23 +83,68 @@ import { setupTracking } from "./modules/tracingV2.js";
8083
* }
8184
* });
8285
* ```
86+
*
87+
* @example
88+
* ```typescript
89+
* // Telemetry-only mode (no MCPCat account required)
90+
* mcpcat.track(mcpServer, null, {
91+
* exporters: {
92+
* otlp: {
93+
* type: "otlp",
94+
* endpoint: "http://localhost:4318/v1/traces"
95+
* }
96+
* }
97+
* });
98+
* ```
99+
*
100+
* @example
101+
* ```typescript
102+
* // Dual mode - send to both MCPCat and telemetry exporters
103+
* mcpcat.track(mcpServer, "proj_abc123xyz", {
104+
* exporters: {
105+
* datadog: {
106+
* type: "datadog",
107+
* apiKey: process.env.DD_API_KEY,
108+
* site: "datadoghq.com"
109+
* }
110+
* }
111+
* });
112+
* ```
83113
*/
84114
function track(
85115
server: any,
86-
projectId: string,
116+
projectId: string | null,
87117
options: MCPCatOptions = {},
88118
): any {
89119
try {
90120
const validatedServer = isCompatibleServerType(server);
121+
91122
// For high-level servers, we need to pass the underlying server to some functions
92123
const lowLevelServer = (
93124
isHighLevelServer(validatedServer)
94125
? (validatedServer as any).server
95126
: validatedServer
96127
) as MCPServerLike;
128+
129+
// Initialize telemetry if exporters are configured
130+
if (options.exporters) {
131+
const telemetryManager = new TelemetryManager(options.exporters);
132+
setTelemetryManager(telemetryManager);
133+
writeToLog(
134+
`Initialized telemetry with ${Object.keys(options.exporters).length} exporters`,
135+
);
136+
}
137+
138+
// If projectId is null and no exporters, warn the user
139+
if (!projectId && !options.exporters) {
140+
writeToLog(
141+
"Warning: No projectId provided and no exporters configured. Events will not be sent anywhere.",
142+
);
143+
}
144+
97145
const sessionInfo = getSessionInfo(lowLevelServer, undefined);
98146
const mcpcatData: MCPCatData = {
99-
projectId,
147+
projectId: projectId || "", // Use empty string for null projectId
100148
sessionId: newSessionId(),
101149
lastActivity: new Date(),
102150
identifiedSessions: new Map<string, UserIdentity>(),
@@ -140,7 +188,13 @@ function track(
140188
}
141189
}
142190

143-
export type { MCPCatOptions, UserIdentity, RedactFunction } from "./types.js";
191+
export type {
192+
MCPCatOptions,
193+
UserIdentity,
194+
RedactFunction,
195+
ExporterConfig,
196+
Exporter,
197+
} from "./types.js";
144198

145199
export type IdentifyFunction = MCPCatOptions["identify"];
146200

0 commit comments

Comments
 (0)