@awarecorp/mcp-logger 0.0.3-dev.0 β†’ 0.0.3-dev.1

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
@@ -65,7 +65,7 @@ npx @awarecorp/mcp-logger \
65
65
  - **Selective Method Tracking**: Configurable filtering to track only relevant methods (excludes noisy notifications like `notifications/initialized`)
66
66
  - **Custom Event Logging**: Add context-aware logs within request handlers
67
67
  - **Enhanced Error Handling**: Robust error serialization for complex error objects
68
- - **API Key Validation**: Secure endpoint validation with SHA-256 hashing
68
+ - **Custom OTLP Endpoint**: Send traces to any OTLP/HTTP compatible observability backend
69
69
  - **Transport Support**: stdio and SSE (Server-Sent Events)
70
70
  - **Zero Configuration**: Works out of the box with sensible defaults
71
71
 
@@ -105,8 +105,12 @@ Enable automatic instrumentation on an MCP server.
105
105
  **Parameters:**
106
106
  - `server`: MCP Server instance
107
107
  - `options.apiKey`: API key for authentication (required)
108
+ - Currently accepts any string value for testing purposes
109
+ - For production use, sign up at [Aware](https://awarecorp.io/) to get your API key
108
110
  - `options.serviceName`: Service identifier (optional, auto-generated if omitted)
109
- - `options.endpoint`: Custom OTLP endpoint (optional)
111
+ - `options.endpoint`: Custom OTLP endpoint (optional, default: `https://aware.mcypher.com/v1/traces`)
112
+ - Accepts any OTLP/HTTP compatible endpoint URL
113
+ - Use this to send traces to your own observability backend (e.g., Jaeger, Grafana, etc.)
110
114
  - `options.debug`: Enable debug logging (optional, default: false)
111
115
 
112
116
  **Returns:** `void`
@@ -142,8 +146,12 @@ mcp-logger [options] <command...>
142
146
  ### Options
143
147
 
144
148
  - `-k, --api-key <key>`: API key (required)
149
+ - Currently accepts any string value for testing
150
+ - Sign up at [Aware](https://awarecorp.io/) to get your API key for production use
145
151
  - `-s, --service-name <name>`: Service name (optional)
146
- - `-e, --endpoint <url>`: Custom OTLP endpoint (optional)
152
+ - `-e, --endpoint <url>`: Custom OTLP endpoint (optional, default: `https://aware.mcypher.com/v1/traces`)
153
+ - Send traces to any OTLP/HTTP compatible backend
154
+ - Examples: Jaeger, Grafana Tempo, New Relic, Honeycomb, etc.
147
155
 
148
156
  ### Examples
149
157
 
package/dist/cli/main.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- function e(e){return!y.includes(e)}function r(){return S}function s(){return v}async function t(){if(q)try{await q.shutdown(),q=null,S=null,C=!1,v=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 l,SpanStatusCode as d}from"@opentelemetry/api";import g from"crypto";import{Transform as u}from"stream";const h="https://aware.mcypher.com/v1/traces",f="1.0.0",y=["notifications/initialized"];let q=null,S=null,C=!1,v=null;class L extends u{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 R{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=!0;(function(e){if(C)return console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),S;let r=e.endpoint||h;var s;s=e.apiKey,"34f17c05db5043bd9c066b2792924876de5ab515d7176c9996e47328aea49297"!==g.createHash("sha256").update(s).digest("hex")&&e.endpoint&&(console.error("[MCP Logger] Warning: Custom endpoint requires internal API key"),console.error("[MCP Logger] Using default endpoint:",h),r=h);const o=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+r),console.error("[MCP Logger] Service: "+o),q=new a({resource:new m({"service.name":o,"service.version":f}),traceExporter:new p({url:r,headers:{"x-api-key":e.apiKey}})}),q.start(),S=l.getTracer("mcp-logger",f),C=!0,console.error("[MCP Logger] Telemetry initialized successfully");const n=async()=>{console.error("[MCP Logger] Shutting down telemetry..."),await t()};process.once("SIGTERM",n),process.once("SIGINT",n)})({...r,debug:s}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[o,...n]=e;!function(e,r){v={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 R(s),u=new L("request",d,s);process.stdin.pipe(u).pipe(c.stdin);const y=new L("response",d,s);c.stdout.pipe(y).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 I=!1;const O=async e=>{I||(I=!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!h.includes(e)}function r(){return y}function s(){return q}async function t(){if(f)try{await f.shutdown(),f=null,y=null,S=!1,q=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 l,SpanStatusCode as g}from"@opentelemetry/api";import{Transform as u}from"stream";const d="1.0.0",h=["notifications/initialized"];let f=null,y=null,S=!1,q=null;class C extends u{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 v{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:g.ERROR,message:s})}else r.setStatus({code:g.OK});o(r,t)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:g.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:g.OK}),o(r,t)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),r.setStatus({code:g.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:g.ERROR,message:s})}else r.setStatus({code:g.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:g.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=!0;!function(e){if(S)return console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),y;const r=e.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);console.error("[MCP Logger] Initializing telemetry..."),console.error("[MCP Logger] Endpoint: "+r),console.error("[MCP Logger] Service: "+s),f=new a({resource:new m({"service.name":s,"service.version":d}),traceExporter:new p({url:r,headers:{"x-api-key":e.apiKey}})}),f.start(),y=l.getTracer("mcp-logger",d),S=!0,console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{console.error("[MCP Logger] Shutting down telemetry..."),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){q={command:e,args:r,env:{}}}(o,n);const c=i(o,n,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),g=new v(s),u=new C("request",g,s);process.stdin.pipe(u).pipe(c.stdin);const h=new C("response",g,s);c.stdout.pipe(h).pipe(process.stdout),c.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),g.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}`),g.clear(),await t(),process.exit(e||0)});let R=!1;const I=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",()=>I("SIGTERM")),process.on("SIGINT",()=>I("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await t(),process.exit(1)}}),c.parse();
@@ -1 +1 @@
1
- function e(e){return!t.includes(e)}import"crypto";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!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 +1 @@
1
- function e(e){return!a.includes(e)}function t(e){return c.createHash("sha256").update(e).digest("hex")===r.INTERNAL_KEY_HASH}import c from"crypto";const r={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server",INTERNAL_KEY_HASH:"34f17c05db5043bd9c066b2792924876de5ab515d7176c9996e47328aea49297"},a=["notifications/initialized"];export{r as CONFIG,t as isInternalApiKey,e as shouldTrackMethod};
1
+ function t(t){return!i.includes(t)}const e={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:"1.0.0",SERVICE_NAME_PREFIX:"mcp-server"},i=["notifications/initialized"];export{e as CONFIG,t as shouldTrackMethod};
@@ -1 +1 @@
1
- function e(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function r(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function o(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";import"crypto";export{e as createPairedSpan,r as createRequestOnlySpan,o as createResponseOnlySpan};
1
+ function e(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function r(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function o(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,r as createRequestOnlySpan,o as createResponseOnlySpan};
@@ -1 +1 @@
1
- function e(e){if(h)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),f;let r=e.endpoint||u;var o;o=e.apiKey,g.createHash("sha256").update(o).digest("hex")!==d&&e.endpoint&&(console.error("[MCP Logger] Warning: Custom endpoint requires internal API key"),console.error("[MCP Logger] Using default endpoint:",u),r=u);const t=e.serviceName||`${m}-${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: "+t)),y=new c({resource:new l({"service.name":t,"service.version":p}),traceExporter:new s({url:r,headers:{"x-api-key":e.apiKey}})}),y.start(),f=a.getTracer("mcp-logger",p),h=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const n=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await i()};return process.once("SIGTERM",n),process.once("SIGINT",n),f}function r(){return f}function o(){return h}function t(e,r,o){M={command:e,args:r,env:o}}function n(){return M}async function i(){if(y)try{await y.shutdown(),y=null,f=null,h=!1,M=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}import{NodeSDK as c}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as s}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as l}from"@opentelemetry/resources";import{trace as a}from"@opentelemetry/api";import g from"crypto";const u="https://aware.mcypher.com/v1/traces",p="1.0.0",m="mcp-server",d="34f17c05db5043bd9c066b2792924876de5ab515d7176c9996e47328aea49297";let y=null,f=null,h=!1,M=null;export{n as getCLIServerInfo,r as getTracer,e as initializeTelemetry,o as isInitializedTelemetry,t as setCLIServerInfo,i 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.endpoint||u,o=e.serviceName||`${m}-${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: "+o)),p=new i({resource:new s({"service.name":o,"service.version":g}),traceExporter:new l({url:r,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 t=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await c()};return process.once("SIGTERM",t),process.once("SIGINT",t),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 c(){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 i}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,c as shutdownTelemetry};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- function e(){return f}function t(){return v}async function r(){if(d)try{await d.shutdown(),d=null,f=null,h=!1,v=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 p}from"@opentelemetry/resources";import l from"crypto";const u="https://aware.mcypher.com/v1/traces",g="1.0.0";let d=null,f=null,h=!1,v=null;const y=Symbol("mcp-logger.customEvents"),q=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(h)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),f;let t=e.endpoint||u;var s;s=e.apiKey,"34f17c05db5043bd9c066b2792924876de5ab515d7176c9996e47328aea49297"!==l.createHash("sha256").update(s).digest("hex")&&e.endpoint&&(console.error("[MCP Logger] Warning: Custom endpoint requires internal API key"),console.error("[MCP Logger] Using default endpoint:",u),t=u);const o=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: "+o)),d=new i({resource:new p({"service.name":o,"service.version":g}),traceExporter:new m({url:t,headers:{"x-api-key":e.apiKey}})}),d.start(),f=n.getTracer("mcp-logger",g),h=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const a=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await r()};process.once("SIGTERM",a),process.once("SIGINT",a)})(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=[],p=c.active().setValue(y,m);try{const l=await c.with(p,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:l},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}),l}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(y);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{q 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.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};
package/dist/sdk/index.js CHANGED
@@ -1 +1 @@
1
- function e(){return f}function t(){return v}async function r(){if(d)try{await d.shutdown(),d=null,f=null,h=!1,v=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 p}from"@opentelemetry/resources";import l from"crypto";const u="https://aware.mcypher.com/v1/traces",g="1.0.0";let d=null,f=null,h=!1,v=null;const y=Symbol("mcp-logger.customEvents"),q=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(h)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),f;let t=e.endpoint||u;var s;s=e.apiKey,"34f17c05db5043bd9c066b2792924876de5ab515d7176c9996e47328aea49297"!==l.createHash("sha256").update(s).digest("hex")&&e.endpoint&&(console.error("[MCP Logger] Warning: Custom endpoint requires internal API key"),console.error("[MCP Logger] Using default endpoint:",u),t=u);const o=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: "+o)),d=new i({resource:new p({"service.name":o,"service.version":g}),traceExporter:new m({url:t,headers:{"x-api-key":e.apiKey}})}),d.start(),f=n.getTracer("mcp-logger",g),h=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const a=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await r()};process.once("SIGTERM",a),process.once("SIGINT",a)})(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=[],p=c.active().setValue(y,m);try{const l=await c.with(p,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:l},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}),l}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(y);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{q 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.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 +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";import"crypto";const r=Symbol("mcp-logger.customEvents");export{o as getCustomEventsFromContext,t as instrumentMCPServer};
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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awarecorp/mcp-logger",
3
- "version": "0.0.3-dev.0",
3
+ "version": "0.0.3-dev.1",
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",