@awarecorp/mcp-logger 0.0.3 β†’ 0.0.4

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
@@ -18,16 +18,12 @@
18
18
 
19
19
  All telemetry data is sent to OpenTelemetry-compatible endpoints with automatic request-response pairing.
20
20
 
21
- ---
22
-
23
21
  ## πŸ“¦ Installation
24
22
 
25
23
  ```bash
26
24
  npm install @awarecorp/mcp-logger
27
25
  ```
28
26
 
29
- ---
30
-
31
27
  ## πŸš€ Quick Start
32
28
 
33
29
  ### SDK Mode
@@ -35,11 +31,11 @@ npm install @awarecorp/mcp-logger
35
31
  Instrument your MCP server with one line of code:
36
32
 
37
33
  ```typescript
38
- import { trace } from '@awarecorp/mcp-logger';
34
+ import { trace } from "@awarecorp/mcp-logger";
39
35
 
40
36
  trace(server, {
41
- apiKey: 'YOUR_API_KEY',
42
- serviceName: 'my-mcp-server'
37
+ apiKey: "YOUR_API_KEY",
38
+ serviceName: "my-mcp-server",
43
39
  });
44
40
  ```
45
41
 
@@ -54,8 +50,6 @@ npx @awarecorp/mcp-logger \
54
50
  npx your-mcp-server
55
51
  ```
56
52
 
57
- ---
58
-
59
53
  ## πŸ“‹ Features
60
54
 
61
55
  ### Core Capabilities
@@ -84,6 +78,7 @@ All spans include:
84
78
  ### Data Format
85
79
 
86
80
  Optimized for Elasticsearch with flat structure:
81
+
87
82
  - `mcp.request.method` / `mcp.response.method`: Method names for request/response
88
83
  - `mcp.source`: `sdk` or `cli`
89
84
  - `mcp.transport`: `stdio` or `sse`
@@ -94,8 +89,6 @@ Optimized for Elasticsearch with flat structure:
94
89
  - `mcp.events`: Custom log entries (JSON array)
95
90
  - `mcp.cli.server`: CLI execution info (JSON object)
96
91
 
97
- ---
98
-
99
92
  ## πŸ”§ SDK API
100
93
 
101
94
  ### `trace(server, options)`
@@ -103,6 +96,7 @@ Optimized for Elasticsearch with flat structure:
103
96
  Enable automatic instrumentation on an MCP server.
104
97
 
105
98
  **Parameters:**
99
+
106
100
  - `server`: MCP Server instance
107
101
  - `options.apiKey`: API key for authentication (required)
108
102
  - Currently accepts any string value for testing purposes
@@ -120,6 +114,7 @@ Enable automatic instrumentation on an MCP server.
120
114
  Add custom log entries within request handlers.
121
115
 
122
116
  **Parameters:**
117
+
123
118
  - `entry.level`: Log level (`info` | `warn` | `error`)
124
119
  - `entry.message`: Log message (required)
125
120
  - `entry.metadata`: Additional data (optional)
@@ -133,8 +128,6 @@ Gracefully shutdown telemetry and flush pending data.
133
128
 
134
129
  **Returns:** `Promise<void>`
135
130
 
136
- ---
137
-
138
131
  ## πŸ–₯️ CLI Usage
139
132
 
140
133
  ### Basic Syntax
@@ -158,11 +151,13 @@ mcp-logger [options] <command...>
158
151
  ### Examples
159
152
 
160
153
  **Wrap an npx command:**
154
+
161
155
  ```bash
162
156
  mcp-logger -k API_KEY -s filesystem npx -y @modelcontextprotocol/server-filesystem /path
163
157
  ```
164
158
 
165
159
  **Use with Claude Desktop:**
160
+
166
161
  ```json
