@awarecorp/mcp-logger 0.0.2-dev.5 → 0.0.2

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/README.md CHANGED
@@ -1,421 +1,224 @@
1
1
  # @awarecorp/mcp-logger
2
2
 
3
- **Unified MCP Observability Solution** - One package for both SDK and CLI.
3
+ **Unified MCP Observability Solution** - Monitor your Model Context Protocol servers with zero configuration.
4
4
 
5
- [한국어 문서](./docs/README.ko.md)
5
+ [![npm version](https://img.shields.io/npm/v/@awarecorp/mcp-logger)](https://www.npmjs.com/package/@awarecorp/mcp-logger)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ [Aware Corp](https://awarecorp.io/)
6
9
 
7
10
  ---
8
11
 
9
- ## 🎯 Two Ways to Use
12
+ ## 🎯 Overview
13
+
14
+ `@awarecorp/mcp-logger` provides comprehensive observability for MCP servers in two ways:
10
15
 
11
- ### 1️⃣ **SDK**: Direct Code Integration
12
- If you're developing an MCP server, use the SDK.
16
+ - **SDK**: Direct integration for custom MCP servers
17
+ - **CLI**: Zero-code wrapper for existing MCP packages
13
18
 
14
- ### 2️⃣ **CLI**: Zero-Code Wrapper
15
- If you're using an existing MCP server (npm package), use the CLI.
19
+ All telemetry data is sent to OpenTelemetry-compatible endpoints with automatic request-response pairing.
16
20
 
17
21
  ---
18
22
 
19
23
  ## 📦 Installation
20
24
 
21
- \`\`\`bash
25
+ ```bash
22
26
  npm install @awarecorp/mcp-logger
23
- \`\`\`
27
+ ```
24
28
 
25
29
  ---
26
30
 
27
- ## 1️⃣ SDK Usage
31
+ ## 🚀 Quick Start
28
32
 
29
- ### TypeScript
33
+ ### SDK Mode
30
34
 
31
- \`\`\`typescript
32
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
33
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
34
- import { trace } from '@awarecorp/mcp-logger';
35
+ Instrument your MCP server with one line of code:
35
36
 
36
- const server = new Server(
37
- { name: 'my-server', version: '1.0.0' },
38
- { capabilities: { tools: {} } }
39
- );
37
+ ```typescript
38
+ import { trace } from '@awarecorp/mcp-logger';
40
39
 
41
- // 🔥 Enable auto-instrumentation (just add this!)
42
40
  trace(server, {
43
- apiKey: process.env.AWARE_API_KEY!,
44
- serviceName: 'my-awesome-mcp-server',
45
- debug: true, // Enable debug logs (optional)
46
- });
47
-
48
- // Register handlers as usual
49
- server.setRequestHandler('tools/list', async () => ({
50
- tools: [
51
- { name: 'read_file', description: 'Read a file' },
52
- { name: 'write_file', description: 'Write to a file' },
53
- ],
54
- }));
55
-
56
- server.setRequestHandler('tools/call', async (request) => {
57
- const { name, arguments: args } = request.params;
58
-
59
- // ✨ Add custom logs (optional)
60
- trace.addLog({ level: 'info', message: \`Processing \${name}\` });
61
-
62
- // Business logic
63
- const result = await processToolCall(name, args);
64
-
65
- trace.addLog({ level: 'info', message: 'Processing completed' });
66
-
67
- return { result };
41
+ apiKey: 'YOUR_API_KEY',
42
+ serviceName: 'my-mcp-server'
68
43
  });
44
+ ```
69
45
 
70
- // Start stdio transport
71
- const transport = new StdioServerTransport();
72
- await server.connect(transport);
73
- \`\`\`
46
+ ### CLI Mode
74
47
 
75
- ### JavaScript (CommonJS)
48
+ Wrap any existing MCP server without code changes:
76
49
 
77
- \`\`\`javascript
78
- const { Server } = require('@modelcontextprotocol/sdk/server/index.js');
79
- const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
80
- const { trace } = require('@awarecorp/mcp-logger');
50
+ ```bash
51
+ npx @awarecorp/mcp-logger \
52
+ -k YOUR_API_KEY \
53
+ -s my-server \
54
+ npx your-mcp-server
55
+ ```
81
56
 
82
- const server = new Server(
83
- { name: 'my-server', version: '1.0.0' },
84
- { capabilities: { tools: {} } }
85
- );
86
-
87
- // 🔥 Enable auto-instrumentation
88
- trace(server, {
89
- apiKey: process.env.AWARE_API_KEY,
90
- serviceName: 'my-mcp-server',
91
- });
57
+ ---
92
58
 
93
- server.setRequestHandler('tools/list', async () => ({
94
- tools: [{ name: 'test', description: 'Test tool' }],
95
- }));
59
+ ## 📋 Features
96
60
 
97
- server.setRequestHandler('tools/call', async (request) => {
98
- // Add custom logs
99
- trace.addLog({ level: 'info', message: 'Tool called' });
100
-
101
- return { result: 'success' };
102
- });
61
+ ### Core Capabilities
103
62
 
104
- const transport = new StdioServerTransport();
105
- server.connect(transport);
106
- \`\`\`
63
+ - **Automatic Request-Response Pairing**: Single span per request-response cycle
64
+ - **Selective Method Tracking**: Only tracks `tools/call` and `tools/list` for efficiency
65
+ - **Custom Event Logging**: Add context-aware logs within request handlers
66
+ - **Transport Support**: stdio and SSE (Server-Sent Events)
67
+ - **Zero Configuration**: Works out of the box with sensible defaults
107
68
 
108
- ### SDK API
69
+ ### Telemetry Data
109
70
 
110
- #### \`trace(server, options)\`
71
+ All spans include:
111
72
 
112
- Add auto-instrumentation to your MCP server.
73
+ - Request/response timestamps and payloads
74
+ - Method name and parameters
75
+ - Tool names and arguments (for `tools/call`)
76
+ - Custom events and logs
77
+ - Error details (if any)
78
+ - Duration metrics
79
+ - CLI server information (command, args, env)
113
80
 
114
- **Parameters:**
115
- - \`server\` (\`MCPServer\`): MCP Server instance
116
- - \`options\` (\`TraceOptions\`):
117
- - \`apiKey\` (string, required): Aware API key
118
- - \`serviceName\` (string, optional): Service name (default: auto-generated)
119
- - \`debug\` (boolean, optional): Enable debug logs (default: false)
120
- - \`endpoint\` (string, optional): Custom OTLP endpoint
81
+ ### Data Format
121
82
 
122
- **Returns:** \`void\`
83
+ Optimized for Elasticsearch with flat structure:
84
+ - `mcp.method`: Method name
85
+ - `mcp.source`: `sdk` or `cli`
86
+ - `mcp.transport`: `stdio` or `sse`
87
+ - `mcp.duration_ms`: Request duration
88
+ - `mcp.tool.name`: Tool name (tools/call only)
89
+ - `mcp.events`: Custom log entries (JSON array)
90
+ - `mcp.cli.server`: CLI execution info (JSON object)
123
91
 
124
92
  ---
125
93
 
126
- #### \`trace.addLog(entry)\`
94
+ ## 🔧 SDK API
95
+
96
+ ### `trace(server, options)`
127
97
 
128
- Add custom logs to the currently executing tool handler.
98
+ Enable automatic instrumentation on an MCP server.
129
99
 
130
100
  **Parameters:**
131
- - \`entry\` (\`CustomLogEntry\`):
132
- - \`level\` ('info' | 'warn' | 'error', optional): Log level (default: 'info')
133
- - \`message\` (string, required): Log message
134
- - \`metadata\` (object, optional): Additional metadata
135
-
136
- **Returns:** \`void\`
137
-
138
- **Example:**
139
- \`\`\`typescript
140
- server.setRequestHandler('tools/call', async (request) => {
141
- trace.addLog({
142
- level: 'info',
143
- message: 'Processing started',
144
- metadata: { userId: '123', action: 'read' }
145
- });
146
-
147
- // Business logic...
148
-
149
- trace.addLog({ level: 'info', message: 'Processing completed' });
150
-
151
- return { result: 'success' };
152
- });
153
- \`\`\`
101
+ - `server`: MCP Server instance
102
+ - `options.apiKey`: API key for authentication (required)
103
+ - `options.serviceName`: Service identifier (optional, auto-generated if omitted)
104
+ - `options.endpoint`: Custom OTLP endpoint (optional)
105
+ - `options.debug`: Enable debug logging (optional, default: false)
154
106
 
155
- ---
107
+ **Returns:** `void`
156
108
 
157
- #### \`trace.shutdown()\`
109
+ ### `trace.addLog(entry)`
158
110
 
159
- Shutdown telemetry and flush remaining data.
111
+ Add custom log entries within request handlers.
160
112
 
161
- **Returns:** \`Promise<void>\`
113
+ **Parameters:**
114
+ - `entry.level`: Log level (`info` | `warn` | `error`)
115
+ - `entry.message`: Log message (required)
116
+ - `entry.metadata`: Additional data (optional)
117
+ - `entry.timestamp`: Unix timestamp in milliseconds (optional, auto-generated)
162
118
 
163
- **Example:**
164
- \`\`\`typescript
165
- process.on('SIGTERM', async () => {
166
- await trace.shutdown();
167
- process.exit(0);
168
- });
169
- \`\`\`
119
+ **Returns:** `void`
170
120
 
171
- ---
121
+ ### `trace.shutdown()`
172
122
 
173
- ## 2️⃣ CLI Usage
123
+ Gracefully shutdown telemetry and flush pending data.
174
124
 
175
- ### Quick Start
125
+ **Returns:** `Promise<void>`
176
126
 
177
- \`\`\`bash
178
- npx @awarecorp/mcp-logger \\
179
- --api-key YOUR_API_KEY \\
180
- -- \\
181
- npx @modelcontextprotocol/server-filesystem /path/to/dir
182
- \`\`\`
127
+ ---
183
128
 
184
- Just add your MCP server command after \`--\`.
129
+ ## 🖥️ CLI Usage
185
130
 
186
- ### Global Installation (Optional)
131
+ ### Basic Syntax
187
132
 
188
- \`\`\`bash
189
- npm install -g @awarecorp/mcp-logger
190
- \`\`\`
133
+ ```bash
134
+ mcp-logger [options] <command...>
135
+ ```
191
136
 
192
- Usage:
193
- \`\`\`bash
194
- mcp-logger --api-key YOUR_KEY -- npx server-filesystem /path
195
- \`\`\`
137
+ ### Options
196
138
 
197
- ### Claude Desktop Configuration
139
+ - `-k, --api-key <key>`: API key (required)
140
+ - `-s, --service-name <name>`: Service name (optional)
141
+ - `-e, --endpoint <url>`: Custom OTLP endpoint (optional)
198
142
 
199
- #### Before (No Observability)
200
- \`\`\`json
201
- {
202
- "mcpServers": {
203
- "filesystem": {
204
- "command": "npx",
205
- "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/Documents"]
206
- }
207
- }
208
- }
209
- \`\`\`
143
+ ### Examples
210
144
 
211
- #### After (With Observability) ✨
212
- \`\`\`json
145
+ **Wrap an npx command:**
146
+ ```bash
147
+ mcp-logger -k API_KEY -s filesystem npx -y @modelcontextprotocol/server-filesystem /path
148
+ ```
149
+
150
+ **Use with Claude Desktop:**
151
+ ```json
213
152
  {
214
153
  "mcpServers": {
215
- "filesystem": {
154
+ "my-server": {
216
155
  "command": "npx",
217
156
  "args": [
218
157
  "-y",
219
158
  "@awarecorp/mcp-logger",
220
- "--api-key", "your-aware-api-key",
221
- "--service-name", "my-filesystem-server",
159
+ "-k",
160
+ "YOUR_API_KEY",
161
+ "-s",
162
+ "my-server",
222
163
  "--",
223
- "@modelcontextprotocol/server-filesystem",
224
- "/Users/username/Documents"
164
+ "npx",
165
+ "-y",
166
+ "your-mcp-server"
225
167
  ]
226
168
  }
227
169
  }
228
170
  }
229
- \`\`\`
230
-
231
- **That's it!** All requests/responses are now logged to the Aware platform without code changes.
232
-
233
- ### CLI Options
234
-
235
- #### Required Options
236
- - \`--api-key <key>\` or \`-k <key>\`: Aware API key (required)
237
-
238
- #### Optional Options
239
- - \`--service-name <name>\` or \`-s <name>\`: Service identifier name (default: auto-generated)
240
- - \`--debug\` or \`-d\`: Enable debug logs
241
- - \`--endpoint <url>\` or \`-e <url>\`: Custom OTLP endpoint
242
-
243
- ### CLI Usage Examples
244
-
245
- #### Filesystem Server
246
- \`\`\`bash
247
- npx @awarecorp/mcp-logger \\
248
- --api-key abc123 \\
249
- --service-name my-filesystem \\
250
- -- \\
251
- npx -y @modelcontextprotocol/server-filesystem /Users/username/Documents
252
- \`\`\`
253
-
254
- #### GitHub Server
255
- \`\`\`bash
256
- npx @awarecorp/mcp-logger \\
257
- -k abc123 \\
258
- -s github-integration \\
259
- -- \\
260
- npx -y @modelcontextprotocol/server-github
261
- \`\`\`
262
-
263
- #### Brave Search Server (Debug Mode)
264
- \`\`\`bash
265
- npx @awarecorp/mcp-logger \\
266
- --api-key abc123 \\
267
- --service-name brave-search \\
268
- --debug \\
269
- -- \\
270
- npx -y @modelcontextprotocol/server-brave-search
271
- \`\`\`
171
+ ```
272
172
 
273
173
  ---
274
174
 
275
- ## 🏗️ Architecture
276
-
277
- This package consists of three layers:
278
-
279
- \`\`\`
280
- @awarecorp/mcp-logger
281
- ├── SDK (Public API)
282
- │ ├── trace() - Instrument MCP server
283
- │ ├── trace.addLog() - Add custom logs
284
- │ └── trace.shutdown() - Shutdown telemetry
285
-
286
- ├── CLI (Binary-only access)
287
- │ ├── main.ts - CLI entry point
288
- │ ├── interceptor.ts - Stream interception
289
- │ └── pairing.ts - Request-Response matching
290
-
291
- └── Core (Internal implementation)
292
- ├── telemetry - OpenTelemetry initialization
293
- ├── span-builder - Unified Span creation
294
- ├── span-utils - Span utilities
295
- ├── config - Tracked method configuration
296
- └── types - Common type definitions
297
- \`\`\`
298
-
299
- ### Key Features
300
-
301
- #### ✅ Request-Response Pairing
302
- - Matches requests and responses by ID into a **single span**
303
- - Automatic cleanup of incomplete requests with 30s timeout
304
-
305
- #### ✅ Selective Tracking
306
- - Only tracks \`tools/call\` and \`tools/list\` (removes unnecessary overhead)
307
- - Configurable to extend tracking targets
308
-
309
- #### ✅ Custom Events
310
- - Add custom events inside handlers with \`trace.addLog()\`
311
- - Automatically linked to the currently executing span via context
312
-
313
- #### ✅ Elasticsearch Optimization
314
- - Extracts tool name and arguments as top-level fields
315
- - Fast searching with \`mcp.tool.name\`, \`mcp.tool.arguments\`, etc.
316
-
317
- ### Design Principles
318
-
319
- 1. **SDK-only public API**: Only SDK accessible via \`import\`
320
- 2. **CLI binary-only access**: Use via \`npx\` or \`mcp-logger\` command only
321
- 3. **Shared code reuse**: OpenTelemetry logic unified in core layer
322
- 4. **Type safety**: CLI type definitions not exposed
323
- 5. **dd-trace style API**: Intuitive usage with \`trace()\` namespace
175
+ ## 🏗️ How It Works
324
176
 
325
- ---
177
+ ### Request-Response Pairing
326
178
 
327
- ## 🔧 Development
179
+ Creates **one span per request-response pair** for complete transaction context:
328
180
 
329
- ### Build
181
+ 1. Request arrives → stored in pending map
182
+ 2. Response arrives → matched by ID, single span created
183
+ 3. Timeout (30s) → pending requests cleared
330
184
 
331
- \`\`\`bash
332
- npm run build
333
- \`\`\`
185
+ ### Tracked Methods
334
186
 
335
- ### Tests
187
+ Only tracks business-critical methods:
188
+ - `tools/call` - Tool executions
189
+ - `tools/list` - Tool discovery
336
190
 
337
- \`\`\`bash
338
- npm test # Run all tests
339
- npm run test:watch # Watch mode
340
- \`\`\`
341
-
342
- ### Local Testing
343
-
344
- #### SDK Testing
345
- \`\`\`typescript
346
- // test.ts
347
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
348
- import { trace } from './src/index.js';
349
-
350
- const server = new Server({ name: 'test', version: '1.0.0' }, { capabilities: {} });
351
- trace(server, { apiKey: 'test', debug: true });
352
-
353
- server.setRequestHandler('tools/call', async (request) => {
354
- trace.addLog({ level: 'info', message: 'Test handler called' });
355
- return { result: 'success' };
356
- });
357
- \`\`\`
358
-
359
- #### CLI Testing
360
- \`\`\`bash
361
- npm run build
362
- node bin/mcp-logger.js --help
363
- \`\`\`
191
+ Protocol methods (`initialize`, `ping`, etc.) are ignored.
364
192
 
365
193
  ---
366
194
 
367
- ## 📝 License
195
+ ## 📊 Telemetry Data Structure
368
196
 
369
- MIT
197
+ Each span includes:
198
+ - Request/response timestamps and payloads
199
+ - Method name and parameters
200
+ - Tool names and arguments (for `tools/call`)
201
+ - Custom events and logs
202
+ - Error details and duration metrics
203
+ - CLI server information (command, args, env)
370
204
 
371
205
  ---
372
206
 
373
207
  ## 🤝 Contributing
374
208
 
375
- Issues and PRs are always welcome!
376
-
377
- See [DEVELOPMENT.md](./DEVELOPMENT.md) for detailed development guidelines.
209
+ For development setup and guidelines, visit [GitHub Repository](https://github.com/awarecorp/mcp-logger).
378
210
 
379
211
  ---
380
212
 
381
- ## 📦 Publishing (Maintainers Only)
382
-
383
- **Recommended:** Automated deployment with GitHub Actions
384
-
385
- ### Automated Deployment (GitHub Actions) ⭐
386
- \`\`\`bash
387
- # Dev version (develop branch)
388
- git checkout develop
389
- npm run version:dev
390
- git push origin develop --tags
391
-
392
- # Production version (main branch)
393
- git checkout main
394
- npm run version:patch # or minor, major
395
- git push origin main --tags
396
- \`\`\`
213
+ ## 📄 License
397
214
 
398
- GitHub Actions will automatically build and publish to npm.
399
-
400
- ### Manual Deployment (Not Recommended)
401
- \`\`\`bash
402
- # Dev version
403
- npm run build
404
- npm publish --access public --tag dev
405
-
406
- # Production version
407
- npm run build
408
- npm publish --access public
409
- \`\`\`
215
+ MIT © [Aware Corp](https://awarecorp.io/)
410
216
 
411
217
  ---
412
218
 
413
- ## 🌐 Links
414
-
415
- - **Aware Platform**: https://aware.mcypher.com
416
- - **MCP Specification**: https://modelcontextprotocol.io
417
- - **GitHub Repository**: https://github.com/awarecorp/mcp-logger
418
-
419
- ---
219
+ ## 🔗 Links
420
220
 
421
- Made with ❤️ by [Aware](https://aware.mcypher.com)
221
+ - [Aware Corp](https://awarecorp.io/)
222
+ - [npm Package](https://www.npmjs.com/package/@awarecorp/mcp-logger)
223
+ - [GitHub Repository](https://github.com/awarecorp/mcp-logger)
224
+ - [Model Context Protocol](https://modelcontextprotocol.io/)
@@ -1 +1 @@
1
- import{Transform as e}from"stream";class s extends e{buffer="";direction;pairer;debug;constructor(e,s,r=!1){super(),this.direction=e,this.pairer=s,this.debug=r}_transform(e,s,r){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),r()}catch(e){r(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const s of e){const e=s.trim();if(e)try{const s=JSON.parse(e);this.handleMessage(s)}catch(s){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){"request"===this.direction&&e.method?this.pairer.onRequest(e):"response"!==this.direction||void 0===e.id||e.method||this.pairer.onResponse(e)}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.pairer.getStats();console.error(`[mcp-logger CLI] Stream closed. Pending requests: ${e.pending}`)}e()}catch(s){e(s)}}}export{s as MessageInterceptor};
1
+ import{Transform as e}from"stream";class s extends e{buffer="";direction;pairer;debug;constructor(e,s,r=!1){super(),this.direction=e,this.pairer=s,this.debug=r}_transform(e,s,r){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),r()}catch(e){r(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const s of e){const e=s.trim();if(e)try{const s=JSON.parse(e);this.handleMessage(s)}catch(s){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){"request"===this.direction&&e.method?this.pairer.onRequest(e):"response"!==this.direction||void 0===e.id||e.method||this.pairer.onResponse(e)}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.pairer.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(s){e(s)}}}export{s as MessageInterceptor};
package/dist/cli/main.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{spawn as e}from"child_process";import{program as r}from"commander";import{NodeSDK as t}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as s}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as o}from"@opentelemetry/resources";import{trace as n,SpanStatusCode as i}from"@opentelemetry/api";import{Transform as c}from"stream";const a={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};let p=null,m=null,u=!1,g=null;function l(){return m}function d(){return g}async function h(){if(p)try{await p.shutdown(),p=null,m=null,u=!1,g=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}class f extends c{buffer="";direction;pairer;debug;constructor(e,r,t=!1){super(),this.direction=e,this.pairer=r,this.debug=t}_transform(e,r,t){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),t()}catch(e){t(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const r of e){const e=r.trim();if(e)try{const r=JSON.parse(e);this.handleMessage(r)}catch(r){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){"request"===this.direction&&e.method?this.pairer.onRequest(e):"response"!==this.direction||void 0===e.id||e.method||this.pairer.onResponse(e)}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.pairer.getStats();console.error(`[mcp-logger CLI] Stream closed. Pending requests: ${e.pending}`)}e()}catch(r){e(r)}}}function S(e,r=1e4){try{const t=JSON.stringify(e);return t.length>r?t.slice(0,r)+"... (truncated)":t}catch(e){return null}}function I(e){const r=l();if(!r)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const t=`mcp.${e.method}`;r.startActiveSpan(t,r=>{try{const t={"mcp.method":e.method,"mcp.source":e.source,"mcp.transport":e.transport,"mcp.log_type":"request-response","mcp.request.id":String(e.request.id),"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":S(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const r=e.request.params.name,s=e.request.params.arguments;if(r&&(t["mcp.tool.name"]=r),s){const e=S(s);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if(void 0!==e.response.result){const r=S(e.response.result);r&&(t["mcp.response.result"]=r,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const r=S(e.request.headers);r&&(t["mcp.headers"]=r,t["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=d();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(e.customEvents&&e.customEvents.length>0&&(t["mcp.events"]=JSON.stringify(e.customEvents),t["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{r.addEvent(`custom.${e.level||"info"}`,{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){t["mcp.error"]=!0;const s=e.response.error instanceof Error?e.response.error.message:String(e.response.error);t["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:i.ERROR,message:s})}else r.setStatus({code:i.OK});!function(e,r){for(const[t,s]of Object.entries(r))null!=s&&e.setAttribute(t,s)}(r,t)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:i.ERROR,message:String(e)})}finally{r.end()}})}class y{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(e){var r;r=e.method,a.TRACKED_METHODS.includes(r)?e.id?(this.pendingRequests.set(e.id,{request:e,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${e.method} (ID: ${e.id})`),setTimeout(()=>this.cleanupStale(e.id),this.TIMEOUT)):this.debug&&console.error("[mcp-logger CLI] Request without ID, skipping:",e.method):this.debug&&console.error(`[mcp-logger CLI] Skipping method: ${e.method}`)}onResponse(e){if(!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const r=this.pendingRequests.get(e.id);if(!r)return void(this.debug&&console.error(`[mcp-logger CLI] No matching request for response ID: ${e.id}`));const t=Date.now(),s=t-r.timestamp;this.debug&&console.error(`[mcp-logger CLI] Pairing success: ${r.request.method} (${s}ms)`),this.createPairedSpan(r.request,e,r.timestamp,t,s),this.pendingRequests.delete(e.id)}createPairedSpan(e,r,t,s,o){I({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:t,params:e.params},response:{timestamp:s,result:r.result,error:r.error},duration:o})}cleanupStale(e){const r=this.pendingRequests.get(e);r&&Date.now()-r.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] Request timeout: ${r.request.method} (ID: ${e})`),this.pendingRequests.delete(e))}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}r.name("mcp-logger").description("Add observability to any MCP server without code changes").version("1.0.0").requiredOption("-k, --api-key <key>","Aware API key").option("-s, --service-name <name>","Service name for identification").option("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(r,i)=>{try{const c=!0;!function(e){if(u)return console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),m;const r=e.serviceName||`${a.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;console.error("[MCP Logger] Initializing telemetry..."),console.error(`[MCP Logger] Endpoint: ${e.endpoint||a.ENDPOINT}`),console.error(`[MCP Logger] Service: ${r}`),p=new t({resource:new o({"service.name":r,"service.version":a.SDK_VERSION}),traceExporter:new s({url:e.endpoint||a.ENDPOINT,headers:{"x-api-key":e.apiKey}})}),p.start(),m=n.getTracer("mcp-logger",a.SDK_VERSION),u=!0,console.error("[MCP Logger] Telemetry initialized successfully");const i=async()=>{console.error("[MCP Logger] Shutting down telemetry..."),await h()};process.once("SIGTERM",i),process.once("SIGINT",i)}({...i,debug:c}),console.error("[MCP Logger CLI] Starting MCP server:",r.join(" "));const[l,...d]=r;!function(e,r,t){g={command:e,args:r,env:t}}(l,d,{...process.env});const S=e(l,d,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),I=new y(c),C=new f("request",I,c);process.stdin.pipe(C).pipe(S.stdin);const E=new f("response",I,c);S.stdout.pipe(E).pipe(process.stdout),S.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),I.clear(),await h(),process.exit(1)}),S.on("exit",async(e,r)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${r}`),I.clear(),await h(),process.exit(e||0)});let q=!1;const v=async e=>{q||(q=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),S.kill(e),await Promise.race([new Promise(e=>S.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await h(),process.exit(0))};process.on("SIGTERM",()=>v("SIGTERM")),process.on("SIGINT",()=>v("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await h(),process.exit(1)}}),r.parse();
2
+ function e(){return h}function r(){return I}async function t(){if(d)try{await d.shutdown(),d=null,h=null,f=!1,I=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function s(e,r=1e4){try{const t=JSON.stringify(e);return t.length>r?t.slice(0,r)+"... (truncated)":t}catch(e){return null}}function o(t){const o=e();if(!o)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+t.method;o.startActiveSpan(n,e=>{try{const o={"mcp.method":t.method,"mcp.source":t.source,"mcp.transport":t.transport,"mcp.log_type":"request-response","mcp.request.id":t.request.id+"","mcp.request.timestamp":t.request.timestamp,"mcp.request.params":s(t.request.params)||"{}","mcp.request.params.size":JSON.stringify(t.request.params||{}).length,"mcp.response.timestamp":t.response.timestamp,"mcp.duration_ms":t.duration};if("tools/call"===t.method&&t.request.params){const e=t.request.params.name,r=t.request.params.arguments;if(e&&(o["mcp.tool.name"]=e),r){const e=s(r);e&&(o["mcp.tool.arguments"]=e,o["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==t.response.result){const e=s(t.response.result);e&&(o["mcp.response.result"]=e,o["mcp.response.result.size"]=JSON.stringify(t.response.result).length)}if(t.request.headers){const e=s(t.request.headers);e&&(o["mcp.headers"]=e,o["mcp.headers.size"]=JSON.stringify(t.request.headers).length)}if("cli"===t.source){const e=r();e&&(o["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(t.customEvents&&t.customEvents.length>0&&(o["mcp.events"]=JSON.stringify(t.customEvents),o["mcp.events.count"]=t.customEvents.length,t.customEvents.forEach(r=>{e.addEvent("custom."+(r.level||"info"),{message:r.message,metadata:JSON.stringify(r.metadata||{}),timestamp:r.timestamp||Date.now()})})),t.response.error){o["mcp.error"]=!0;const r=t.response.error instanceof Error?t.response.error.message:t.response.error+"";o["mcp.error.message"]=r,t.response.error instanceof Error&&e.recordException(t.response.error),e.setStatus({code:u.ERROR,message:r})}else e.setStatus({code:u.OK});!function(e,r){for(const[t,s]of Object.entries(r))null!=s&&e.setAttribute(t,s)}(e,o)}catch(r){console.error("[MCP Logger] Error creating paired span:",r),e.setStatus({code:u.ERROR,message:r+""})}finally{e.end()}})}import{spawn as n}from"child_process";import{program as i}from"commander";import{NodeSDK as c}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as a}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as p}from"@opentelemetry/resources";import{trace as m,SpanStatusCode as u}from"@opentelemetry/api";import{Transform as l}from"stream";const g={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};let d=null,h=null,f=!1,I=null;class y extends l{buffer="";direction;pairer;debug;constructor(e,r,t=!1){super(),this.direction=e,this.pairer=r,this.debug=t}_transform(e,r,t){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),t()}catch(e){t(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const r of e){const e=r.trim();if(e)try{const r=JSON.parse(e);this.handleMessage(r)}catch(r){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){"request"===this.direction&&e.method?this.pairer.onRequest(e):"response"!==this.direction||void 0===e.id||e.method||this.pairer.onResponse(e)}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.pairer.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(r){e(r)}}}class S{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(e){var r;r=e.method,g.TRACKED_METHODS.includes(r)?e.id?(this.pendingRequests.set(e.id,{request:e,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${e.method} (ID: ${e.id})`),setTimeout(()=>this.cleanupStale(e.id),this.TIMEOUT)):this.debug&&console.error("[mcp-logger CLI] Request without ID, skipping:",e.method):this.debug&&console.error("[mcp-logger CLI] Skipping method: "+e.method)}onResponse(e){if(!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const r=this.pendingRequests.get(e.id);if(!r)return void(this.debug&&console.error("[mcp-logger CLI] No matching request for response ID: "+e.id));const t=Date.now(),s=t-r.timestamp;this.debug&&console.error(`[mcp-logger CLI] Pairing success: ${r.request.method} (${s}ms)`),this.createPairedSpan(r.request,e,r.timestamp,t,s),this.pendingRequests.delete(e.id)}createPairedSpan(e,r,t,s,n){o({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:t,params:e.params},response:{timestamp:s,result:r.result,error:r.error},duration:n})}cleanupStale(e){const r=this.pendingRequests.get(e);r&&Date.now()-r.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] Request timeout: ${r.request.method} (ID: ${e})`),this.pendingRequests.delete(e))}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}i.name("mcp-logger").description("Add observability to any MCP server without code changes").version("1.0.0").requiredOption("-k, --api-key <key>","Aware API key").option("-s, --service-name <name>","Service name for identification").option("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(e,r)=>{try{const s=!0;!function(e){if(f)return console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),h;const r=e.serviceName||`${g.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+(e.endpoint||g.ENDPOINT)),console.error("[MCP Logger] Service: "+r),d=new c({resource:new p({"service.name":r,"service.version":g.SDK_VERSION}),traceExporter:new a({url:e.endpoint||g.ENDPOINT,headers:{"x-api-key":e.apiKey}})}),d.start(),h=m.getTracer("mcp-logger",g.SDK_VERSION),f=!0,console.error("[MCP Logger] Telemetry initialized successfully");const s=async()=>{console.error("[MCP Logger] Shutting down telemetry..."),await t()};process.once("SIGTERM",s),process.once("SIGINT",s)}({...r,debug:s}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[o,...i]=e;!function(e,r,t){I={command:e,args:r,env:t}}(o,i,{...process.env});const u=n(o,i,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),l=new S(s),C=new y("request",l,s);process.stdin.pipe(C).pipe(u.stdin);const E=new y("response",l,s);u.stdout.pipe(E).pipe(process.stdout),u.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),l.clear(),await t(),process.exit(1)}),u.on("exit",async(e,r)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${r}`),l.clear(),await t(),process.exit(e||0)});let q=!1;const v=async e=>{q||(q=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),u.kill(e),await Promise.race([new Promise(e=>u.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await t(),process.exit(0))};process.on("SIGTERM",()=>v("SIGTERM")),process.on("SIGINT",()=>v("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await t(),process.exit(1)}}),i.parse();
@@ -1 +1 @@
1
- import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const e={TRACKED_METHODS:["tools/call","tools/list"]};class t{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(t){var s;s=t.method,e.TRACKED_METHODS.includes(s)?t.id?(this.pendingRequests.set(t.id,{request:t,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${t.method} (ID: ${t.id})`),setTimeout(()=>this.cleanupStale(t.id),this.TIMEOUT)):this.debug&&console.error("[mcp-logger CLI] Request without ID, skipping:",t.method):this.debug&&console.error(`[mcp-logger CLI] Skipping method: ${t.method}`)}onResponse(e){if(!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const t=this.pendingRequests.get(e.id);if(!t)return void(this.debug&&console.error(`[mcp-logger CLI] No matching request for response ID: ${e.id}`));const s=Date.now(),o=s-t.timestamp;this.debug&&console.error(`[mcp-logger CLI] Pairing success: ${t.request.method} (${o}ms)`),this.createPairedSpan(t.request,e,t.timestamp,s,o),this.pendingRequests.delete(e.id)}createPairedSpan(e,t,s,o,r){e.method,e.id,e.params,t.result,t.error,console.error("[MCP Logger] Tracer not initialized, cannot create span")}cleanupStale(e){const t=this.pendingRequests.get(e);t&&Date.now()-t.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] Request timeout: ${t.request.method} (ID: ${e})`),this.pendingRequests.delete(e))}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}export{t as RequestResponsePairer};
1
+ import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const e={TRACKED_METHODS:["tools/call","tools/list"]};class t{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(t){var s;s=t.method,e.TRACKED_METHODS.includes(s)?t.id?(this.pendingRequests.set(t.id,{request:t,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${t.method} (ID: ${t.id})`),setTimeout(()=>this.cleanupStale(t.id),this.TIMEOUT)):this.debug&&console.error("[mcp-logger CLI] Request without ID, skipping:",t.method):this.debug&&console.error("[mcp-logger CLI] Skipping method: "+t.method)}onResponse(e){if(!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const t=this.pendingRequests.get(e.id);if(!t)return void(this.debug&&console.error("[mcp-logger CLI] No matching request for response ID: "+e.id));const s=Date.now(),o=s-t.timestamp;this.debug&&console.error(`[mcp-logger CLI] Pairing success: ${t.request.method} (${o}ms)`),this.createPairedSpan(t.request,e,t.timestamp,s,o),this.pendingRequests.delete(e.id)}createPairedSpan(e,t,s,o,r){e.method,e.id,e.params,t.result,t.error,console.error("[MCP Logger] Tracer not initialized, cannot create span")}cleanupStale(e){const t=this.pendingRequests.get(e);t&&Date.now()-t.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] Request timeout: ${t.request.method} (ID: ${e})`),this.pendingRequests.delete(e))}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}export{t as RequestResponsePairer};
@@ -1 +1 @@
1
- const t={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};function E(E){return t.TRACKED_METHODS.includes(E)}export{t as CONFIG,E as shouldTrackMethod};
1
+ function t(t){return E.TRACKED_METHODS.includes(t)}const E={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};export{E as CONFIG,t as shouldTrackMethod};
@@ -1 +1 @@
1
- import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";function e(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}export{e as createPairedSpan};
1
+ function e(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";export{e as createPairedSpan};
@@ -1 +1 @@
1
- import{SpanStatusCode as t,context as r,trace as e}from"@opentelemetry/api";function n(t,r){for(const[e,n]of Object.entries(r))null!=n&&t.setAttribute(e,n)}function o(t,r,e,n){try{e?t.addEvent(r,e):t.addEvent(r)}catch(t){n&&console.error("[MCP Logger] Failed to add span event:",t)}}function c(r,e,n){const o=e instanceof Error?e.message:"Unknown error",c=e instanceof Error?e.constructor.name:"Error";e instanceof Error&&r.recordException(e),r.setStatus({code:t.ERROR,message:o}),r.setAttribute("error",!0),r.setAttribute("error.type",c),r.setAttribute("error.message",o),n&&console.error("[MCP Logger] Error recorded in span:",e)}function s(r){r.setStatus({code:t.OK})}function a(t,r=1e4){try{const e=JSON.stringify(t);return e.length>r?e.slice(0,r)+"... (truncated)":e}catch(t){return null}}function i(){const t={},n=r.active(),o=e.getSpan(n);if(o){const r=o.spanContext();r&&(t.traceparent=`00-${r.traceId}-${r.spanId}-01`)}return t}export{o as addSpanEvent,i as getTraceHeaders,s as markSpanSuccess,c as recordSpanError,a as safeStringify,n as setSpanAttributes};
1
+ function t(t,r){for(const[e,n]of Object.entries(r))null!=n&&t.setAttribute(e,n)}function r(t,r,e,n){try{e?t.addEvent(r,e):t.addEvent(r)}catch(t){n&&console.error("[MCP Logger] Failed to add span event:",t)}}function e(t,r,e){const n=r instanceof Error?r.message:"Unknown error",o=r instanceof Error?r.constructor.name:"Error";r instanceof Error&&t.recordException(r),t.setStatus({code:s.ERROR,message:n}),t.setAttribute("error",!0),t.setAttribute("error.type",o),t.setAttribute("error.message",n),e&&console.error("[MCP Logger] Error recorded in span:",r)}function n(t){t.setStatus({code:s.OK})}function o(t,r=1e4){try{const e=JSON.stringify(t);return e.length>r?e.slice(0,r)+"... (truncated)":e}catch(t){return null}}function c(){const t={},r=a.active(),e=i.getSpan(r);if(e){const r=e.spanContext();r&&(t.traceparent=`00-${r.traceId}-${r.spanId}-01`)}return t}import{SpanStatusCode as s,context as a,trace as i}from"@opentelemetry/api";export{r as addSpanEvent,c as getTraceHeaders,n as markSpanSuccess,e as recordSpanError,o as safeStringify,t as setSpanAttributes};
@@ -1 +1 @@
1
- import{NodeSDK as e}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as r}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as o}from"@opentelemetry/resources";import{trace as t}from"@opentelemetry/api";const n="https://aware.mcypher.com/v1/traces",i="1.0.0";let c=null,l=null,s=!1,a=null;function u(a){if(s)return a.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),l;const u=a.serviceName||`mcp-server-${Math.random().toString(36).slice(2,8)}`;a.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error(`[MCP Logger] Endpoint: ${a.endpoint||n}`),console.error(`[MCP Logger] Service: ${u}`)),c=new e({resource:new o({"service.name":u,"service.version":i}),traceExporter:new r({url:a.endpoint||n,headers:{"x-api-key":a.apiKey}})}),c.start(),l=t.getTracer("mcp-logger",i),s=!0,a.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const g=async()=>{a.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await y()};return process.once("SIGTERM",g),process.once("SIGINT",g),l}function g(){return l}function m(){return s}function p(e,r,o){a={command:e,args:r,env:o}}function d(){return a}async function y(){if(c)try{await c.shutdown(),c=null,l=null,s=!1,a=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}export{d as getCLIServerInfo,g as getTracer,u as initializeTelemetry,m as isInitializedTelemetry,p as setCLIServerInfo,y as shutdownTelemetry};
1
+ function e(e){if(y)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),d;const r=e.serviceName||`${m}-${Math.random().toString(36).slice(2,8)}`;e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+(e.endpoint||u)),console.error("[MCP Logger] Service: "+r)),p=new c({resource:new s({"service.name":r,"service.version":g}),traceExporter:new l({url:e.endpoint||u,headers:{"x-api-key":e.apiKey}})}),p.start(),d=a.getTracer("mcp-logger",g),y=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await i()};return process.once("SIGTERM",o),process.once("SIGINT",o),d}function r(){return d}function o(){return y}function t(e,r,o){f={command:e,args:r,env:o}}function n(){return f}async function i(){if(p)try{await p.shutdown(),p=null,d=null,y=!1,f=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}import{NodeSDK as c}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as l}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as s}from"@opentelemetry/resources";import{trace as a}from"@opentelemetry/api";const u="https://aware.mcypher.com/v1/traces",g="1.0.0",m="mcp-server";let p=null,d=null,y=!1,f=null;export{n as getCLIServerInfo,r as getTracer,e as initializeTelemetry,o as isInitializedTelemetry,t as setCLIServerInfo,i as shutdownTelemetry};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{trace as e,SpanStatusCode as t,context as r}from"@opentelemetry/api";import{NodeSDK as s}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as o}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as n}from"@opentelemetry/resources";const a={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};let c=null,i=null,m=!1,l=null;function p(){return i}function u(){return l}async function g(){if(c)try{await c.shutdown(),c=null,i=null,m=!1,l=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function d(e,t=1e4){try{const r=JSON.stringify(e);return r.length>t?r.slice(0,t)+"... (truncated)":r}catch(e){return null}}function f(e){const r=p();if(!r)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const s=`mcp.${e.method}`;r.startActiveSpan(s,r=>{try{const s={"mcp.method":e.method,"mcp.source":e.source,"mcp.transport":e.transport,"mcp.log_type":"request-response","mcp.request.id":String(e.request.id),"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":d(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const t=e.request.params.name,r=e.request.params.arguments;if(t&&(s["mcp.tool.name"]=t),r){const e=d(r);e&&(s["mcp.tool.arguments"]=e,s["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==e.response.result){const t=d(e.response.result);t&&(s["mcp.response.result"]=t,s["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const t=d(e.request.headers);t&&(s["mcp.headers"]=t,s["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=u();e&&(s["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(e.customEvents&&e.customEvents.length>0&&(s["mcp.events"]=JSON.stringify(e.customEvents),s["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{r.addEvent(`custom.${e.level||"info"}`,{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){s["mcp.error"]=!0;const o=e.response.error instanceof Error?e.response.error.message:String(e.response.error);s["mcp.error.message"]=o,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:t.ERROR,message:o})}else r.setStatus({code:t.OK});!function(e,t){for(const[r,s]of Object.entries(t))null!=s&&e.setAttribute(r,s)}(r,s)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:t.ERROR,message:String(e)})}finally{r.end()}})}const h=Symbol("mcp-logger.customEvents");const E=Object.assign(function(t,l){if(!l.apiKey)throw new Error("[mcp-logger] apiKey is required");if(!t)throw new Error("[mcp-logger] server instance is required");!function(t){if(m)return t.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),i;const r=t.serviceName||`${a.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;t.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error(`[MCP Logger] Endpoint: ${t.endpoint||a.ENDPOINT}`),console.error(`[MCP Logger] Service: ${r}`)),c=new s({resource:new n({"service.name":r,"service.version":a.SDK_VERSION}),traceExporter:new o({url:t.endpoint||a.ENDPOINT,headers:{"x-api-key":t.apiKey}})}),c.start(),i=e.getTracer("mcp-logger",a.SDK_VERSION),m=!0,t.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const l=async()=>{t.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await g()};process.once("SIGTERM",l),process.once("SIGINT",l)}(l),function(e,t){const s=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,o){const n=e.shape?.method,c=n?._def?.value||"unknown";return function(e){return a.TRACKED_METHODS.includes(e)}(c)?(t.debug&&console.log(`[mcp-logger] Instrumenting handler: ${c}`),s(e,async(e,s)=>{const n=Date.now(),a=`${c}-${n}-${Math.random().toString(36).slice(2,8)}`,i=[],m=r.active().setValue(h,i);try{const l=await r.with(m,async()=>await o(e,s)),p=Date.now();return f({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:n,params:e.params},response:{timestamp:p,result:l},duration:p-n,customEvents:i.length>0?i:void 0}),t.debug&&console.log(`[mcp-logger] Request completed (${p-n}ms):`,{method:c,customEvents:i.length}),l}catch(r){const s=Date.now();throw f({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:n,params:e.params},response:{timestamp:s,error:r},duration:s-n,customEvents:i.length>0?i:void 0}),t.debug&&console.error("[mcp-logger] Request failed:",r),r}})):(t.debug&&console.log(`[mcp-logger] Skipping instrumentation for method: ${c}`),s(e,o))},t.debug&&console.log("[mcp-logger] Server instrumentation complete")}(t,l),l.debug&&console.log("[mcp-logger] ✓ Initialization complete")},{addLog(t){const s=r.active().getValue(h);if(!s)return void console.warn("[mcp-logger] addLog called outside of handler context");const o={level:t.level||"info",message:t.message,metadata:t.metadata,timestamp:t.timestamp||Date.now()};s.push(o);const n=e.getSpan(r.active());n&&n.addEvent(`custom.${o.level}`,{message:o.message,metadata:JSON.stringify(o.metadata||{}),timestamp:o.timestamp})},async shutdown(){await g()}});export{E as trace};
1
+ function e(){return g}function t(){return f}async function r(){if(u)try{await u.shutdown(),u=null,g=null,d=!1,f=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function s(e,t=1e4){try{const r=JSON.stringify(e);return r.length>t?r.slice(0,t)+"... (truncated)":r}catch(e){return null}}function o(r){const o=e();if(!o)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+r.method;o.startActiveSpan(n,e=>{try{const o={"mcp.method":r.method,"mcp.source":r.source,"mcp.transport":r.transport,"mcp.log_type":"request-response","mcp.request.id":r.request.id+"","mcp.request.timestamp":r.request.timestamp,"mcp.request.params":s(r.request.params)||"{}","mcp.request.params.size":JSON.stringify(r.request.params||{}).length,"mcp.response.timestamp":r.response.timestamp,"mcp.duration_ms":r.duration};if("tools/call"===r.method&&r.request.params){const e=r.request.params.name,t=r.request.params.arguments;if(e&&(o["mcp.tool.name"]=e),t){const e=s(t);e&&(o["mcp.tool.arguments"]=e,o["mcp.tool.arguments.size"]=JSON.stringify(t).length)}}if(void 0!==r.response.result){const e=s(r.response.result);e&&(o["mcp.response.result"]=e,o["mcp.response.result.size"]=JSON.stringify(r.response.result).length)}if(r.request.headers){const e=s(r.request.headers);e&&(o["mcp.headers"]=e,o["mcp.headers.size"]=JSON.stringify(r.request.headers).length)}if("cli"===r.source){const e=t();e&&(o["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(r.customEvents&&r.customEvents.length>0&&(o["mcp.events"]=JSON.stringify(r.customEvents),o["mcp.events.count"]=r.customEvents.length,r.customEvents.forEach(t=>{e.addEvent("custom."+(t.level||"info"),{message:t.message,metadata:JSON.stringify(t.metadata||{}),timestamp:t.timestamp||Date.now()})})),r.response.error){o["mcp.error"]=!0;const t=r.response.error instanceof Error?r.response.error.message:r.response.error+"";o["mcp.error.message"]=t,r.response.error instanceof Error&&e.recordException(r.response.error),e.setStatus({code:a.ERROR,message:t})}else e.setStatus({code:a.OK});!function(e,t){for(const[r,s]of Object.entries(t))null!=s&&e.setAttribute(r,s)}(e,o)}catch(t){console.error("[MCP Logger] Error creating paired span:",t),e.setStatus({code:a.ERROR,message:t+""})}finally{e.end()}})}import{trace as n,SpanStatusCode as a,context as c}from"@opentelemetry/api";import{NodeSDK as i}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as m}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as l}from"@opentelemetry/resources";const p={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};let u=null,g=null,d=!1,f=null;const h=Symbol("mcp-logger.customEvents"),E=Object.assign(function(e,t){if(!t.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(e){if(d)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),g;const t=e.serviceName||`${p.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+(e.endpoint||p.ENDPOINT)),console.error("[MCP Logger] Service: "+t)),u=new i({resource:new l({"service.name":t,"service.version":p.SDK_VERSION}),traceExporter:new m({url:e.endpoint||p.ENDPOINT,headers:{"x-api-key":e.apiKey}})}),u.start(),g=n.getTracer("mcp-logger",p.SDK_VERSION),d=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const s=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await r()};process.once("SIGTERM",s),process.once("SIGINT",s)}(t),function(e,t){const r=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,s){const n=e.shape?.method,a=n?._def?.value||"unknown";return function(e){return p.TRACKED_METHODS.includes(e)}(a)?(t.debug&&console.log("[mcp-logger] Instrumenting handler: "+a),r(e,async(e,r)=>{const n=Date.now(),i=`${a}-${n}-${Math.random().toString(36).slice(2,8)}`,m=[],l=c.active().setValue(h,m);try{const p=await c.with(l,async()=>await s(e,r)),u=Date.now();return o({method:a,source:"sdk",transport:"stdio",request:{id:i,timestamp:n,params:e.params},response:{timestamp:u,result:p},duration:u-n,customEvents:m.length>0?m:void 0}),t.debug&&console.log(`[mcp-logger] Request completed (${u-n}ms):`,{method:a,customEvents:m.length}),p}catch(r){const s=Date.now();throw o({method:a,source:"sdk",transport:"stdio",request:{id:i,timestamp:n,params:e.params},response:{timestamp:s,error:r},duration:s-n,customEvents:m.length>0?m:void 0}),t.debug&&console.error("[mcp-logger] Request failed:",r),r}})):(t.debug&&console.log("[mcp-logger] Skipping instrumentation for method: "+a),r(e,s))},t.debug&&console.log("[mcp-logger] Server instrumentation complete")}(e,t),t.debug&&console.log("[mcp-logger] ✓ Initialization complete")},{addLog(e){const t=c.active().getValue(h);if(!t)return void console.warn("[mcp-logger] addLog called outside of handler context");const r={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};t.push(r);const s=n.getSpan(c.active());s&&s.addEvent("custom."+r.level,{message:r.message,metadata:JSON.stringify(r.metadata||{}),timestamp:r.timestamp})},async shutdown(){await r()}});export{E as trace};
package/dist/sdk/index.js CHANGED
@@ -1 +1 @@
1
- import{trace as e,SpanStatusCode as t,context as r}from"@opentelemetry/api";import{NodeSDK as s}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as o}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as n}from"@opentelemetry/resources";const a={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};let c=null,i=null,m=!1,l=null;function p(){return i}function u(){return l}async function g(){if(c)try{await c.shutdown(),c=null,i=null,m=!1,l=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function d(e,t=1e4){try{const r=JSON.stringify(e);return r.length>t?r.slice(0,t)+"... (truncated)":r}catch(e){return null}}function f(e){const r=p();if(!r)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const s=`mcp.${e.method}`;r.startActiveSpan(s,r=>{try{const s={"mcp.method":e.method,"mcp.source":e.source,"mcp.transport":e.transport,"mcp.log_type":"request-response","mcp.request.id":String(e.request.id),"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":d(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const t=e.request.params.name,r=e.request.params.arguments;if(t&&(s["mcp.tool.name"]=t),r){const e=d(r);e&&(s["mcp.tool.arguments"]=e,s["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==e.response.result){const t=d(e.response.result);t&&(s["mcp.response.result"]=t,s["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const t=d(e.request.headers);t&&(s["mcp.headers"]=t,s["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=u();e&&(s["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(e.customEvents&&e.customEvents.length>0&&(s["mcp.events"]=JSON.stringify(e.customEvents),s["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{r.addEvent(`custom.${e.level||"info"}`,{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){s["mcp.error"]=!0;const o=e.response.error instanceof Error?e.response.error.message:String(e.response.error);s["mcp.error.message"]=o,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:t.ERROR,message:o})}else r.setStatus({code:t.OK});!function(e,t){for(const[r,s]of Object.entries(t))null!=s&&e.setAttribute(r,s)}(r,s)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:t.ERROR,message:String(e)})}finally{r.end()}})}const h=Symbol("mcp-logger.customEvents");const E=Object.assign(function(t,l){if(!l.apiKey)throw new Error("[mcp-logger] apiKey is required");if(!t)throw new Error("[mcp-logger] server instance is required");!function(t){if(m)return t.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),i;const r=t.serviceName||`${a.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;t.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error(`[MCP Logger] Endpoint: ${t.endpoint||a.ENDPOINT}`),console.error(`[MCP Logger] Service: ${r}`)),c=new s({resource:new n({"service.name":r,"service.version":a.SDK_VERSION}),traceExporter:new o({url:t.endpoint||a.ENDPOINT,headers:{"x-api-key":t.apiKey}})}),c.start(),i=e.getTracer("mcp-logger",a.SDK_VERSION),m=!0,t.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const l=async()=>{t.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await g()};process.once("SIGTERM",l),process.once("SIGINT",l)}(l),function(e,t){const s=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,o){const n=e.shape?.method,c=n?._def?.value||"unknown";return function(e){return a.TRACKED_METHODS.includes(e)}(c)?(t.debug&&console.log(`[mcp-logger] Instrumenting handler: ${c}`),s(e,async(e,s)=>{const n=Date.now(),a=`${c}-${n}-${Math.random().toString(36).slice(2,8)}`,i=[],m=r.active().setValue(h,i);try{const l=await r.with(m,async()=>await o(e,s)),p=Date.now();return f({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:n,params:e.params},response:{timestamp:p,result:l},duration:p-n,customEvents:i.length>0?i:void 0}),t.debug&&console.log(`[mcp-logger] Request completed (${p-n}ms):`,{method:c,customEvents:i.length}),l}catch(r){const s=Date.now();throw f({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:n,params:e.params},response:{timestamp:s,error:r},duration:s-n,customEvents:i.length>0?i:void 0}),t.debug&&console.error("[mcp-logger] Request failed:",r),r}})):(t.debug&&console.log(`[mcp-logger] Skipping instrumentation for method: ${c}`),s(e,o))},t.debug&&console.log("[mcp-logger] Server instrumentation complete")}(t,l),l.debug&&console.log("[mcp-logger] ✓ Initialization complete")},{addLog(t){const s=r.active().getValue(h);if(!s)return void console.warn("[mcp-logger] addLog called outside of handler context");const o={level:t.level||"info",message:t.message,metadata:t.metadata,timestamp:t.timestamp||Date.now()};s.push(o);const n=e.getSpan(r.active());n&&n.addEvent(`custom.${o.level}`,{message:o.message,metadata:JSON.stringify(o.metadata||{}),timestamp:o.timestamp})},async shutdown(){await g()}});export{E as trace};
1
+ function e(){return g}function t(){return f}async function r(){if(u)try{await u.shutdown(),u=null,g=null,d=!1,f=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function s(e,t=1e4){try{const r=JSON.stringify(e);return r.length>t?r.slice(0,t)+"... (truncated)":r}catch(e){return null}}function o(r){const o=e();if(!o)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+r.method;o.startActiveSpan(n,e=>{try{const o={"mcp.method":r.method,"mcp.source":r.source,"mcp.transport":r.transport,"mcp.log_type":"request-response","mcp.request.id":r.request.id+"","mcp.request.timestamp":r.request.timestamp,"mcp.request.params":s(r.request.params)||"{}","mcp.request.params.size":JSON.stringify(r.request.params||{}).length,"mcp.response.timestamp":r.response.timestamp,"mcp.duration_ms":r.duration};if("tools/call"===r.method&&r.request.params){const e=r.request.params.name,t=r.request.params.arguments;if(e&&(o["mcp.tool.name"]=e),t){const e=s(t);e&&(o["mcp.tool.arguments"]=e,o["mcp.tool.arguments.size"]=JSON.stringify(t).length)}}if(void 0!==r.response.result){const e=s(r.response.result);e&&(o["mcp.response.result"]=e,o["mcp.response.result.size"]=JSON.stringify(r.response.result).length)}if(r.request.headers){const e=s(r.request.headers);e&&(o["mcp.headers"]=e,o["mcp.headers.size"]=JSON.stringify(r.request.headers).length)}if("cli"===r.source){const e=t();e&&(o["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(r.customEvents&&r.customEvents.length>0&&(o["mcp.events"]=JSON.stringify(r.customEvents),o["mcp.events.count"]=r.customEvents.length,r.customEvents.forEach(t=>{e.addEvent("custom."+(t.level||"info"),{message:t.message,metadata:JSON.stringify(t.metadata||{}),timestamp:t.timestamp||Date.now()})})),r.response.error){o["mcp.error"]=!0;const t=r.response.error instanceof Error?r.response.error.message:r.response.error+"";o["mcp.error.message"]=t,r.response.error instanceof Error&&e.recordException(r.response.error),e.setStatus({code:a.ERROR,message:t})}else e.setStatus({code:a.OK});!function(e,t){for(const[r,s]of Object.entries(t))null!=s&&e.setAttribute(r,s)}(e,o)}catch(t){console.error("[MCP Logger] Error creating paired span:",t),e.setStatus({code:a.ERROR,message:t+""})}finally{e.end()}})}import{trace as n,SpanStatusCode as a,context as c}from"@opentelemetry/api";import{NodeSDK as i}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as m}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as l}from"@opentelemetry/resources";const p={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",TRACKED_METHODS:["tools/call","tools/list"]};let u=null,g=null,d=!1,f=null;const h=Symbol("mcp-logger.customEvents"),E=Object.assign(function(e,t){if(!t.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(e){if(d)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),g;const t=e.serviceName||`${p.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+(e.endpoint||p.ENDPOINT)),console.error("[MCP Logger] Service: "+t)),u=new i({resource:new l({"service.name":t,"service.version":p.SDK_VERSION}),traceExporter:new m({url:e.endpoint||p.ENDPOINT,headers:{"x-api-key":e.apiKey}})}),u.start(),g=n.getTracer("mcp-logger",p.SDK_VERSION),d=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const s=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await r()};process.once("SIGTERM",s),process.once("SIGINT",s)}(t),function(e,t){const r=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,s){const n=e.shape?.method,a=n?._def?.value||"unknown";return function(e){return p.TRACKED_METHODS.includes(e)}(a)?(t.debug&&console.log("[mcp-logger] Instrumenting handler: "+a),r(e,async(e,r)=>{const n=Date.now(),i=`${a}-${n}-${Math.random().toString(36).slice(2,8)}`,m=[],l=c.active().setValue(h,m);try{const p=await c.with(l,async()=>await s(e,r)),u=Date.now();return o({method:a,source:"sdk",transport:"stdio",request:{id:i,timestamp:n,params:e.params},response:{timestamp:u,result:p},duration:u-n,customEvents:m.length>0?m:void 0}),t.debug&&console.log(`[mcp-logger] Request completed (${u-n}ms):`,{method:a,customEvents:m.length}),p}catch(r){const s=Date.now();throw o({method:a,source:"sdk",transport:"stdio",request:{id:i,timestamp:n,params:e.params},response:{timestamp:s,error:r},duration:s-n,customEvents:m.length>0?m:void 0}),t.debug&&console.error("[mcp-logger] Request failed:",r),r}})):(t.debug&&console.log("[mcp-logger] Skipping instrumentation for method: "+a),r(e,s))},t.debug&&console.log("[mcp-logger] Server instrumentation complete")}(e,t),t.debug&&console.log("[mcp-logger] ✓ Initialization complete")},{addLog(e){const t=c.active().getValue(h);if(!t)return void console.warn("[mcp-logger] addLog called outside of handler context");const r={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};t.push(r);const s=n.getSpan(c.active());s&&s.addEvent("custom."+r.level,{message:r.message,metadata:JSON.stringify(r.metadata||{}),timestamp:r.timestamp})},async shutdown(){await r()}});export{E as trace};
@@ -1 +1 @@
1
- import{context as e}from"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const t={TRACKED_METHODS:["tools/call","tools/list"]};function o(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}const n=Symbol("mcp-logger.customEvents");function r(r,c){const s=r.setRequestHandler.bind(r);r.setRequestHandler=function(r,l){const a=r.shape?.method,i=a?._def?.value||"unknown";return function(e){return t.TRACKED_METHODS.includes(e)}(i)?(c.debug&&console.log(`[mcp-logger] Instrumenting handler: ${i}`),s(r,async(t,r)=>{const s=Date.now(),a=(Math.random().toString(36).slice(2,8),[]),m=e.active().setValue(n,a);try{const n=await e.with(m,async()=>await l(t,r)),u=Date.now();return o(t.params),c.debug&&console.log(`[mcp-logger] Request completed (${u-s}ms):`,{method:i,customEvents:a.length}),n}catch(e){throw o(t.params),c.debug&&console.error("[mcp-logger] Request failed:",e),e}})):(c.debug&&console.log(`[mcp-logger] Skipping instrumentation for method: ${i}`),s(r,l))},c.debug&&console.log("[mcp-logger] Server instrumentation complete")}function c(){return e.active().getValue(n)}export{c as getCustomEventsFromContext,r as instrumentMCPServer};
1
+ function e(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function t(t,o){const l=t.setRequestHandler.bind(t);t.setRequestHandler=function(t,s){const a=t.shape?.method,i=a?._def?.value||"unknown";return function(e){return r.TRACKED_METHODS.includes(e)}(i)?(o.debug&&console.log("[mcp-logger] Instrumenting handler: "+i),l(t,async(t,r)=>{const l=Date.now(),a=(Math.random().toString(36).slice(2,8),[]),m=n.active().setValue(c,a);try{const c=await n.with(m,async()=>await s(t,r)),u=Date.now();return e(t.params),o.debug&&console.log(`[mcp-logger] Request completed (${u-l}ms):`,{method:i,customEvents:a.length}),c}catch(n){throw e(t.params),o.debug&&console.error("[mcp-logger] Request failed:",n),n}})):(o.debug&&console.log("[mcp-logger] Skipping instrumentation for method: "+i),l(t,s))},o.debug&&console.log("[mcp-logger] Server instrumentation complete")}function o(){return n.active().getValue(c)}import{context as n}from"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const r={TRACKED_METHODS:["tools/call","tools/list"]},c=Symbol("mcp-logger.customEvents");export{o as getCustomEventsFromContext,t as instrumentMCPServer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awarecorp/mcp-logger",
3
- "version": "0.0.2-dev.5",
3
+ "version": "0.0.2",
4
4
  "description": "Unified MCP observability solution - SDK for code integration and CLI for zero-config wrapping",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",