@awarecorp/mcp-logger 0.0.10 → 0.0.11

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/dist/cli/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- function e(...e){console.error("[mcp-logger]",...e)}function t(...e){x&&console.error("[mcp-logger]",...e)}function s(...e){E&&console.error("[mcp-logger]",...e)}function r(e){return!N.includes(e)}async function n(){await R.shutdown()}function o(e,t=2e5){try{const s=JSON.stringify(e);return s.length>t?s.slice(0,t)+"... (truncated)":s}catch{return null}}function i(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}}function c(e,t,s,r,n){const o=function(e,t){const s=e?.getClientInfo();if(s?.name||s?.version)return{name:s.name,version:s.version};const r=e?.getHeaders()||t;if(r){const e=r["user-agent"]||r["User-Agent"];if(e){const t=e.match(/^([^\/]+)\/([^\s]+)/);if(t)return{name:t[1],version:t[2]}}}return{name:void 0,version:void 0}}(r,n),c=`${o.name||"unknown"}-${o.version||"0"}`,a=T.getOrCreateSessionId(c),p=T.getOrCreateConversationId(a,e),u=function(e,t){return e?.userId||e?.user_id?e.userId||e.user_id:t?t["x-user-id"]||t["X-User-Id"]:void 0}(t,n),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),d=i(e,t);d.uri&&T.trackResourceAccess(d.uri);const l=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,s),h={};return a&&(h.sessionId=a),p&&(h.conversationId=p),u&&(h.userId=u),o.name&&(h.clientName=o.name),o.version&&(h.clientVersion=o.version),m&&(h.permissionLevel=m),void 0!==l&&(h.cost=l),h}function a(e){const t=R.getTracer();if(!t)return;const r="mcp."+e.method;t.startActiveSpan(r,t=>{try{const r={"ingest.type":"mcp","mcp.type":e.type,"mcp.transport":e.transport,"mcp.method":e.method,"mcp.source":e.source},n=e.contextMetadata||c(e.method,e.params,e.result,void 0,e.headers);if(n.sessionId&&n.conversationId&&(r["mcp.trace.id"]=`${n.sessionId}:${n.conversationId}`),"paired"===e.type||"request"===e.type){if(void 0!==e.requestId&&(r["mcp.request.id"]=e.requestId+""),r["mcp.request.method"]=e.method,void 0!==e.requestTimestamp&&(r["mcp.request.timestamp"]=e.requestTimestamp),void 0!==e.params&&(r["mcp.request.params"]=o(e.params)||"{}",r["mcp.request.params.size"]=JSON.stringify(e.params||{}).length),"tools/call"===e.method&&e.params){const t=e.params.name,s=e.params.arguments;if(t&&(r["mcp.tool.name"]=t),s){const e=o(s);e&&(r["mcp.tool.arguments"]=e,r["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if(e.headers){const t=o(e.headers);t&&(r["mcp.headers"]=t,r["mcp.headers.size"]=JSON.stringify(e.headers).length)}}if(("paired"===e.type||"response"===e.type)&&(void 0!==e.responseId&&(r["mcp.response.id"]=e.responseId+""),r["mcp.response.method"]=e.method,void 0!==e.responseTimestamp&&(r["mcp.response.timestamp"]=e.responseTimestamp),void 0!==e.result)){const t=o(e.result);t&&(r["mcp.response.result"]=t,r["mcp.response.result.size"]=JSON.stringify(e.result).length)}void 0!==e.durationMs&&(r["mcp.duration_ms"]=e.durationMs);const a=R.getCLIServerInfo();a&&(r["mcp.cli"]=JSON.stringify({command:a.command,args:a.args,env:a.env})),e.serverInfo&&(e.serverInfo.name&&(r["mcp.sdk.name"]=e.serverInfo.name),e.serverInfo.version&&(r["mcp.sdk.version"]=e.serverInfo.version)),n.sessionId&&(r["mcp.session.id"]=n.sessionId),n.conversationId&&(r["mcp.conversation.id"]=n.conversationId),n.userId&&(r["mcp.user.id"]=n.userId),n.clientName&&(r["mcp.client.name"]=n.clientName),n.clientVersion&&(r["mcp.client.version"]=n.clientVersion),n.permissionLevel&&(r["mcp.permission.level"]=n.permissionLevel),void 0!==n.cost&&(r["mcp.cost"]=n.cost);const p=function(e,t){const s=i(e,t);if(!s.uri)return{};const r=T.getResourceAccessCount(s.uri);return{resourceType:s.type,resourceUri:s.uri,resourceAccessCount:r}}(e.method,e.params);if(p.resourceType&&(r["mcp.resource.type"]=p.resourceType),p.resourceUri&&(r["mcp.resource.uri"]=p.resourceUri),p.resourceAccessCount&&(r["mcp.resource.access_count"]=p.resourceAccessCount),e.customEvents&&e.customEvents.length>0&&(r["mcp.events"]=JSON.stringify(e.customEvents),r["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{t.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.error){let s;r["mcp.error"]=!0,e.error instanceof Error?(s=e.error.message,t.recordException(e.error)):s="string"==typeof e.error?e.error:o(e.error)||"Unknown error",r["mcp.error.message"]=s,t.setStatus({code:v.ERROR,message:s})}else t.setStatus({code:v.OK});!function(e,t){for(const[s,r]of Object.entries(t))null!=r&&e.setAttribute(s,r)}(t,r),s("[OTLP] Span data:",JSON.stringify(r,null,2))}catch{t.setStatus({code:v.ERROR,message:"Error creating span"})}finally{t.end()}})}import{execSync as p,spawn as u}from"child_process";import{program as m}from"commander";import{NodeSDK as d}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as l}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as h}from"@opentelemetry/resources";import{trace as f,SpanStatusCode as v}from"@opentelemetry/api";import{readFileSync as g}from"fs";import{fileURLToPath as I}from"url";import{dirname as y,join as S}from"path";import{Transform as C}from"stream";import{ulid as w}from"ulid";const x="true"===process.env.AWARE_DEBUG||"1"===process.env.AWARE_DEBUG,E="true"===process.env.AWARE_RAW_DEBUG||"1"===process.env.AWARE_RAW_DEBUG,M={ENDPOINT:"https://api.awarecorp.io/traces",SDK_VERSION:function(){try{const e=I(import.meta.url),t=y(e),s=S(t,"..","..","package.json");return JSON.parse(g(s,"utf-8")).version||"1.0.0"}catch{return"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},N=["notifications/initialized"],R=new class{sdk=null;tracer=null;initialized=!1;cliServerInfo=null;initialize(e){if(this.initialized)return t("Telemetry already initialized, returning existing tracer"),this.tracer;const s=e.endpoint||M.ENDPOINT,r=e.serviceName||`${M.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`,n=e.serviceVersion||"unknown";t("Initializing telemetry..."),t("Endpoint: "+s),t("Service: "+r),t("Version: "+n),this.sdk=new d({resource:new h({"service.name":r,"service.version":n}),traceExporter:new l({url:s,headers:{"x-api-key":e.apiKey}})}),this.sdk.start(),this.tracer=f.getTracer("mcp-logger",M.SDK_VERSION),this.initialized=!0,t("Telemetry initialized successfully");const o=async()=>{t("Shutting down telemetry..."),await this.shutdown()};return process.once("SIGTERM",o),process.once("SIGINT",o),this.tracer}getTracer(){return this.tracer}isInitialized(){return this.initialized}setCLIServerInfo(e,t,s){this.cliServerInfo={command:e,args:t,env:s}}getCLIServerInfo(){return this.cliServerInfo}async shutdown(){if(this.sdk)try{await this.sdk.shutdown(),this.sdk=null,this.tracer=null,this.initialized=!1,this.cliServerInfo=null}catch(e){console.error("[mcp-logger] Error shutting down telemetry:",e)}}};class q extends C{buffer="";direction;logger;sessionContextStore;constructor(e,t,s){super(),this.direction=e,this.logger=t,this.sessionContextStore=s}_transform(e,s,r){try{if("request"===this.direction)return this.push(e),this.buffer+=e.toString(),this.tryParseMessages(),void r();this.buffer+=e.toString(),this.buffer.length>1e6&&(t("Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseAndForwardMessages(),r()}catch(e){r(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 t=JSON.parse(e);s(`[${this.direction}] Raw message:`,e),this.handleMessage(t)}catch{t("Failed to parse:",e.slice(0,100))}}}tryParseAndForwardMessages(){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);"2.0"===r.jsonrpc?(this.push(e+"\n"),s(`[${this.direction}] Raw message:`,e),this.handleMessage(r)):t("Non-JSON-RPC JSON ignored:",e.slice(0,100))}catch{t("Non-JSON output filtered:",e.slice(0,100))}}}isValidInfo(e){return!(!e?.name||!e?.version)}handleMessage(e){if("request"===this.direction&&"initialize"===e.method&&e.params?.clientInfo&&!this.isValidInfo(this.sessionContextStore.getClientInfo())){const t=e.params;this.sessionContextStore.setClientInfo({name:t.clientInfo?.name,version:t.clientInfo?.version})}if("response"===this.direction&&void 0!==e.id&&!e.method){const t=e;if(t.result?.serverInfo&&!this.isValidInfo(this.sessionContextStore.getServerInfo())){const e=t.result.serverInfo;this.sessionContextStore.setServerInfo({name:e.name,version:e.version})}}"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&null==e.id&&this.logger.onNotification(e):this.logger.onResponse(e))}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);"response"===this.direction&&"2.0"===e.jsonrpc&&this.push(this.buffer.trim()+"\n"),this.handleMessage(e)}catch{"request"===this.direction&&this.push(this.buffer),t("Failed to parse final buffer")}x&&t("Stream closed. Pending requests: "+this.logger.getStats().pending),e()}catch(t){e(t)}}}const T=new class{sessionMap=new Map;sessionCounter=0;conversationMap=new Map;conversationCounter=0;resourceAccessCount=new Map;getOrCreateSessionId(e){if(!this.sessionMap.has(e)){const t=w();this.sessionMap.set(e,t),this.sessionCounter++}return this.sessionMap.get(e)}getOrCreateConversationId(e,t){const s=`${e}-${t}`;return this.conversationMap.has(s)||this.conversationMap.set(s,"conv-"+ ++this.conversationCounter),this.conversationMap.get(s)}trackResourceAccess(e){const t=(this.resourceAccessCount.get(e)||0)+1;return this.resourceAccessCount.set(e,t),t}getResourceAccessCount(e){return this.resourceAccessCount.get(e)||0}clear(){this.sessionMap.clear(),this.conversationMap.clear(),this.resourceAccessCount.clear(),this.sessionCounter=0,this.conversationCounter=0}};class A{pendingRequests=new Map;TIMEOUT=3e4;sessionContextStore;constructor(e){this.sessionContextStore=e}onRequest(e){if(r(e.method)){if(null==e.id){const t=c(e.method,e.params,void 0,this.sessionContextStore,void 0);return void a({type:"response",method:e.method,transport:"stdio",source:"cli",params:e.params,contextMetadata:t})}"initialize"===e.method&&e.params?.clientInfo&&this.sessionContextStore.setClientInfo({name:e.params.clientInfo.name,version:e.params.clientInfo.version}),this.pendingRequests.set(e.id,{request:e,timestamp:Date.now()}),setTimeout(()=>this.handleTimeout(e.id),this.TIMEOUT)}}onResponse(e){if(null==e.id)return;const t=this.pendingRequests.get(e.id),s=Date.now(),r=t?.request.method||e.method||"unknown";let n=this.sessionContextStore.getServerInfo();"initialize"===t?.request.method&&e.result?.serverInfo&&(n={name:e.result.serverInfo.name,version:e.result.serverInfo.version});const o=c(r,t?.request.params,e.result,this.sessionContextStore,void 0);t?(a({type:"paired",method:r,transport:"stdio",source:"cli",requestId:t.request.id,requestTimestamp:t.timestamp,params:t.request.params,responseId:e.id,responseTimestamp:s,result:e.result,error:e.error,durationMs:s-t.timestamp,contextMetadata:o,serverInfo:n}),this.pendingRequests.delete(e.id)):a({type:"response",method:r,transport:"stdio",source:"cli",responseId:e.id,responseTimestamp:s,result:e.result,error:e.error,contextMetadata:o,serverInfo:n})}onNotification(e){if(!r(e.method))return;const t=c(e.method,e.params,void 0,this.sessionContextStore,void 0);a({type:"response",method:e.method,transport:"stdio",source:"cli",params:e.params,contextMetadata:t})}handleTimeout(e){const t=this.pendingRequests.get(e);if(t&&Date.now()-t.timestamp>this.TIMEOUT){const s=c(t.request.method,t.request.params,void 0,this.sessionContextStore,void 0);a({type:"request",method:t.request.method,transport:"stdio",source:"cli",requestId:t.request.id,requestTimestamp:t.timestamp,params:t.request.params,contextMetadata:s}),this.pendingRequests.delete(e)}}getStats(){return{pending:this.pendingRequests.size}}clear(){this.pendingRequests.clear()}}class O{context={};getClientInfo(){return this.context.clientInfo}setClientInfo(e){this.context.clientInfo=e}getServerInfo(){return this.context.serverInfo}setServerInfo(e){this.context.serverInfo=e}getHeaders(){return this.context.headers}setHeaders(e){this.context.headers=e}snapshot(){return{clientInfo:this.context.clientInfo?{...this.context.clientInfo}:void 0,serverInfo:this.context.serverInfo?{...this.context.serverInfo}:void 0,headers:this.context.headers?{...this.context.headers}:void 0}}}const k=new Set(["npx","node","uvx","python","python3","pip","pipx","deno","bun"]),P=new Set(["uvx","python","python3","pip","pipx"]);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("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(s,r)=>{try{const o=function(e){const t={};for(const s of e){const e=process.env[s];void 0!==e&&(t[s]=e)}return t}(function(){try{return p("aware mcpenv sync",{encoding:"utf-8",timeout:1e4,stdio:["pipe","pipe","pipe"]}),p("aware mcpenv keys",{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).trim().split("\n").filter(e=>e.length>0)}catch{return[]}}()),[i,...c]=s,a=function(e,t){const s=function(e){for(const t of e){if(k.has(t))continue;const e=t.match(/^(@[^@]+\/[^@]+)@(.+)$/);if(e)return{packageName:e[1],version:e[2]};const s=t.match(/^([^@]+)@(.+)$/);if(s)return{packageName:s[1],version:s[2]};const r=t.match(/^(@[^@]+\/[^@]+)$/);if(r)return{packageName:r[1],version:"latest"};if(t.match(/^[a-z0-9-]+$/)&&!t.includes("/"))return{packageName:t,version:"latest"}}return null}(t.filter(e=>!e.startsWith("-")));let r,n;if(r=s?.packageName?s.packageName:e.split("/").pop()||e||M.SERVICE_NAME_PREFIX+"-unknown",s&&(n=s.version,"latest"===s.version)){const t=P.has(e)?"pip":"npm",r=function(e,t){try{if("pip"===t){let t=null;for(const s of["pip3","pip"])try{t=p(`${s} index versions ${e}`,{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]});break}catch{continue}if(!t)return null;const s=t.match(/\(([^)]+)\)/);if(s)return s[1];const r=t.match(/Available versions:\s*([^\s,]+)/);return r?r[1]:null}{const t=p(`npm view ${e} version --json`,{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).trim().replace(/^["']|["']$/g,""),s=t.match(/^(\d+\.\d+\.\d+(?:-[\w.]+)?)/);return s?s[1]:t}}catch{return null}}(s.packageName,t);r&&(n=r)}return{name:r,version:n}}(i,c);!function(e){R.initialize(e)}({...r,serviceName:a.name,serviceVersion:a.version}),e("Starting MCP server: "+s.join(" ")),e(`Service: ${a.name} = ${a.version||"unknown"}`),function(e,t,s){R.setCLIServerInfo(e,t,s)}(i,c,o);const m=u(i,c,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),d=new O,l=new A(d),h=new q("request",l,d);process.stdin.pipe(h).pipe(m.stdin);const f=new q("response",l,d);m.stdout.pipe(f).pipe(process.stdout),m.on("error",async t=>{e("Failed to start MCP server:",t),l.clear(),await n(),process.exit(1)}),m.on("exit",async(e,s)=>{t(`MCP server exited with code ${e}, signal ${s}`),l.clear(),await n(),process.exit(e||0)});let v=!1;const g=async e=>{v||(v=!0,t(`Received ${e}, shutting down...`),m.kill(e),await Promise.race([new Promise(e=>m.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await n(),process.exit(0))};process.on("SIGTERM",()=>g("SIGTERM")),process.on("SIGINT",()=>g("SIGINT"))}catch(t){e("Fatal error:",t),await n(),process.exit(1)}}),m.parse();
2
+ function t(...t){console.error("[mcp-logger]",...t)}function e(...t){M&&console.error("[mcp-logger]",...t)}function s(...t){C&&console.error("[mcp-logger]",...t)}function i(t){return!D.includes(t)}function n(t,e=2e5){try{const s=JSON.stringify(t);return s.length>e?s.slice(0,e)+"... (truncated)":s}catch{return null}}function r(t,e){return t.startsWith("resources/")?{type:t.split("/")[1],uri:e?.uri||e?.path||e?.resource}:t.startsWith("prompts/")?{type:"prompt",uri:e?.name||e?.promptId}:"tools/call"===t?{type:"tool",uri:e?.name}:e?.resourceType&&e?.resourceUri?{type:e.resourceType,uri:e.resourceUri}:{type:void 0,uri:void 0}}function o(t,e,s,i,n){const o=function(t,e){const s=t?.getClientInfo();if(s?.name||s?.version)return{name:s.name,version:s.version};const i=t?.getHeaders()||e;if(i){const t=i["user-agent"]||i["User-Agent"];if(t){const e=t.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(i,n),a=`${o.name||"unknown"}-${o.version||"0"}`,p=T.getOrCreateSessionId(a),c=T.getOrCreateConversationId(p,t),u=function(t,e){return t?.userId||t?.user_id?t.userId||t.user_id:e?e["x-user-id"]||e["X-User-Id"]:void 0}(e,n),h=function(t){if(t?.permission||t?.permissionLevel)return t.permission||t.permissionLevel}(e),d=r(t,e);d.uri&&T.trackResourceAccess(d.uri);const m=function(t,e){return void 0!==t?.cost?t.cost:void 0!==e?.cost?e.cost:void 0}(e,s),l={};return p&&(l.sessionId=p),c&&(l.conversationId=c),u&&(l.userId=u),o.name&&(l.clientName=o.name),o.version&&(l.clientVersion=o.version),h&&(l.permissionLevel=h),void 0!==m&&(l.cost=m),l}function a(t){O.build(t)}import{program as p}from"commander";import{NodeSDK as c}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as u}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as h}from"@opentelemetry/resources";import{trace as d,SpanStatusCode as m}from"@opentelemetry/api";import{readFileSync as l}from"fs";import{fileURLToPath as f}from"url";import{dirname as v,join as g}from"path";import{execSync as I,spawn as y}from"child_process";import{Transform as S}from"stream";import{ulid as x}from"ulid";const M="true"===process.env.AWARE_DEBUG||"1"===process.env.AWARE_DEBUG,C="true"===process.env.AWARE_RAW_DEBUG||"1"===process.env.AWARE_RAW_DEBUG,w={ENDPOINT:"https://api.awarecorp.io/traces",SDK_VERSION:function(){try{const t=f(import.meta.url),e=v(t),s=g(e,"..","..","package.json");return JSON.parse(l(s,"utf-8")).version||"1.0.0"}catch{return"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},D=["notifications/initialized"],E=new class{sdk=null;tracer=null;initialized=!1;cliServerInfo=null;initialize(t){if(this.initialized)return e("Telemetry already initialized, returning existing tracer"),this.tracer;const s=t.endpoint||w.ENDPOINT,i=t.serviceName||`${w.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`,n=t.serviceVersion||"unknown";e("Initializing telemetry..."),e("Endpoint: "+s),e("Service: "+i),e("Version: "+n),this.sdk=new c({resource:new h({"service.name":i,"service.version":n}),traceExporter:new u({url:s,headers:{"x-api-key":t.apiKey}})}),this.sdk.start(),this.tracer=d.getTracer("mcp-logger",w.SDK_VERSION),this.initialized=!0,e("Telemetry initialized successfully");const r=async()=>{e("Shutting down telemetry..."),await this.shutdown()};return process.once("SIGTERM",r),process.once("SIGINT",r),this.tracer}getTracer(){return this.tracer}isInitialized(){return this.initialized}setCLIServerInfo(t,e,s){this.cliServerInfo={command:t,args:e,env:s}}getCLIServerInfo(){return this.cliServerInfo}async shutdown(){if(this.sdk)try{await this.sdk.shutdown(),this.sdk=null,this.tracer=null,this.initialized=!1,this.cliServerInfo=null}catch(t){console.error("[mcp-logger] Error shutting down telemetry:",t)}}},R=new Set(["npx","node","uvx","python","python3","pip","pipx","deno","bun"]),q=new Set(["uvx","python","python3","pip","pipx"]);class N extends S{buffer="";direction;logger;sessionContextStore;constructor(t,e,s){super(),this.direction=t,this.logger=e,this.sessionContextStore=s}_transform(t,s,i){try{if("request"===this.direction)return this.push(t),this.buffer+=t.toString(),this.processBuffer(!1),void i();this.buffer+=t.toString(),this.buffer.length>1e6&&(e("Buffer size exceeded, clearing..."),this.buffer=""),this.processBuffer(!0),i()}catch(t){i(t)}}processBuffer(t){const i=this.buffer.split("\n");this.buffer=i.pop()||"";for(const n of i){const i=n.trim();if(i)try{const n=JSON.parse(i);if(t){if("2.0"!==n.jsonrpc){e("Non-JSON-RPC JSON ignored:",i.slice(0,100));continue}this.push(i+"\n")}s(`[${this.direction}] Raw message:`,i),this.handleMessage(n)}catch{e(t?"Non-JSON output filtered:":"Failed to parse:",i.slice(0,100))}}}isValidInfo(t){return!(!t?.name||!t?.version)}handleMessage(t){if("request"===this.direction&&"initialize"===t.method&&t.params?.clientInfo&&!this.isValidInfo(this.sessionContextStore.getClientInfo())){const e=t.params;this.sessionContextStore.setClientInfo({name:e.clientInfo?.name,version:e.clientInfo?.version})}if("response"===this.direction&&void 0!==t.id&&!t.method){const e=t;if(e.result?.serverInfo&&!this.isValidInfo(this.sessionContextStore.getServerInfo())){const t=e.result.serverInfo;this.sessionContextStore.setServerInfo({name:t.name,version:t.version})}}"request"===this.direction&&t.method?this.logger.onRequest(t):"response"===this.direction&&(void 0===t.id||t.method?t.method&&null==t.id&&this.logger.onNotification(t):this.logger.onResponse(t))}_flush(t){try{if(this.buffer.trim())try{const t=JSON.parse(this.buffer);"response"===this.direction&&"2.0"===t.jsonrpc&&this.push(this.buffer.trim()+"\n"),this.handleMessage(t)}catch{"request"===this.direction&&this.push(this.buffer),e("Failed to parse final buffer")}M&&e("Stream closed. Pending requests: "+this.logger.getStats().pending),t()}catch(e){t(e)}}}const T=new class{sessionMap=new Map;sessionCounter=0;conversationMap=new Map;conversationCounter=0;resourceAccessCount=new Map;getOrCreateSessionId(t){if(!this.sessionMap.has(t)){const e=x();this.sessionMap.set(t,e),this.sessionCounter++}return this.sessionMap.get(t)}getOrCreateConversationId(t,e){const s=`${t}-${e}`;return this.conversationMap.has(s)||this.conversationMap.set(s,"conv-"+ ++this.conversationCounter),this.conversationMap.get(s)}trackResourceAccess(t){const e=(this.resourceAccessCount.get(t)||0)+1;return this.resourceAccessCount.set(t,e),e}getResourceAccessCount(t){return this.resourceAccessCount.get(t)||0}clear(){this.sessionMap.clear(),this.conversationMap.clear(),this.resourceAccessCount.clear(),this.sessionCounter=0,this.conversationCounter=0}},O=new class{spanData;input;contextMetadata;build(t){const e=E.getTracer();e&&(this.input=t,e.startActiveSpan("mcp."+t.method,t=>{try{this.spanData=this.buildBase(),this.resolveContextMetadata(),this.applyTraceId(),this.applyRequestFields(),this.applyResponseFields(),this.applyDuration(),this.applyCLIServerInfo(),this.applyMCPServerInfo(),this.applyContextMetadata(),this.applyResourceMetadata(),this.applyCustomEvents(t),this.applyError(t),function(t,e){for(const[s,i]of Object.entries(e))null!=i&&t.setAttribute(s,i)}(t,this.spanData),s("[OTLP] Span data:",JSON.stringify(this.spanData,null,2))}catch{t.setStatus({code:m.ERROR,message:"Error creating span"})}finally{t.end()}}))}buildBase(){return{"ingest.type":"mcp","mcp.type":this.input.type,"mcp.transport":this.input.transport,"mcp.method":this.input.method,"mcp.source":this.input.source}}resolveContextMetadata(){this.contextMetadata=this.input.contextMetadata||o(this.input.method,this.input.params,this.input.result,void 0,this.input.headers)}applyTraceId(){this.contextMetadata.sessionId&&this.contextMetadata.conversationId&&(this.spanData["mcp.trace.id"]=`${this.contextMetadata.sessionId}:${this.contextMetadata.conversationId}`)}applyRequestFields(){if("paired"===this.input.type||"request"===this.input.type||"notification"===this.input.type){if(void 0!==this.input.requestId&&(this.spanData["mcp.request.id"]=this.input.requestId+""),this.spanData["mcp.request.method"]=this.input.method,void 0!==this.input.requestTimestamp&&(this.spanData["mcp.request.timestamp"]=this.input.requestTimestamp),void 0!==this.input.params&&(this.spanData["mcp.request.params"]=n(this.input.params)||"{}",this.spanData["mcp.request.params.size"]=JSON.stringify(this.input.params||{}).length),"tools/call"===this.input.method&&this.input.params){const t=this.input.params.name,e=this.input.params.arguments;if(t&&(this.spanData["mcp.tool.name"]=t),e){const t=n(e);t&&(this.spanData["mcp.tool.arguments"]=t,this.spanData["mcp.tool.arguments.size"]=JSON.stringify(e).length)}}if(this.input.headers){const t=n(this.input.headers);t&&(this.spanData["mcp.headers"]=t,this.spanData["mcp.headers.size"]=JSON.stringify(this.input.headers).length)}}}applyResponseFields(){if(("paired"===this.input.type||"response"===this.input.type)&&(void 0!==this.input.responseId&&(this.spanData["mcp.response.id"]=this.input.responseId+""),this.spanData["mcp.response.method"]=this.input.method,void 0!==this.input.responseTimestamp&&(this.spanData["mcp.response.timestamp"]=this.input.responseTimestamp),void 0!==this.input.result)){const t=n(this.input.result);t&&(this.spanData["mcp.response.result"]=t,this.spanData["mcp.response.result.size"]=JSON.stringify(this.input.result).length)}}applyDuration(){void 0!==this.input.durationMs&&(this.spanData["mcp.duration_ms"]=this.input.durationMs)}applyCLIServerInfo(){const t=E.getCLIServerInfo();t&&(this.spanData["mcp.cli"]=JSON.stringify({command:t.command,args:t.args,env:t.env}))}applyMCPServerInfo(){this.input.serverInfo&&(this.input.serverInfo.name&&(this.spanData["mcp.sdk.name"]=this.input.serverInfo.name),this.input.serverInfo.version&&(this.spanData["mcp.sdk.version"]=this.input.serverInfo.version))}applyContextMetadata(){this.contextMetadata.sessionId&&(this.spanData["mcp.session.id"]=this.contextMetadata.sessionId),this.contextMetadata.conversationId&&(this.spanData["mcp.conversation.id"]=this.contextMetadata.conversationId),this.contextMetadata.userId&&(this.spanData["mcp.user.id"]=this.contextMetadata.userId),this.contextMetadata.clientName&&(this.spanData["mcp.client.name"]=this.contextMetadata.clientName),this.contextMetadata.clientVersion&&(this.spanData["mcp.client.version"]=this.contextMetadata.clientVersion),this.contextMetadata.permissionLevel&&(this.spanData["mcp.permission.level"]=this.contextMetadata.permissionLevel),void 0!==this.contextMetadata.cost&&(this.spanData["mcp.cost"]=this.contextMetadata.cost)}applyResourceMetadata(){const t=function(t,e){const s=r(t,e);if(!s.uri)return{};const i=T.getResourceAccessCount(s.uri);return{resourceType:s.type,resourceUri:s.uri,resourceAccessCount:i}}(this.input.method,this.input.params);t.resourceType&&(this.spanData["mcp.resource.type"]=t.resourceType),t.resourceUri&&(this.spanData["mcp.resource.uri"]=t.resourceUri),t.resourceAccessCount&&(this.spanData["mcp.resource.access_count"]=t.resourceAccessCount)}applyCustomEvents(t){this.input.customEvents&&0!==this.input.customEvents.length&&(this.spanData["mcp.events"]=JSON.stringify(this.input.customEvents),this.spanData["mcp.events.count"]=this.input.customEvents.length,this.input.customEvents.forEach(e=>{t.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})}))}applyError(t){if(this.input.error){let e;this.spanData["mcp.error"]=!0,this.input.error instanceof Error?(e=this.input.error.message,t.recordException(this.input.error)):e="string"==typeof this.input.error?this.input.error:n(this.input.error)||"Unknown error",this.spanData["mcp.error.message"]=e,t.setStatus({code:m.ERROR,message:e})}else t.setStatus({code:m.OK})}};class k{pendingRequests=new Map;TIMEOUT=3e4;sessionContextStore;constructor(t){this.sessionContextStore=t}onRequest(t){if(i(t.method)){if(null==t.id){const e=o(t.method,t.params,void 0,this.sessionContextStore,void 0);return void a({type:"notification",method:t.method,transport:"stdio",source:"cli",params:t.params,contextMetadata:e})}"initialize"===t.method&&t.params?.clientInfo&&this.sessionContextStore.setClientInfo({name:t.params.clientInfo.name,version:t.params.clientInfo.version}),this.pendingRequests.set(t.id,{request:t,timestamp:Date.now()}),setTimeout(()=>this.handleTimeout(t.id),this.TIMEOUT)}}onResponse(t){if(null==t.id)return;const e=this.pendingRequests.get(t.id),s=Date.now(),i=e?.request.method||t.method||"unknown";let n=this.sessionContextStore.getServerInfo();"initialize"===e?.request.method&&t.result?.serverInfo&&(n={name:t.result.serverInfo.name,version:t.result.serverInfo.version});const r=o(i,e?.request.params,t.result,this.sessionContextStore,void 0);e?(a({type:"paired",method:i,transport:"stdio",source:"cli",requestId:e.request.id,requestTimestamp:e.timestamp,params:e.request.params,responseId:t.id,responseTimestamp:s,result:t.result,error:t.error,durationMs:s-e.timestamp,contextMetadata:r,serverInfo:n}),this.pendingRequests.delete(t.id)):a({type:"response",method:i,transport:"stdio",source:"cli",responseId:t.id,responseTimestamp:s,result:t.result,error:t.error,contextMetadata:r,serverInfo:n})}onNotification(t){if(!i(t.method))return;const e=o(t.method,t.params,void 0,this.sessionContextStore,void 0);a({type:"notification",method:t.method,transport:"stdio",source:"cli",params:t.params,contextMetadata:e})}handleTimeout(t){const e=this.pendingRequests.get(t);if(e&&Date.now()-e.timestamp>this.TIMEOUT){const s=o(e.request.method,e.request.params,void 0,this.sessionContextStore,void 0);a({type:"request",method:e.request.method,transport:"stdio",source:"cli",requestId:e.request.id,requestTimestamp:e.timestamp,params:e.request.params,contextMetadata:s}),this.pendingRequests.delete(t)}}getStats(){return{pending:this.pendingRequests.size}}clear(){this.pendingRequests.clear()}}class A{context={};getClientInfo(){return this.context.clientInfo}setClientInfo(t){this.context.clientInfo=t}getServerInfo(){return this.context.serverInfo}setServerInfo(t){this.context.serverInfo=t}getHeaders(){return this.context.headers}setHeaders(t){this.context.headers=t}snapshot(){return{clientInfo:this.context.clientInfo?{...this.context.clientInfo}:void 0,serverInfo:this.context.serverInfo?{...this.context.serverInfo}:void 0,headers:this.context.headers?{...this.context.headers}:void 0}}}class b{process;logger;isShuttingDown=!1;constructor(t,e){const s=new A;this.logger=new k(s),this.process=y(t,e,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}});const i=new N("request",this.logger,s),n=new N("response",this.logger,s);process.stdin.pipe(i).pipe(this.process.stdin),this.process.stdout.pipe(n).pipe(process.stdout),this.process.on("error",t=>this.handleError(t)),this.process.on("exit",(t,e)=>this.handleExit(t,e)),process.on("SIGTERM",()=>this.shutdown("SIGTERM")),process.on("SIGINT",()=>this.shutdown("SIGINT"))}async handleError(e){t("Failed to start MCP server:",e),await this.cleanup(),process.exit(1)}async handleExit(t,s){e(`MCP server exited with code ${t}, signal ${s}`),await this.cleanup(),process.exit(t||0)}async shutdown(t){this.isShuttingDown||(this.isShuttingDown=!0,e(`Received ${t}, shutting down...`),this.process.kill(t),await Promise.race([new Promise(t=>this.process.once("exit",t)),new Promise(t=>setTimeout(t,5e3))]),await this.cleanup(),process.exit(0))}async cleanup(){this.logger.clear(),await async function(){await E.shutdown()}()}}p.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("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(e,s)=>{const i=function(t){const e={};for(const s of t){const t=process.env[s];void 0!==t&&(e[s]=t)}return e}(function(){try{return I("aware mcpenv sync",{encoding:"utf-8",timeout:1e4,stdio:["pipe","pipe","pipe"]}),I("aware mcpenv keys",{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).trim().split("\n").filter(t=>t.length>0)}catch{return[]}}()),[n,...r]=e,o=function(t,e){const s=function(t){for(const e of t){if(R.has(e))continue;const t=e.match(/^(@[^@]+\/[^@]+)@(.+)$/);if(t)return{packageName:t[1],version:t[2]};const s=e.match(/^([^@]+)@(.+)$/);if(s)return{packageName:s[1],version:s[2]};const i=e.match(/^(@[^@]+\/[^@]+)$/);if(i)return{packageName:i[1],version:"latest"};if(e.match(/^[a-z0-9-]+$/)&&!e.includes("/"))return{packageName:e,version:"latest"}}return null}(e.filter(t=>!t.startsWith("-")));let i,n;if(i=s?.packageName?s.packageName:t.split("/").pop()||t||w.SERVICE_NAME_PREFIX+"-unknown",s&&(n=s.version,"latest"===s.version)){const e=q.has(t)?"pip":"npm",i=function(t,e){try{if("pip"===e){let e=null;for(const s of["pip3","pip"])try{e=I(`${s} index versions ${t}`,{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]});break}catch{continue}if(!e)return null;const s=e.match(/\(([^)]+)\)/);if(s)return s[1];const i=e.match(/Available versions:\s*([^\s,]+)/);return i?i[1]:null}{const e=I(`npm view ${t} version --json`,{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).trim().replace(/^["']|["']$/g,""),s=e.match(/^(\d+\.\d+\.\d+(?:-[\w.]+)?)/);return s?s[1]:e}}catch{return null}}(s.packageName,e);i&&(n=i)}return{name:i,version:n}}(n,r);!function(t){E.initialize(t)}({...s,serviceName:o.name,serviceVersion:o.version}),t("Starting MCP server: "+e.join(" ")),t(`Service: ${o.name} = ${o.version||"unknown"}`),function(t,e,s){E.setCLIServerInfo(t,e,s)}(n,r,i),new b(n,r)}),p.parse();
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- function e(...e){g&&console.error("[mcp-logger]",...e)}function t(e,t=2e5){try{const s=JSON.stringify(e);return s.length>t?s.slice(0,t)+"... (truncated)":s}catch{return null}}function s(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}}function r(e,t,r,n,o){const i=function(e,t){const s=t;if(s){const e=s["user-agent"]||s["User-Agent"];if(e){const t=e.match(/^([^\/]+)\/([^\s]+)/);if(t)return{name:t[1],version:t[2]}}}return{name:void 0,version:void 0}}(0,o),c=`${i.name||"unknown"}-${i.version||"0"}`,a=E.getOrCreateSessionId(c),m=E.getOrCreateConversationId(a,e),u=function(e,t){return e?.userId||e?.user_id?e.userId||e.user_id:t?t["x-user-id"]||t["X-User-Id"]:void 0}(t,o),p=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),d=s(e,t);d.uri&&E.trackResourceAccess(d.uri);const l=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,r),v={};return a&&(v.sessionId=a),m&&(v.conversationId=m),u&&(v.userId=u),i.name&&(v.clientName=i.name),i.version&&(v.clientVersion=i.version),p&&(v.permissionLevel=p),void 0!==l&&(v.cost=l),v}function n(e){const n=I.getTracer();if(!n)return;const o="mcp."+e.method;n.startActiveSpan(o,n=>{try{const o={"ingest.type":"mcp","mcp.type":e.type,"mcp.transport":e.transport,"mcp.method":e.method,"mcp.source":e.source},c=e.contextMetadata||r(e.method,e.params,e.result,0,e.headers);if(c.sessionId&&c.conversationId&&(o["mcp.trace.id"]=`${c.sessionId}:${c.conversationId}`),"paired"===e.type||"request"===e.type){if(void 0!==e.requestId&&(o["mcp.request.id"]=e.requestId+""),o["mcp.request.method"]=e.method,void 0!==e.requestTimestamp&&(o["mcp.request.timestamp"]=e.requestTimestamp),void 0!==e.params&&(o["mcp.request.params"]=t(e.params)||"{}",o["mcp.request.params.size"]=JSON.stringify(e.params||{}).length),"tools/call"===e.method&&e.params){const s=e.params.name,r=e.params.arguments;if(s&&(o["mcp.tool.name"]=s),r){const e=t(r);e&&(o["mcp.tool.arguments"]=e,o["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(e.headers){const s=t(e.headers);s&&(o["mcp.headers"]=s,o["mcp.headers.size"]=JSON.stringify(e.headers).length)}}if(("paired"===e.type||"response"===e.type)&&(void 0!==e.responseId&&(o["mcp.response.id"]=e.responseId+""),o["mcp.response.method"]=e.method,void 0!==e.responseTimestamp&&(o["mcp.response.timestamp"]=e.responseTimestamp),void 0!==e.result)){const s=t(e.result);s&&(o["mcp.response.result"]=s,o["mcp.response.result.size"]=JSON.stringify(e.result).length)}void 0!==e.durationMs&&(o["mcp.duration_ms"]=e.durationMs);const a=I.getCLIServerInfo();a&&(o["mcp.cli"]=JSON.stringify({command:a.command,args:a.args,env:a.env})),e.serverInfo&&(e.serverInfo.name&&(o["mcp.sdk.name"]=e.serverInfo.name),e.serverInfo.version&&(o["mcp.sdk.version"]=e.serverInfo.version)),c.sessionId&&(o["mcp.session.id"]=c.sessionId),c.conversationId&&(o["mcp.conversation.id"]=c.conversationId),c.userId&&(o["mcp.user.id"]=c.userId),c.clientName&&(o["mcp.client.name"]=c.clientName),c.clientVersion&&(o["mcp.client.version"]=c.clientVersion),c.permissionLevel&&(o["mcp.permission.level"]=c.permissionLevel),void 0!==c.cost&&(o["mcp.cost"]=c.cost);const m=function(e,t){const r=s(e,t);if(!r.uri)return{};const n=E.getResourceAccessCount(r.uri);return{resourceType:r.type,resourceUri:r.uri,resourceAccessCount:n}}(e.method,e.params);if(m.resourceType&&(o["mcp.resource.type"]=m.resourceType),m.resourceUri&&(o["mcp.resource.uri"]=m.resourceUri),m.resourceAccessCount&&(o["mcp.resource.access_count"]=m.resourceAccessCount),e.customEvents&&e.customEvents.length>0&&(o["mcp.events"]=JSON.stringify(e.customEvents),o["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{n.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.error){let s;o["mcp.error"]=!0,e.error instanceof Error?(s=e.error.message,n.recordException(e.error)):s="string"==typeof e.error?e.error:t(e.error)||"Unknown error",o["mcp.error.message"]=s,n.setStatus({code:i.ERROR,message:s})}else n.setStatus({code:i.OK});!function(e,t){for(const[s,r]of Object.entries(t))null!=r&&e.setAttribute(s,r)}(n,o),function(...e){f&&console.error("[mcp-logger]",...e)}("[OTLP] Span data:",JSON.stringify(o,null,2))}catch{n.setStatus({code:i.ERROR,message:"Error creating span"})}finally{n.end()}})}import{trace as o,SpanStatusCode as i,context as c}from"@opentelemetry/api";import{NodeSDK as a}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as m}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as u}from"@opentelemetry/resources";import{readFileSync as p}from"fs";import{fileURLToPath as d}from"url";import{dirname as l,join as v}from"path";import{ulid as h}from"ulid";const g="true"===process.env.AWARE_DEBUG||"1"===process.env.AWARE_DEBUG,f="true"===process.env.AWARE_RAW_DEBUG||"1"===process.env.AWARE_RAW_DEBUG,y={ENDPOINT:"https://api.awarecorp.io/traces",SDK_VERSION:function(){try{const e=d(import.meta.url),t=l(e),s=v(t,"..","..","package.json");return JSON.parse(p(s,"utf-8")).version||"1.0.0"}catch{return"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},I=new class{sdk=null;tracer=null;initialized=!1;cliServerInfo=null;initialize(t){if(this.initialized)return e("Telemetry already initialized, returning existing tracer"),this.tracer;const s=t.endpoint||y.ENDPOINT,r=t.serviceName||`${y.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`,n=t.serviceVersion||"unknown";e("Initializing telemetry..."),e("Endpoint: "+s),e("Service: "+r),e("Version: "+n),this.sdk=new a({resource:new u({"service.name":r,"service.version":n}),traceExporter:new m({url:s,headers:{"x-api-key":t.apiKey}})}),this.sdk.start(),this.tracer=o.getTracer("mcp-logger",y.SDK_VERSION),this.initialized=!0,e("Telemetry initialized successfully");const i=async()=>{e("Shutting down telemetry..."),await this.shutdown()};return process.once("SIGTERM",i),process.once("SIGINT",i),this.tracer}getTracer(){return this.tracer}isInitialized(){return this.initialized}setCLIServerInfo(e,t,s){this.cliServerInfo={command:e,args:t,env:s}}getCLIServerInfo(){return this.cliServerInfo}async shutdown(){if(this.sdk)try{await this.sdk.shutdown(),this.sdk=null,this.tracer=null,this.initialized=!1,this.cliServerInfo=null}catch(e){console.error("[mcp-logger] Error shutting down telemetry:",e)}}},E=new class{sessionMap=new Map;sessionCounter=0;conversationMap=new Map;conversationCounter=0;resourceAccessCount=new Map;getOrCreateSessionId(e){if(!this.sessionMap.has(e)){const t=h();this.sessionMap.set(e,t),this.sessionCounter++}return this.sessionMap.get(e)}getOrCreateConversationId(e,t){const s=`${e}-${t}`;return this.conversationMap.has(s)||this.conversationMap.set(s,"conv-"+ ++this.conversationCounter),this.conversationMap.get(s)}trackResourceAccess(e){const t=(this.resourceAccessCount.get(e)||0)+1;return this.resourceAccessCount.set(e,t),t}getResourceAccessCount(e){return this.resourceAccessCount.get(e)||0}clear(){this.sessionMap.clear(),this.conversationMap.clear(),this.resourceAccessCount.clear(),this.sessionCounter=0,this.conversationCounter=0}},S=Symbol("mcp-logger.customEvents"),w=Object.assign(function(t,s){if(!s.apiKey)throw Error("[mcp-logger] apiKey is required");if(!t)throw Error("[mcp-logger] server instance is required");!function(e){I.initialize(e)}(s),function(t){const s=t.setRequestHandler.bind(t);t.setRequestHandler=function(t,o){const i=t.shape?.method,a=i?._def?.value||"unknown";return e("Instrumenting handler: "+a),s(t,async(t,s)=>{const i=Date.now(),m=`${a}-${i}-${Math.random().toString(36).slice(2,8)}`,u=[],p=c.active().setValue(S,u);try{const d=await c.with(p,async()=>await o(t,s)),l=Date.now(),v=r(a,t.params,d,0,void 0);return n({type:"paired",method:a,transport:"stdio",source:"sdk",requestId:m,requestTimestamp:i,params:t.params,responseId:m,responseTimestamp:l,result:d,durationMs:l-i,customEvents:u.length>0?u:void 0,contextMetadata:v}),e(`Request completed (${l-i}ms):`,{method:a,customEvents:u.length}),d}catch(s){const o=Date.now(),c=r(a,t.params,void 0,0,void 0);throw n({type:"paired",method:a,transport:"stdio",source:"sdk",requestId:m,requestTimestamp:i,params:t.params,responseId:m,responseTimestamp:o,error:s,durationMs:o-i,customEvents:u.length>0?u:void 0,contextMetadata:c}),e("Request failed:",s),s}})},e("Server instrumentation complete")}(t)},{addLog(e){const t=c.active().getValue(S);if(!t)return;const s={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};t.push(s);const r=o.getSpan(c.active());r&&r.addEvent("custom."+s.level,{message:s.message,metadata:JSON.stringify(s.metadata||{}),timestamp:s.timestamp})},async shutdown(){await async function(){await I.shutdown()}()}});export{w as trace};
1
+ function t(...t){v&&console.error("[mcp-logger]",...t)}function e(t,e=2e5){try{const s=JSON.stringify(t);return s.length>e?s.slice(0,e)+"... (truncated)":s}catch{return null}}function s(t,e){return t.startsWith("resources/")?{type:t.split("/")[1],uri:e?.uri||e?.path||e?.resource}:t.startsWith("prompts/")?{type:"prompt",uri:e?.name||e?.promptId}:"tools/call"===t?{type:"tool",uri:e?.name}:e?.resourceType&&e?.resourceUri?{type:e.resourceType,uri:e.resourceUri}:{type:void 0,uri:void 0}}function i(t,e,i,n,r){const a=function(t,e){const s=e;if(s){const t=s["user-agent"]||s["User-Agent"];if(t){const e=t.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(0,r),o=`${a.name||"unknown"}-${a.version||"0"}`,p=I.getOrCreateSessionId(o),c=I.getOrCreateConversationId(p,t),u=function(t,e){return t?.userId||t?.user_id?t.userId||t.user_id:e?e["x-user-id"]||e["X-User-Id"]:void 0}(e,r),h=function(t){if(t?.permission||t?.permissionLevel)return t.permission||t.permissionLevel}(e),m=s(t,e);m.uri&&I.trackResourceAccess(m.uri);const d=function(t,e){return void 0!==t?.cost?t.cost:void 0!==e?.cost?e.cost:void 0}(e,i),l={};return p&&(l.sessionId=p),c&&(l.conversationId=c),u&&(l.userId=u),a.name&&(l.clientName=a.name),a.version&&(l.clientVersion=a.version),h&&(l.permissionLevel=h),void 0!==d&&(l.cost=d),l}import{trace as n,SpanStatusCode as r,context as a}from"@opentelemetry/api";import{NodeSDK as o}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as p}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as c}from"@opentelemetry/resources";import{readFileSync as u}from"fs";import{fileURLToPath as h}from"url";import{dirname as m,join as d}from"path";import{ulid as l}from"ulid";const v="true"===process.env.AWARE_DEBUG||"1"===process.env.AWARE_DEBUG,f="true"===process.env.AWARE_RAW_DEBUG||"1"===process.env.AWARE_RAW_DEBUG,g={ENDPOINT:"https://api.awarecorp.io/traces",SDK_VERSION:function(){try{const t=h(import.meta.url),e=m(t),s=d(e,"..","..","package.json");return JSON.parse(u(s,"utf-8")).version||"1.0.0"}catch{return"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},y=new class{sdk=null;tracer=null;initialized=!1;cliServerInfo=null;initialize(e){if(this.initialized)return t("Telemetry already initialized, returning existing tracer"),this.tracer;const s=e.endpoint||g.ENDPOINT,i=e.serviceName||`${g.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`,r=e.serviceVersion||"unknown";t("Initializing telemetry..."),t("Endpoint: "+s),t("Service: "+i),t("Version: "+r),this.sdk=new o({resource:new c({"service.name":i,"service.version":r}),traceExporter:new p({url:s,headers:{"x-api-key":e.apiKey}})}),this.sdk.start(),this.tracer=n.getTracer("mcp-logger",g.SDK_VERSION),this.initialized=!0,t("Telemetry initialized successfully");const a=async()=>{t("Shutting down telemetry..."),await this.shutdown()};return process.once("SIGTERM",a),process.once("SIGINT",a),this.tracer}getTracer(){return this.tracer}isInitialized(){return this.initialized}setCLIServerInfo(t,e,s){this.cliServerInfo={command:t,args:e,env:s}}getCLIServerInfo(){return this.cliServerInfo}async shutdown(){if(this.sdk)try{await this.sdk.shutdown(),this.sdk=null,this.tracer=null,this.initialized=!1,this.cliServerInfo=null}catch(t){console.error("[mcp-logger] Error shutting down telemetry:",t)}}},I=new class{sessionMap=new Map;sessionCounter=0;conversationMap=new Map;conversationCounter=0;resourceAccessCount=new Map;getOrCreateSessionId(t){if(!this.sessionMap.has(t)){const e=l();this.sessionMap.set(t,e),this.sessionCounter++}return this.sessionMap.get(t)}getOrCreateConversationId(t,e){const s=`${t}-${e}`;return this.conversationMap.has(s)||this.conversationMap.set(s,"conv-"+ ++this.conversationCounter),this.conversationMap.get(s)}trackResourceAccess(t){const e=(this.resourceAccessCount.get(t)||0)+1;return this.resourceAccessCount.set(t,e),e}getResourceAccessCount(t){return this.resourceAccessCount.get(t)||0}clear(){this.sessionMap.clear(),this.conversationMap.clear(),this.resourceAccessCount.clear(),this.sessionCounter=0,this.conversationCounter=0}},D=new class{spanData;input;contextMetadata;build(t){const e=y.getTracer();e&&(this.input=t,e.startActiveSpan("mcp."+t.method,t=>{try{this.spanData=this.buildBase(),this.resolveContextMetadata(),this.applyTraceId(),this.applyRequestFields(),this.applyResponseFields(),this.applyDuration(),this.applyCLIServerInfo(),this.applyMCPServerInfo(),this.applyContextMetadata(),this.applyResourceMetadata(),this.applyCustomEvents(t),this.applyError(t),function(t,e){for(const[s,i]of Object.entries(e))null!=i&&t.setAttribute(s,i)}(t,this.spanData),function(...t){f&&console.error("[mcp-logger]",...t)}("[OTLP] Span data:",JSON.stringify(this.spanData,null,2))}catch{t.setStatus({code:r.ERROR,message:"Error creating span"})}finally{t.end()}}))}buildBase(){return{"ingest.type":"mcp","mcp.type":this.input.type,"mcp.transport":this.input.transport,"mcp.method":this.input.method,"mcp.source":this.input.source}}resolveContextMetadata(){this.contextMetadata=this.input.contextMetadata||i(this.input.method,this.input.params,this.input.result,0,this.input.headers)}applyTraceId(){this.contextMetadata.sessionId&&this.contextMetadata.conversationId&&(this.spanData["mcp.trace.id"]=`${this.contextMetadata.sessionId}:${this.contextMetadata.conversationId}`)}applyRequestFields(){if("paired"===this.input.type||"request"===this.input.type||"notification"===this.input.type){if(void 0!==this.input.requestId&&(this.spanData["mcp.request.id"]=this.input.requestId+""),this.spanData["mcp.request.method"]=this.input.method,void 0!==this.input.requestTimestamp&&(this.spanData["mcp.request.timestamp"]=this.input.requestTimestamp),void 0!==this.input.params&&(this.spanData["mcp.request.params"]=e(this.input.params)||"{}",this.spanData["mcp.request.params.size"]=JSON.stringify(this.input.params||{}).length),"tools/call"===this.input.method&&this.input.params){const t=this.input.params.name,s=this.input.params.arguments;if(t&&(this.spanData["mcp.tool.name"]=t),s){const t=e(s);t&&(this.spanData["mcp.tool.arguments"]=t,this.spanData["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if(this.input.headers){const t=e(this.input.headers);t&&(this.spanData["mcp.headers"]=t,this.spanData["mcp.headers.size"]=JSON.stringify(this.input.headers).length)}}}applyResponseFields(){if(("paired"===this.input.type||"response"===this.input.type)&&(void 0!==this.input.responseId&&(this.spanData["mcp.response.id"]=this.input.responseId+""),this.spanData["mcp.response.method"]=this.input.method,void 0!==this.input.responseTimestamp&&(this.spanData["mcp.response.timestamp"]=this.input.responseTimestamp),void 0!==this.input.result)){const t=e(this.input.result);t&&(this.spanData["mcp.response.result"]=t,this.spanData["mcp.response.result.size"]=JSON.stringify(this.input.result).length)}}applyDuration(){void 0!==this.input.durationMs&&(this.spanData["mcp.duration_ms"]=this.input.durationMs)}applyCLIServerInfo(){const t=y.getCLIServerInfo();t&&(this.spanData["mcp.cli"]=JSON.stringify({command:t.command,args:t.args,env:t.env}))}applyMCPServerInfo(){this.input.serverInfo&&(this.input.serverInfo.name&&(this.spanData["mcp.sdk.name"]=this.input.serverInfo.name),this.input.serverInfo.version&&(this.spanData["mcp.sdk.version"]=this.input.serverInfo.version))}applyContextMetadata(){this.contextMetadata.sessionId&&(this.spanData["mcp.session.id"]=this.contextMetadata.sessionId),this.contextMetadata.conversationId&&(this.spanData["mcp.conversation.id"]=this.contextMetadata.conversationId),this.contextMetadata.userId&&(this.spanData["mcp.user.id"]=this.contextMetadata.userId),this.contextMetadata.clientName&&(this.spanData["mcp.client.name"]=this.contextMetadata.clientName),this.contextMetadata.clientVersion&&(this.spanData["mcp.client.version"]=this.contextMetadata.clientVersion),this.contextMetadata.permissionLevel&&(this.spanData["mcp.permission.level"]=this.contextMetadata.permissionLevel),void 0!==this.contextMetadata.cost&&(this.spanData["mcp.cost"]=this.contextMetadata.cost)}applyResourceMetadata(){const t=function(t,e){const i=s(t,e);if(!i.uri)return{};const n=I.getResourceAccessCount(i.uri);return{resourceType:i.type,resourceUri:i.uri,resourceAccessCount:n}}(this.input.method,this.input.params);t.resourceType&&(this.spanData["mcp.resource.type"]=t.resourceType),t.resourceUri&&(this.spanData["mcp.resource.uri"]=t.resourceUri),t.resourceAccessCount&&(this.spanData["mcp.resource.access_count"]=t.resourceAccessCount)}applyCustomEvents(t){this.input.customEvents&&0!==this.input.customEvents.length&&(this.spanData["mcp.events"]=JSON.stringify(this.input.customEvents),this.spanData["mcp.events.count"]=this.input.customEvents.length,this.input.customEvents.forEach(e=>{t.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})}))}applyError(t){if(this.input.error){let s;this.spanData["mcp.error"]=!0,this.input.error instanceof Error?(s=this.input.error.message,t.recordException(this.input.error)):s="string"==typeof this.input.error?this.input.error:e(this.input.error)||"Unknown error",this.spanData["mcp.error.message"]=s,t.setStatus({code:r.ERROR,message:s})}else t.setStatus({code:r.OK})}},M=Symbol("mcp-logger.customEvents"),E=Object.assign(function(e,s){if(!s.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(t){y.initialize(t)}(s),function(e){const s=e.setRequestHandler;e.setRequestHandler=function(n,r){const o=n.shape?.method,p=o?._def?.value||"unknown";return t("Instrumenting handler: "+p),s.call(e,n,async(e,s)=>{const n=Date.now(),o=`${p}-${n}-${Math.random().toString(36).slice(2,8)}`,c=[],u=a.active().setValue(M,c);let h,m;try{h=await a.with(u,()=>r(e,s))}catch(t){m=t}const d=Date.now(),l=d-n,v=i(p,e.params,m?void 0:h,0,void 0);var f;if(f={type:"paired",method:p,transport:"stdio",source:"sdk",requestId:o,requestTimestamp:n,params:e.params,responseId:o,responseTimestamp:d,result:m?void 0:h,error:m||void 0,durationMs:l,customEvents:c.length>0?c:void 0,contextMetadata:v},D.build(f),t(`Request ${m?"failed":"completed"} (${l}ms):`,{method:p,customEvents:c.length}),m)throw m;return h})},t("Server instrumentation complete")}(e)},{addLog(t){const e=a.active().getValue(M);if(!e)return;const s={level:t.level||"info",message:t.message,metadata:t.metadata,timestamp:t.timestamp||Date.now()};e.push(s);const i=n.getSpan(a.active());i&&i.addEvent("custom."+s.level,{message:s.message,metadata:JSON.stringify(s.metadata||{}),timestamp:s.timestamp})},async shutdown(){await async function(){await y.shutdown()}()}});export{E as trace};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awarecorp/mcp-logger",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
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",
@@ -52,7 +52,7 @@
52
52
  "url": "https://github.com/awarecorp/mcp-logger/issues"
53
53
  },
54
54
  "peerDependencies": {
55
- "@modelcontextprotocol/sdk": "^0.5.0"
55
+ "@modelcontextprotocol/sdk": "^1.29.0"
56
56
  },
57
57
  "peerDependenciesMeta": {
58
58
  "@modelcontextprotocol/sdk": {
@@ -65,13 +65,13 @@
65
65
  "@opentelemetry/resources": "^1.25.0",
66
66
  "@opentelemetry/sdk-node": "^0.52.0",
67
67
  "@opentelemetry/semantic-conventions": "^1.25.0",
68
- "@pinta-ai/types": "^0.0.2",
68
+ "@pinta-ai/types": "^0.0.3",
69
69
  "commander": "^11.1.0",
70
70
  "ulid": "^3.0.1"
71
71
  },
72
72
  "devDependencies": {
73
- "@modelcontextprotocol/sdk": "^0.5.0",
74
- "@rollup/plugin-terser": "^0.4.4",
73
+ "@modelcontextprotocol/sdk": "^1.29.0",
74
+ "@rollup/plugin-terser": "^1.0.0",
75
75
  "@rollup/plugin-typescript": "^12.1.4",
76
76
  "@types/node": "^20.0.0",
77
77
  "@types/ws": "^8.18.1",