167
162
  {
168
163
  "mcpServers": {
@@ -185,8 +180,6 @@ mcp-logger -k API_KEY -s filesystem npx -y @modelcontextprotocol/server-filesyst
185
180
  }
186
181
  ```
187
182
 
188
- ---
189
-
190
183
  ## πŸ—οΈ How It Works
191
184
 
192
185
  ### Request-Response Pairing
@@ -207,15 +200,15 @@ Creates **one span per request-response pair** for complete transaction context:
207
200
  ### Tracked Methods
208
201
 
209
202
  Configurable method filtering with exclusion list:
203
+
210
204
  - Tracks: `tools/call`, `tools/list`, and other business-critical methods
211
205
  - Excludes: `notifications/initialized` and other noisy protocol notifications
212
206
  - Customizable via `EXCLUDED_METHODS` configuration
213
207
 
214
- ---
215
-
216
208
  ## πŸ“Š Telemetry Data Structure
217
209
 
218
210
  Each span includes:
211
+
219
212
  - Request/response timestamps and payloads (with optional fields for notifications)
220
213
  - Method name and parameters (separate fields for request/response)
221
214
  - Tool names and arguments (for `tools/call`)
@@ -227,19 +220,36 @@ Each span includes:
227
220
  ### Span Attributes
228
221
 
229
222
  All attributes use a flat structure optimized for Elasticsearch:
223
+
224
+ **Core Attributes:**
225
+
230
226
  - Request attributes: `mcp.request.*` (id, method, params, timestamp)
231
227
  - Response attributes: `mcp.response.*` (method, result, error, timestamp)
232
228
  - Error handling: Proper serialization for Error objects, strings, and complex objects
233
229
  - Metadata: source, transport, duration, tool information
234
230
 
235
- ---
231
+ **Context Tracking:**
232
+
233
+ - `mcp.session.id`: Unique session identifier
234
+ - `mcp.conversation.id`: Conversation/thread identifier
235
+ - `mcp.user.id`: User identifier (from params, headers, or environment)
236
+ - `mcp.client.name`: Client application name (e.g., 'Claude Desktop')
237
+ - `mcp.client.version`: Client version
238
+
239
+ **Permission & Security:**
240
+
241
+ - `mcp.permission.level`: Permission level (read/write/admin/elevated)
242
+
243
+ **Resource Tracking:**
244
+
245
+ - `mcp.resource.type`: Resource type (tool/file/prompt)
246
+ - `mcp.resource.uri`: Resource URI or identifier
247
+ - `mcp.resource.access_count`: Number of times resource accessed
236
248
 
237
249
  ## πŸ“„ License
238
250
 
239
251
  MIT Β© [Aware Corp](https://awarecorp.io/)
240
252
 
241
- ---
242
-
243
253
  ## πŸ”— Links
244
254
 
245
255
  - [Aware Corp](https://awarecorp.io/)
package/dist/cli/main.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- function e(e){return!h.includes(e)}function r(){return y}function s(){return S}async function t(){if(f)try{await f.shutdown(),f=null,y=null,q=!1,S=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,r){for(const[s,t]of Object.entries(r))null!=t&&e.setAttribute(s,t)}function n(e,r=1e4){try{const s=JSON.stringify(e);return s.length>r?s.slice(0,r)+"... (truncated)":s}catch(e){return null}}import{spawn as i}from"child_process";import{program as c}from"commander";import{NodeSDK as a}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as p}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as m}from"@opentelemetry/resources";import{trace as u,SpanStatusCode as d}from"@opentelemetry/api";import{Transform as l}from"stream";const g="1.0.0",h=["notifications/initialized"];let f=null,y=null,q=!1,S=null;class v extends l{buffer="";direction;logger;debug;constructor(e,r,s=!1){super(),this.direction=e,this.logger=r,this.debug=s}_transform(e,r,s){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(),s()}catch(e){s(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){console.error("[mcp-logger CLI] Handling message:",JSON.stringify(e).slice(0,100)),"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.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.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(r){e(r)}}}class C{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(r){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“₯ REQUEST: method="${r.method}", id=${r.id}, params=${JSON.stringify(r.params)?.slice(0,100)}`),e(r.method)){if(!r.id)return this.debug&&console.error(`[mcp-logger CLI] πŸ”” Notification detected: ${r.method} β†’ Creating request-only span`),void this.createRequestOnlySpan(r,Date.now());this.pendingRequests.set(r.id,{request:r,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${r.method} (ID: ${r.id})`),setTimeout(()=>this.cleanupStale(r.id),this.TIMEOUT)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+r.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“€ RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!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 this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} β†’ Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const s=Date.now(),t=s-r.timestamp;this.debug&&console.error(`[mcp-logger CLI] βœ… Pairing success: ${r.request.method} (${t}ms)`),this.createPairedSpan(r.request,e,r.timestamp,s,t),this.pendingRequests.delete(e.id)}onNotification(r){this.debug&&console.error(`[mcp-logger CLI] πŸ“’ NOTIFICATION (Serverβ†’Client): method="${r.method}", params=${JSON.stringify(r.params)?.slice(0,100)}`),e(r.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:r.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+r.method)}createPairedSpan(e,t,i,c,a){!function(e){const t=r();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,r=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":n(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.id":e.request.id+"","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=n(s);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if(void 0!==e.response.result){const r=n(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=n(e.request.headers);r&&(t["mcp.headers"]=r,t["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=s();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){let s;t["mcp.error"]=!0,s=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:n(e.response.error)||"Unknown error",t["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:d.ERROR,message:s})}else r.setStatus({code:d.OK});o(r,t)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:d.ERROR,message:e+""})}finally{r.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:i,params:e.params},response:{timestamp:c,result:t.result,error:t.error},duration:a})}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}) β†’ Creating request-only span`),this.createRequestOnlySpan(r.request,r.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,t){!function(e){const t=r();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,r=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":n(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length};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=n(s);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if("cli"===e.source){const e=s();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}r.setStatus({code:d.OK}),o(r,t)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),r.setStatus({code:d.ERROR,message:e+""})}finally{r.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:t,params:e.params}})}createResponseOnlySpan(e,t){!function(e){const t=r();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,r=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.response.id":e.response.id+"","mcp.response.method":e.method,"mcp.response.timestamp":e.response.timestamp};if(void 0!==e.response.result){const r=n(e.response.result);r&&(t["mcp.response.result"]=r,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.response.error){let s;t["mcp.error"]=!0,s=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:n(e.response.error)||"Unknown error",t["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:d.ERROR,message:s})}else r.setStatus({code:d.OK});if("cli"===e.source){const e=s();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}o(r,t)}catch(e){console.error("[MCP Logger] Error creating response-only span:",e),r.setStatus({code:d.ERROR,message:e+""})}finally{r.end()}})}({method:e.method||"same_request_method",source:"cli",transport:"stdio",response:{id:e.id,timestamp:t,result:e.result,error:e.error}})}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}c.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=!1;!function(e){if(q)return y;const r=e.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);f=new a({resource:new m({"service.name":s,"service.version":g}),traceExporter:new p({url:r,headers:{"x-api-key":e.apiKey}})}),f.start(),y=u.getTracer("mcp-logger",g),q=!0;const o=async()=>{await t()};process.once("SIGTERM",o),process.once("SIGINT",o)}({...r,debug:s}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[o,...n]=e;!function(e,r){S={command:e,args:r,env:{}}}(o,n);const c=i(o,n,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),d=new C(s),l=new v("request",d,s);process.stdin.pipe(l).pipe(c.stdin);const h=new v("response",d,s);c.stdout.pipe(h).pipe(process.stdout),c.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),d.clear(),await t(),process.exit(1)}),c.on("exit",async(e,r)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${r}`),d.clear(),await t(),process.exit(e||0)});let R=!1;const O=async e=>{R||(R=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),c.kill(e),await Promise.race([new Promise(e=>c.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await t(),process.exit(0))};process.on("SIGTERM",()=>O("SIGTERM")),process.on("SIGINT",()=>O("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await t(),process.exit(1)}}),c.parse();
2
+ function e(e){return!y.includes(e)}function s(){return I}function r(){return C}async function t(){if(q)try{await q.shutdown(),q=null,I=null,S=!1,C=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,s){for(const[r,t]of Object.entries(s))null!=t&&e.setAttribute(r,t)}function n(e,s=1e4){try{const r=JSON.stringify(e);return r.length>s?r.slice(0,s)+"... (truncated)":r}catch(e){return null}}function i(e,s){return e.startsWith("resources/")?{type:e.split("/")[1],uri:s?.uri||s?.path||s?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:s?.name||s?.promptId}:"tools/call"===e?{type:"tool",uri:s?.name}:s?.resourceType&&s?.resourceUri?{type:s.resourceType,uri:s.resourceUri}:{type:void 0,uri:void 0}}function c(e,s,r,t){const o=function(e){if(e){const s=e["user-agent"]||e["User-Agent"];if(s){const e=s.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(t),n=(u=`${o.name||"unknown"}-${o.version||"0"}`,R.has(u)||R.set(u,"session-"+ ++O),R.get(u)),c=function(e,s){const r=`${e}-${s}`;return M.has(r)||M.set(r,"conv-"+ ++w),M.get(r)}(n,e),a=function(e,s){return e?.userId||e?.user_id?e.userId||e.user_id:s?s["x-user-id"]||s["X-User-Id"]:void 0}(s,t),p=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(s),m=i(e,s);var u;m.uri&&function(e){const s=(N.get(e)||0)+1;N.set(e,s)}(m.uri);const d=function(e,s){return void 0!==e?.cost?e.cost:void 0!==s?.cost?s.cost:void 0}(s,r),l={};return n&&(l.sessionId=n),c&&(l.conversationId=c),a&&(l.userId=a),o.name&&(l.clientName=o.name),o.version&&(l.clientVersion=o.version),p&&(l.permissionLevel=p),void 0!==d&&(l.cost=d),l}function a(e,s){const r=i(e,s);if(!r.uri)return{};const t=N.get(r.uri)||0;return{resourceType:r.type,resourceUri:r.uri,resourceAccessCount:t}}import{spawn as p}from"child_process";import{program as m}from"commander";import{NodeSDK as u}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as d}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as l}from"@opentelemetry/resources";import{trace as g,SpanStatusCode as h}from"@opentelemetry/api";import{Transform as f}from"stream";const v="1.0.0",y=["notifications/initialized"];let q=null,I=null,S=!1,C=null;class L extends f{buffer="";direction;logger;debug;constructor(e,s,r=!1){super(),this.direction=e,this.logger=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){console.error("[mcp-logger CLI] Handling message:",JSON.stringify(e).slice(0,100)),"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.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.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(s){e(s)}}}const R=new Map;let O=0;const M=new Map;let w=0;const N=new Map;class E{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(s){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“₯ REQUEST: method="${s.method}", id=${s.id}, params=${JSON.stringify(s.params)?.slice(0,100)}`),e(s.method)){if(!s.id)return this.debug&&console.error(`[mcp-logger CLI] πŸ”” Notification detected: ${s.method} β†’ Creating request-only span`),void this.createRequestOnlySpan(s,Date.now());this.pendingRequests.set(s.id,{request:s,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${s.method} (ID: ${s.id})`),setTimeout(()=>this.cleanupStale(s.id),this.TIMEOUT)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+s.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“€ RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const s=this.pendingRequests.get(e.id);if(!s)return this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} β†’ Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const r=Date.now(),t=r-s.timestamp;this.debug&&console.error(`[mcp-logger CLI] βœ… Pairing success: ${s.request.method} (${t}ms)`),this.createPairedSpan(s.request,e,s.timestamp,r,t),this.pendingRequests.delete(e.id)}onNotification(s){this.debug&&console.error(`[mcp-logger CLI] πŸ“’ NOTIFICATION (Serverβ†’Client): method="${s.method}", params=${JSON.stringify(s.params)?.slice(0,100)}`),e(s.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:s.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+s.method)}createPairedSpan(e,t,i,p,m){const u=c(e.method,e.params,t.result,void 0);!function(e){const t=s();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,s=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":n(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.id":e.request.id+"","mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const s=e.request.params.name,r=e.request.params.arguments;if(s&&(t["mcp.tool.name"]=s),r){const e=n(r);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==e.response.result){const s=n(e.response.result);s&&(t["mcp.response.result"]=s,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const s=n(e.request.headers);s&&(t["mcp.headers"]=s,t["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=r();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}const i=e.contextMetadata||c(e.method,e.request.params,e.response.result,e.request.headers);i.sessionId&&(t["mcp.session.id"]=i.sessionId),i.conversationId&&(t["mcp.conversation.id"]=i.conversationId),i.userId&&(t["mcp.user.id"]=i.userId),i.clientName&&(t["mcp.client.name"]=i.clientName),i.clientVersion&&(t["mcp.client.version"]=i.clientVersion),i.permissionLevel&&(t["mcp.permission.level"]=i.permissionLevel),void 0!==i.cost&&(t["mcp.cost"]=i.cost);const p=a(e.method,e.request.params);if(p.resourceType&&(t["mcp.resource.type"]=p.resourceType),p.resourceUri&&(t["mcp.resource.uri"]=p.resourceUri),p.resourceAccessCount&&(t["mcp.resource.access_count"]=p.resourceAccessCount),e.customEvents&&e.customEvents.length>0&&(t["mcp.events"]=JSON.stringify(e.customEvents),t["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{s.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){let r;t["mcp.error"]=!0,r=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:n(e.response.error)||"Unknown error",t["mcp.error.message"]=r,e.response.error instanceof Error&&s.recordException(e.response.error),s.setStatus({code:h.ERROR,message:r})}else s.setStatus({code:h.OK});o(s,t)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),s.setStatus({code:h.ERROR,message:e+""})}finally{s.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:i,params:e.params},response:{timestamp:p,result:t.result,error:t.error},duration:m,contextMetadata:u})}cleanupStale(e){const s=this.pendingRequests.get(e);s&&Date.now()-s.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] ⏰ Request timeout: ${s.request.method} (ID: ${e}) β†’ Creating request-only span`),this.createRequestOnlySpan(s.request,s.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,t){const i=c(e.method,e.params,void 0,void 0);!function(e){const t=s();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,s=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":n(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length};if("tools/call"===e.method&&e.request.params){const s=e.request.params.name,r=e.request.params.arguments;if(s&&(t["mcp.tool.name"]=s),r){const e=n(r);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if("cli"===e.source){const e=r();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}const i=e.contextMetadata||c(e.method,e.request.params,void 0,e.request.headers);i.sessionId&&(t["mcp.session.id"]=i.sessionId),i.conversationId&&(t["mcp.conversation.id"]=i.conversationId),i.userId&&(t["mcp.user.id"]=i.userId),i.clientName&&(t["mcp.client.name"]=i.clientName),i.clientVersion&&(t["mcp.client.version"]=i.clientVersion),i.permissionLevel&&(t["mcp.permission.level"]=i.permissionLevel),void 0!==i.cost&&(t["mcp.cost"]=i.cost);const p=a(e.method,e.request.params);p.resourceType&&(t["mcp.resource.type"]=p.resourceType),p.resourceUri&&(t["mcp.resource.uri"]=p.resourceUri),p.resourceAccessCount&&(t["mcp.resource.access_count"]=p.resourceAccessCount),s.setStatus({code:h.OK}),o(s,t)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),s.setStatus({code:h.ERROR,message:e+""})}finally{s.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:t,params:e.params},contextMetadata:i})}createResponseOnlySpan(e,t){const i=c(e.method||"unknown",void 0,e.result,void 0);!function(e){const t=s();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,s=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.response.id":e.response.id+"","mcp.response.method":e.method,"mcp.response.timestamp":e.response.timestamp};if(void 0!==e.response.result){const s=n(e.response.result);s&&(t["mcp.response.result"]=s,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.response.error){let r;t["mcp.error"]=!0,r=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:n(e.response.error)||"Unknown error",t["mcp.error.message"]=r,e.response.error instanceof Error&&s.recordException(e.response.error),s.setStatus({code:h.ERROR,message:r})}else s.setStatus({code:h.OK});if("cli"===e.source){const e=r();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}const i=e.contextMetadata||c(e.method,void 0,e.response.result,void 0);i.sessionId&&(t["mcp.session.id"]=i.sessionId),i.conversationId&&(t["mcp.conversation.id"]=i.conversationId),i.userId&&(t["mcp.user.id"]=i.userId),i.clientName&&(t["mcp.client.name"]=i.clientName),i.clientVersion&&(t["mcp.client.version"]=i.clientVersion),i.permissionLevel&&(t["mcp.permission.level"]=i.permissionLevel),void 0!==i.cost&&(t["mcp.cost"]=i.cost),o(s,t)}catch(e){console.error("[MCP Logger] Error creating response-only span:",e),s.setStatus({code:h.ERROR,message:e+""})}finally{s.end()}})}({method:e.method||"same_request_method",source:"cli",transport:"stdio",response:{id:e.id,timestamp:t,result:e.result,error:e.error},contextMetadata:i})}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}m.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,s)=>{try{const r=!1;!function(e){if(S)return I;const s=e.endpoint||"https://aware.mcypher.com/v1/traces",r=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);q=new u({resource:new l({"service.name":r,"service.version":v}),traceExporter:new d({url:s,headers:{"x-api-key":e.apiKey}})}),q.start(),I=g.getTracer("mcp-logger",v),S=!0;const o=async()=>{await t()};process.once("SIGTERM",o),process.once("SIGINT",o)}({...s,debug:r}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[o,...n]=e;!function(e,s){C={command:e,args:s,env:{}}}(o,n);const i=p(o,n,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),c=new E(r),a=new L("request",c,r);process.stdin.pipe(a).pipe(i.stdin);const m=new L("response",c,r);i.stdout.pipe(m).pipe(process.stdout),i.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),c.clear(),await t(),process.exit(1)}),i.on("exit",async(e,s)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${s}`),c.clear(),await t(),process.exit(e||0)});let h=!1;const f=async e=>{h||(h=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),i.kill(e),await Promise.race([new Promise(e=>i.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await t(),process.exit(0))};process.on("SIGTERM",()=>f("SIGTERM")),process.on("SIGINT",()=>f("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await t(),process.exit(1)}}),m.parse();
@@ -1 +1 @@
1
- function e(e){return!t.includes(e)}import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const t=["notifications/initialized"];class r{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(t){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“₯ REQUEST: method="${t.method}", id=${t.id}, params=${JSON.stringify(t.params)?.slice(0,100)}`),e(t.method)){if(!t.id)return this.debug&&console.error(`[mcp-logger CLI] πŸ”” Notification detected: ${t.method} β†’ Creating request-only span`),void this.createRequestOnlySpan(t,Date.now());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)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+t.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“€ RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!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 this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} β†’ Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const r=Date.now(),o=r-t.timestamp;this.debug&&console.error(`[mcp-logger CLI] βœ… Pairing success: ${t.request.method} (${o}ms)`),this.createPairedSpan(t.request,e,t.timestamp,r,o),this.pendingRequests.delete(e.id)}onNotification(t){this.debug&&console.error(`[mcp-logger CLI] πŸ“’ NOTIFICATION (Serverβ†’Client): method="${t.method}", params=${JSON.stringify(t.params)?.slice(0,100)}`),e(t.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:t.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+t.method)}createPairedSpan(e,t,r,o,s){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}) β†’ Creating request-only span`),this.createRequestOnlySpan(t.request,t.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,t){e.method,e.id,e.params,console.error("[MCP Logger] Tracer not initialized, cannot create span")}createResponseOnlySpan(e,t){e.method,e.id,e.result,e.error,console.error("[MCP Logger] Tracer not initialized, cannot create span")}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}export{r as MCPMessageLogger};
1
+ function e(e){return!r.includes(e)}function t(e,t,r,p){const c=(g="unknown-0",s.has(g)||s.set(g,"session-"+ ++o),s.get(g)),d=function(e,t){const r=`${e}-${t}`;return i.has(r)||i.set(r,"conv-"+ ++n),i.get(r)}(c,e),u=function(e){if(e?.userId||e?.user_id)return e.userId||e.user_id}(t),l=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),m=function(e,t){return e.startsWith("resources/")?{type:e.split("/")[1],uri:t?.uri||t?.path||t?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:t?.name||t?.promptId}:"tools/call"===e?{type:"tool",uri:t?.name}:t?.resourceType&&t?.resourceUri?{type:t.resourceType,uri:t.resourceUri}:{type:void 0,uri:void 0}}(e,t);var g;m.uri&&function(e){const t=(a.get(e)||0)+1;a.set(e,t)}(m.uri);const h=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,r),I={};return c&&(I.sessionId=c),d&&(I.conversationId=d),u&&(I.userId=u),l&&(I.permissionLevel=l),void 0!==h&&(I.cost=h),I}import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const r=["notifications/initialized"],s=new Map;let o=0;const i=new Map;let n=0;const a=new Map;class p{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(t){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“₯ REQUEST: method="${t.method}", id=${t.id}, params=${JSON.stringify(t.params)?.slice(0,100)}`),e(t.method)){if(!t.id)return this.debug&&console.error(`[mcp-logger CLI] πŸ”” Notification detected: ${t.method} β†’ Creating request-only span`),void this.createRequestOnlySpan(t,Date.now());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)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+t.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] πŸ“€ RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!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 this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} β†’ Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const r=Date.now(),s=r-t.timestamp;this.debug&&console.error(`[mcp-logger CLI] βœ… Pairing success: ${t.request.method} (${s}ms)`),this.createPairedSpan(t.request,e,t.timestamp,r,s),this.pendingRequests.delete(e.id)}onNotification(t){this.debug&&console.error(`[mcp-logger CLI] πŸ“’ NOTIFICATION (Serverβ†’Client): method="${t.method}", params=${JSON.stringify(t.params)?.slice(0,100)}`),e(t.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:t.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+t.method)}createPairedSpan(e,r,s,o,i){t(e.method,e.params,r.result),e.method,e.id,e.params,r.result,r.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}) β†’ Creating request-only span`),this.createRequestOnlySpan(t.request,t.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,r){t(e.method,e.params,void 0),e.method,e.id,e.params,console.error("[MCP Logger] Tracer not initialized, cannot create span")}createResponseOnlySpan(e,r){t(e.method||"unknown",void 0,e.result),e.method,e.id,e.result,e.error,console.error("[MCP Logger] Tracer not initialized, cannot create span")}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}export{p as MCPMessageLogger};
@@ -100,6 +100,16 @@ interface MCPSpanData {
100
100
  'mcp.error'?: boolean;
101
101
  'mcp.error.code'?: number;
102
102
  'mcp.error.message'?: string;
103
+ 'mcp.session.id'?: string;
104
+ 'mcp.conversation.id'?: string;
105
+ 'mcp.user.id'?: string;
106
+ 'mcp.client.name'?: string;
107
+ 'mcp.client.version'?: string;
108
+ 'mcp.permission.level'?: string;
109
+ 'mcp.resource.type'?: string;
110
+ 'mcp.resource.uri'?: string;
111
+ 'mcp.resource.access_count'?: number;
112
+ 'mcp.cost'?: number;
103
113
  }
104
114
  /**
105
115
  * μ»€μŠ€ν…€ 둜그 μ—”νŠΈλ¦¬
@@ -122,6 +132,18 @@ interface CustomLogEntry {
122
132
  */
123
133
  timestamp?: number;
124
134
  }
135
+ /**
136
+ * MCP μ»¨ν…μŠ€νŠΈ 메타데이터
137
+ */
138
+ interface MCPContextMetadata {
139
+ sessionId?: string;
140
+ conversationId?: string;
141
+ userId?: string;
142
+ clientName?: string;
143
+ clientVersion?: string;
144
+ permissionLevel?: string;
145
+ cost?: number;
146
+ }
125
147
  /**
126
148
  * Paired Span μž…λ ₯ 데이터
127
149
  */
@@ -142,6 +164,7 @@ interface PairedSpanInput {
142
164
  };
143
165
  duration: number;
144
166
  customEvents?: CustomLogEntry[];
167
+ contextMetadata?: MCPContextMetadata;
145
168
  }
146
169
  /**
147
170
  * Response-only Span μž…λ ₯ 데이터 (notification λ“± responseκ°€ μ—†λŠ” 경우)
@@ -156,6 +179,7 @@ interface ResponseOnlySpanInput {
156
179
  result?: any;
157
180
  error?: any;
158
181
  };
182
+ contextMetadata?: MCPContextMetadata;
159
183
  }
160
184
  /**
161
185
  * Request-only Span μž…λ ₯ 데이터 (timeout λ“±μœΌλ‘œ responseκ°€ μ—†λŠ” 경우)
@@ -170,6 +194,7 @@ interface RequestOnlySpanInput {
170
194
  params: any;
171
195
  headers?: Record<string, string>;
172
196
  };
197
+ contextMetadata?: MCPContextMetadata;
173
198
  }
174
199
 
175
- export type { CustomLogEntry, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse, MCPSpanData, MessageDirection, PairedSpanInput, RequestOnlySpanInput, ResponseOnlySpanInput, SpanAttributes, TelemetryOptions };
200
+ export type { CustomLogEntry, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse, MCPContextMetadata, MCPSpanData, MessageDirection, PairedSpanInput, RequestOnlySpanInput, ResponseOnlySpanInput, SpanAttributes, TelemetryOptions };
package/dist/index.js CHANGED
@@ -1 +1 @@
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.source":r.source,"mcp.transport":r.transport,"mcp.request.id":r.request.id+"","mcp.request.method":r.method,"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.id":r.request.id+"","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){let t;o["mcp.error"]=!0,t=r.response.error instanceof Error?r.response.error.message:"string"==typeof r.response.error?r.response.error:s(r.response.error)||"Unknown 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="1.0.0";let u=null,g=null,d=!1,f=null;const h=Symbol("mcp-logger.customEvents"),v=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.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+t),console.error("[MCP Logger] Service: "+s)),u=new i({resource:new l({"service.name":s,"service.version":p}),traceExporter:new m({url:t,headers:{"x-api-key":e.apiKey}})}),u.start(),g=n.getTracer("mcp-logger",p),d=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await r()};process.once("SIGTERM",o),process.once("SIGINT",o)}(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 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] 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{v as trace};
1
+ function e(){return v}function r(){return h}async function t(){if(g)try{await g.shutdown(),g=null,v=null,f=!1,h=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(e,r){return e.startsWith("resources/")?{type:e.split("/")[1],uri:r?.uri||r?.path||r?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:r?.name||r?.promptId}:"tools/call"===e?{type:"tool",uri:r?.name}:r?.resourceType&&r?.resourceUri?{type:r.resourceType,uri:r.resourceUri}:{type:void 0,uri:void 0}}function n(e,r,t,s){const n=function(e){if(e){const r=e["user-agent"]||e["User-Agent"];if(r){const e=r.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(s),i=(p=`${n.name||"unknown"}-${n.version||"0"}`,y.has(p)||y.set(p,"session-"+ ++w),y.get(p)),c=function(e,r){const t=`${e}-${r}`;return q.has(t)||q.set(t,"conv-"+ ++E),q.get(t)}(i,e),a=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,s),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),u=o(e,r);var p;u.uri&&function(e){const r=(S.get(e)||0)+1;S.set(e,r)}(u.uri);const l=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,t),d={};return i&&(d.sessionId=i),c&&(d.conversationId=c),a&&(d.userId=a),n.name&&(d.clientName=n.name),n.version&&(d.clientVersion=n.version),m&&(d.permissionLevel=m),void 0!==l&&(d.cost=l),d}function i(t){const i=e();if(!i)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const c="mcp."+t.method;i.startActiveSpan(c,e=>{try{const i={"mcp.source":t.source,"mcp.transport":t.transport,"mcp.request.id":t.request.id+"","mcp.request.method":t.method,"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.id":t.request.id+"","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&&(i["mcp.tool.name"]=e),r){const e=s(r);e&&(i["mcp.tool.arguments"]=e,i["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==t.response.result){const e=s(t.response.result);e&&(i["mcp.response.result"]=e,i["mcp.response.result.size"]=JSON.stringify(t.response.result).length)}if(t.request.headers){const e=s(t.request.headers);e&&(i["mcp.headers"]=e,i["mcp.headers.size"]=JSON.stringify(t.request.headers).length)}if("cli"===t.source){const e=r();e&&(i["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}const c=t.contextMetadata||n(t.method,t.request.params,t.response.result,t.request.headers);c.sessionId&&(i["mcp.session.id"]=c.sessionId),c.conversationId&&(i["mcp.conversation.id"]=c.conversationId),c.userId&&(i["mcp.user.id"]=c.userId),c.clientName&&(i["mcp.client.name"]=c.clientName),c.clientVersion&&(i["mcp.client.version"]=c.clientVersion),c.permissionLevel&&(i["mcp.permission.level"]=c.permissionLevel),void 0!==c.cost&&(i["mcp.cost"]=c.cost);const m=function(e,r){const t=o(e,r);if(!t.uri)return{};const s=S.get(t.uri)||0;return{resourceType:t.type,resourceUri:t.uri,resourceAccessCount:s}}(t.method,t.request.params);if(m.resourceType&&(i["mcp.resource.type"]=m.resourceType),m.resourceUri&&(i["mcp.resource.uri"]=m.resourceUri),m.resourceAccessCount&&(i["mcp.resource.access_count"]=m.resourceAccessCount),t.customEvents&&t.customEvents.length>0&&(i["mcp.events"]=JSON.stringify(t.customEvents),i["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){let r;i["mcp.error"]=!0,r=t.response.error instanceof Error?t.response.error.message:"string"==typeof t.response.error?t.response.error:s(t.response.error)||"Unknown error",i["mcp.error.message"]=r,t.response.error instanceof Error&&e.recordException(t.response.error),e.setStatus({code:a.ERROR,message:r})}else e.setStatus({code:a.OK});!function(e,r){for(const[t,s]of Object.entries(r))null!=s&&e.setAttribute(t,s)}(e,i)}catch(r){console.error("[MCP Logger] Error creating paired span:",r),e.setStatus({code:a.ERROR,message:r+""})}finally{e.end()}})}import{trace as c,SpanStatusCode as a,context as m}from"@opentelemetry/api";import{NodeSDK as u}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as p}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as l}from"@opentelemetry/resources";const d="1.0.0";let g=null,v=null,f=!1,h=null;const y=new Map;let w=0;const q=new Map;let E=0;const S=new Map,I=Symbol("mcp-logger.customEvents"),M=Object.assign(function(e,r){if(!r.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(e){if(f)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),v;const r=e.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+r),console.error("[MCP Logger] Service: "+s)),g=new u({resource:new l({"service.name":s,"service.version":d}),traceExporter:new p({url:r,headers:{"x-api-key":e.apiKey}})}),g.start(),v=c.getTracer("mcp-logger",d),f=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await t()};process.once("SIGTERM",o),process.once("SIGINT",o)}(r),function(e,r){const t=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,s){const o=e.shape?.method,c=o?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+c),t(e,async(e,t)=>{const o=Date.now(),a=`${c}-${o}-${Math.random().toString(36).slice(2,8)}`,u=[],p=m.active().setValue(I,u);try{const l=await m.with(p,async()=>await s(e,t)),d=Date.now(),g=n(c,e.params,l,void 0);return i({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:d,result:l},duration:d-o,customEvents:u.length>0?u:void 0,contextMetadata:g}),r.debug&&console.log(`[mcp-logger] Request completed (${d-o}ms):`,{method:c,customEvents:u.length}),l}catch(t){const s=Date.now(),m=n(c,e.params,void 0,void 0);throw i({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:s,error:t},duration:s-o,customEvents:u.length>0?u:void 0,contextMetadata:m}),r.debug&&console.error("[mcp-logger] Request failed:",t),t}})},r.debug&&console.log("[mcp-logger] Server instrumentation complete")}(e,r),r.debug&&console.log("[mcp-logger] βœ“ Initialization complete")},{addLog(e){const r=m.active().getValue(I);if(!r)return void console.warn("[mcp-logger] addLog called outside of handler context");const t={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};r.push(t);const s=c.getSpan(m.active());s&&s.addEvent("custom."+t.level,{message:t.message,metadata:JSON.stringify(t.metadata||{}),timestamp:t.timestamp})},async shutdown(){await t()}});export{M as trace};
package/dist/sdk/index.js CHANGED
@@ -1 +1 @@
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.source":r.source,"mcp.transport":r.transport,"mcp.request.id":r.request.id+"","mcp.request.method":r.method,"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.id":r.request.id+"","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){let t;o["mcp.error"]=!0,t=r.response.error instanceof Error?r.response.error.message:"string"==typeof r.response.error?r.response.error:s(r.response.error)||"Unknown 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="1.0.0";let u=null,g=null,d=!1,f=null;const h=Symbol("mcp-logger.customEvents"),v=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.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+t),console.error("[MCP Logger] Service: "+s)),u=new i({resource:new l({"service.name":s,"service.version":p}),traceExporter:new m({url:t,headers:{"x-api-key":e.apiKey}})}),u.start(),g=n.getTracer("mcp-logger",p),d=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await r()};process.once("SIGTERM",o),process.once("SIGINT",o)}(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 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] 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{v as trace};
1
+ function e(){return v}function r(){return h}async function t(){if(g)try{await g.shutdown(),g=null,v=null,f=!1,h=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(e,r){return e.startsWith("resources/")?{type:e.split("/")[1],uri:r?.uri||r?.path||r?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:r?.name||r?.promptId}:"tools/call"===e?{type:"tool",uri:r?.name}:r?.resourceType&&r?.resourceUri?{type:r.resourceType,uri:r.resourceUri}:{type:void 0,uri:void 0}}function n(e,r,t,s){const n=function(e){if(e){const r=e["user-agent"]||e["User-Agent"];if(r){const e=r.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(s),i=(p=`${n.name||"unknown"}-${n.version||"0"}`,y.has(p)||y.set(p,"session-"+ ++w),y.get(p)),c=function(e,r){const t=`${e}-${r}`;return q.has(t)||q.set(t,"conv-"+ ++E),q.get(t)}(i,e),a=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,s),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),u=o(e,r);var p;u.uri&&function(e){const r=(S.get(e)||0)+1;S.set(e,r)}(u.uri);const l=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,t),d={};return i&&(d.sessionId=i),c&&(d.conversationId=c),a&&(d.userId=a),n.name&&(d.clientName=n.name),n.version&&(d.clientVersion=n.version),m&&(d.permissionLevel=m),void 0!==l&&(d.cost=l),d}function i(t){const i=e();if(!i)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const c="mcp."+t.method;i.startActiveSpan(c,e=>{try{const i={"mcp.source":t.source,"mcp.transport":t.transport,"mcp.request.id":t.request.id+"","mcp.request.method":t.method,"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.id":t.request.id+"","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&&(i["mcp.tool.name"]=e),r){const e=s(r);e&&(i["mcp.tool.arguments"]=e,i["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==t.response.result){const e=s(t.response.result);e&&(i["mcp.response.result"]=e,i["mcp.response.result.size"]=JSON.stringify(t.response.result).length)}if(t.request.headers){const e=s(t.request.headers);e&&(i["mcp.headers"]=e,i["mcp.headers.size"]=JSON.stringify(t.request.headers).length)}if("cli"===t.source){const e=r();e&&(i["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}const c=t.contextMetadata||n(t.method,t.request.params,t.response.result,t.request.headers);c.sessionId&&(i["mcp.session.id"]=c.sessionId),c.conversationId&&(i["mcp.conversation.id"]=c.conversationId),c.userId&&(i["mcp.user.id"]=c.userId),c.clientName&&(i["mcp.client.name"]=c.clientName),c.clientVersion&&(i["mcp.client.version"]=c.clientVersion),c.permissionLevel&&(i["mcp.permission.level"]=c.permissionLevel),void 0!==c.cost&&(i["mcp.cost"]=c.cost);const m=function(e,r){const t=o(e,r);if(!t.uri)return{};const s=S.get(t.uri)||0;return{resourceType:t.type,resourceUri:t.uri,resourceAccessCount:s}}(t.method,t.request.params);if(m.resourceType&&(i["mcp.resource.type"]=m.resourceType),m.resourceUri&&(i["mcp.resource.uri"]=m.resourceUri),m.resourceAccessCount&&(i["mcp.resource.access_count"]=m.resourceAccessCount),t.customEvents&&t.customEvents.length>0&&(i["mcp.events"]=JSON.stringify(t.customEvents),i["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){let r;i["mcp.error"]=!0,r=t.response.error instanceof Error?t.response.error.message:"string"==typeof t.response.error?t.response.error:s(t.response.error)||"Unknown error",i["mcp.error.message"]=r,t.response.error instanceof Error&&e.recordException(t.response.error),e.setStatus({code:a.ERROR,message:r})}else e.setStatus({code:a.OK});!function(e,r){for(const[t,s]of Object.entries(r))null!=s&&e.setAttribute(t,s)}(e,i)}catch(r){console.error("[MCP Logger] Error creating paired span:",r),e.setStatus({code:a.ERROR,message:r+""})}finally{e.end()}})}import{trace as c,SpanStatusCode as a,context as m}from"@opentelemetry/api";import{NodeSDK as u}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as p}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as l}from"@opentelemetry/resources";const d="1.0.0";let g=null,v=null,f=!1,h=null;const y=new Map;let w=0;const q=new Map;let E=0;const S=new Map,I=Symbol("mcp-logger.customEvents"),M=Object.assign(function(e,r){if(!r.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(e){if(f)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),v;const r=e.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);e.debug&&(console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+r),console.error("[MCP Logger] Service: "+s)),g=new u({resource:new l({"service.name":s,"service.version":d}),traceExporter:new p({url:r,headers:{"x-api-key":e.apiKey}})}),g.start(),v=c.getTracer("mcp-logger",d),f=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await t()};process.once("SIGTERM",o),process.once("SIGINT",o)}(r),function(e,r){const t=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,s){const o=e.shape?.method,c=o?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+c),t(e,async(e,t)=>{const o=Date.now(),a=`${c}-${o}-${Math.random().toString(36).slice(2,8)}`,u=[],p=m.active().setValue(I,u);try{const l=await m.with(p,async()=>await s(e,t)),d=Date.now(),g=n(c,e.params,l,void 0);return i({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:d,result:l},duration:d-o,customEvents:u.length>0?u:void 0,contextMetadata:g}),r.debug&&console.log(`[mcp-logger] Request completed (${d-o}ms):`,{method:c,customEvents:u.length}),l}catch(t){const s=Date.now(),m=n(c,e.params,void 0,void 0);throw i({method:c,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:s,error:t},duration:s-o,customEvents:u.length>0?u:void 0,contextMetadata:m}),r.debug&&console.error("[mcp-logger] Request failed:",t),t}})},r.debug&&console.log("[mcp-logger] Server instrumentation complete")}(e,r),r.debug&&console.log("[mcp-logger] βœ“ Initialization complete")},{addLog(e){const r=m.active().getValue(I);if(!r)return void console.warn("[mcp-logger] addLog called outside of handler context");const t={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};r.push(t);const s=c.getSpan(m.active());s&&s.addEvent("custom."+t.level,{message:t.message,metadata:JSON.stringify(t.metadata||{}),timestamp:t.timestamp})},async shutdown(){await t()}});export{M as trace};
@@ -1 +1 @@
1
- function e(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function t(t,o){const c=t.setRequestHandler.bind(t);t.setRequestHandler=function(t,s){const a=t.shape?.method,l=a?._def?.value||"unknown";return o.debug&&console.log("[mcp-logger] Instrumenting handler: "+l),c(t,async(t,c)=>{const a=Date.now(),m=(Math.random().toString(36).slice(2,8),[]),i=n.active().setValue(r,m);try{const r=await n.with(i,async()=>await s(t,c)),p=Date.now();return e(t.params),o.debug&&console.log(`[mcp-logger] Request completed (${p-a}ms):`,{method:l,customEvents:m.length}),r}catch(n){throw e(t.params),o.debug&&console.error("[mcp-logger] Request failed:",n),n}})},o.debug&&console.log("[mcp-logger] Server instrumentation complete")}function o(){return n.active().getValue(r)}import{context as n}from"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const r=Symbol("mcp-logger.customEvents");export{o as getCustomEventsFromContext,t as instrumentMCPServer};
1
+ function e(e,t,o,r){const n=(g="unknown-0",s.has(g)||s.set(g,"session-"+ ++i),s.get(g)),p=function(e,t){const o=`${e}-${t}`;return c.has(o)||c.set(o,"conv-"+ ++u),c.get(o)}(n,e),l=function(e){if(e?.userId||e?.user_id)return e.userId||e.user_id}(t),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),d=function(e,t){return e.startsWith("resources/")?{type:e.split("/")[1],uri:t?.uri||t?.path||t?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:t?.name||t?.promptId}:"tools/call"===e?{type:"tool",uri:t?.name}:t?.resourceType&&t?.resourceUri?{type:t.resourceType,uri:t.resourceUri}:{type:void 0,uri:void 0}}(e,t);var g;d.uri&&function(e){const t=(a.get(e)||0)+1;a.set(e,t)}(d.uri);const v=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,o),f={};return n&&(f.sessionId=n),p&&(f.conversationId=p),l&&(f.userId=l),m&&(f.permissionLevel=m),void 0!==v&&(f.cost=v),f}function t(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function o(o,r){const s=o.setRequestHandler.bind(o);o.setRequestHandler=function(o,i){const c=o.shape?.method,u=c?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+u),s(o,async(o,s)=>{const c=Date.now(),a=(Math.random().toString(36).slice(2,8),[]),l=n.active().setValue(p,a);try{const p=await n.with(l,async()=>await i(o,s)),m=Date.now();return e(u,o.params,p),t(o.params),r.debug&&console.log(`[mcp-logger] Request completed (${m-c}ms):`,{method:u,customEvents:a.length}),p}catch(n){throw e(u,o.params,void 0),t(o.params),r.debug&&console.error("[mcp-logger] Request failed:",n),n}})},r.debug&&console.log("[mcp-logger] Server instrumentation complete")}function r(){return n.active().getValue(p)}import{context as n}from"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";const s=new Map;let i=0;const c=new Map;let u=0;const a=new Map,p=Symbol("mcp-logger.customEvents");export{r as getCustomEventsFromContext,o as instrumentMCPServer};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awarecorp/mcp-logger",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
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",
@@ -28,7 +28,11 @@
28
28
  "version:patch": "npm version patch",
29
29
  "version:minor": "npm version minor",
30
30
  "version:major": "npm version major",
31
- "version:dev": "npm version prerelease --preid=dev"
31
+ "version:dev": "npm version prerelease --preid=dev",
32
+ "version:stable": "node -e \"const pkg=require('./package.json'); pkg.version=pkg.version.replace(/-dev\\.\\d+$/,''); require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\n'); console.log('Version updated to',pkg.version)\"",
33
+ "version:next-dev:patch": "node -e \"const pkg=require('./package.json'); if(pkg.version.includes('-dev')){console.error('Error: version:next-dev should only be used on stable versions');console.error('Current version:',pkg.version);console.error('Run \\\"npm run version:stable\\\" first');process.exit(1);} const match=pkg.version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/); const newVer=match[1]+'.'+match[2]+'.'+(parseInt(match[3])+1)+'-dev.0'; pkg.version=newVer; require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\n'); console.log('Version updated to',newVer)\"",
34
+ "version:next-dev:minor": "node -e \"const pkg=require('./package.json'); if(pkg.version.includes('-dev')){console.error('Error: version:next-dev should only be used on stable versions');console.error('Current version:',pkg.version);console.error('Run \\\"npm run version:stable\\\" first');process.exit(1);} const match=pkg.version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/); const newVer=match[1]+'.'+(parseInt(match[2])+1)+'.0-dev.0'; pkg.version=newVer; require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\n'); console.log('Version updated to',newVer)\"",
35
+ "version:next-dev:major": "node -e \"const pkg=require('./package.json'); if(pkg.version.includes('-dev')){console.error('Error: version:next-dev should only be used on stable versions');console.error('Current version:',pkg.version);console.error('Run \\\"npm run version:stable\\\" first');process.exit(1);} const match=pkg.version.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/); const newVer=(parseInt(match[1])+1)+'.0.0-dev.0'; pkg.version=newVer; require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\n'); console.log('Version updated to',newVer)\""
32
36
  },
33
37
  "keywords": [
34
38
  "mcp",