@awarecorp/mcp-logger 0.0.6 → 0.0.7-dev.0
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/interceptor.js +1 -1
- package/dist/cli/main.js +1 -1
- package/dist/cli/mcp-message-logger.js +1 -1
- package/dist/core/types.d.ts +27 -1
- package/dist/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/instrumentation.js +1 -1
- package/package.json +1 -1
package/dist/cli/interceptor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Transform as e}from"stream";class s extends e{buffer="";direction;logger;debug;constructor(e,s,t=!1){super(),this.direction=e,this.logger=s,this.debug=
|
|
1
|
+
import{Transform as e}from"stream";class s extends e{buffer="";direction;logger;sessionContextStore;debug;constructor(e,s,t,r=!1){super(),this.direction=e,this.logger=s,this.sessionContextStore=t,this.debug=r}_transform(e,s,t){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),t()}catch(e){t(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const s of e){const e=s.trim();if(e)try{const s=JSON.parse(e);this.handleMessage(s)}catch(s){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){if("request"===this.direction&&"initialize"===e.method&&e.params?.clientInfo){const s=e.params;this.sessionContextStore.setClientInfo({name:s.clientInfo?.name,version:s.clientInfo?.version})}"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.onResponse(e))}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(s){e(s)}}}export{s as MessageInterceptor};
|
package/dist/cli/main.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
function e(e){return!O.includes(e)}function s(){return C}function r(){return P}function t(e){T=e}function o(){return T||"unknown"}async function n(){if(w)try{await w.shutdown(),w=null,C=null,L=!1,P=null,T=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function i(e,s){for(const[r,t]of Object.entries(s))null!=t&&e.setAttribute(r,t)}function c(e,s=2e5){try{const r=JSON.stringify(e);return r.length>s?r.slice(0,s)+"... (truncated)":r}catch(e){return null}}function a(e,s){return e.startsWith("resources/")?{type:e.split("/")[1],uri:s?.uri||s?.path||s?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:s?.name||s?.promptId}:"tools/call"===e?{type:"tool",uri:s?.name}:s?.resourceType&&s?.resourceUri?{type:s.resourceType,uri:s.resourceUri}:{type:void 0,uri:void 0}}function p(e,s,r,t){const o=function(e){if(e){const s=e["user-agent"]||e["User-Agent"];if(s){const e=s.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(t),n=function(e){if(!x.has(e)){const s=R();x.set(e,s)}return x.get(e)}(`${o.name||"unknown"}-${o.version||"0"}`),i=function(e,s){const r=`${e}-${s}`;return $.has(r)||$.set(r,"conv-"+ ++k),$.get(r)}(n,e),c=function(e,s){return e?.userId||e?.user_id?e.userId||e.user_id:s?s["x-user-id"]||s["X-User-Id"]:void 0}(s,t),p=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(s),m=a(e,s);m.uri&&function(e){const s=(_.get(e)||0)+1;_.set(e,s)}(m.uri);const u=function(e,s){return void 0!==e?.cost?e.cost:void 0!==s?.cost?s.cost:void 0}(s,r),d={};return n&&(d.sessionId=n),i&&(d.conversationId=i),c&&(d.userId=c),o.name&&(d.clientName=o.name),o.version&&(d.clientVersion=o.version),p&&(d.permissionLevel=p),void 0!==u&&(d.cost=u),d}function m(e,s){const r=a(e,s);if(!r.uri)return{};const t=_.get(r.uri)||0;return{resourceType:r.type,resourceUri:r.uri,resourceAccessCount:t}}import{spawn as u,execSync as d}from"child_process";import{program as l}from"commander";import{NodeSDK as g}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as f}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as h}from"@opentelemetry/resources";import{trace as v,SpanStatusCode as y}from"@opentelemetry/api";import{readFileSync as I}from"fs";import{fileURLToPath as S}from"url";import{dirname as q,join as E}from"path";import{Transform as N}from"stream";import{ulid as R}from"ulid";const M={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const e=S(import.meta.url),s=q(e),r=E(s,"..","..","package.json");return JSON.parse(I(r,"utf-8")).version||"1.0.0"}catch(e){return console.error("[MCP Logger] Failed to read package version:",e),"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},O=["notifications/initialized"];let w=null,C=null,L=!1,P=null,T=null;class b extends N{buffer="";direction;logger;debug;constructor(e,s,r=!1){super(),this.direction=e,this.logger=s,this.debug=r}_transform(e,s,r){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),r()}catch(e){r(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const s of e){const e=s.trim();if(e)try{const s=JSON.parse(e);this.handleMessage(s)}catch(s){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){console.error("[mcp-logger CLI] Handling message:",JSON.stringify(e).slice(0,100)),"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.onResponse(e))}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(s){e(s)}}}const x=new Map,$=new Map;let k=0;const _=new Map;class A{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(s){e(s.method)&&(s.id?(this.pendingRequests.set(s.id,{request:s,timestamp:Date.now()}),setTimeout(()=>this.cleanupStale(s.id),this.TIMEOUT)):this.createRequestOnlySpan(s,Date.now()))}onResponse(e){if(!e.id)return;const s=this.pendingRequests.get(e.id);if("initialize"===s?.request.method)if(e.result?.serverInfo?.version)t(e.result.serverInfo.version);else{const s=e.result?.capabilities?.implementation?.version;s&&t(s)}if(!s)return void this.createResponseOnlySpan(e,Date.now());const r=Date.now(),o=r-s.timestamp;this.createPairedSpan(s.request,e,s.timestamp,r,o),this.pendingRequests.delete(e.id)}onNotification(s){e(s.method)&&this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:s.method},Date.now())}createPairedSpan(e,t,n,a,u){const d=p(e.method,e.params,t.result,void 0);!function(e){const t=s();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+e.method;t.startActiveSpan(n,s=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":c(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.id":e.request.id+"","mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const s=e.request.params.name,r=e.request.params.arguments;if(s&&(t["mcp.tool.name"]=s),r){const e=c(r);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==e.response.result){const s=c(e.response.result);s&&(t["mcp.response.result"]=s,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const s=c(e.request.headers);s&&(t["mcp.headers"]=s,t["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=r();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=o();t["mcp.server.version"]=s}const n=e.contextMetadata||p(e.method,e.request.params,e.response.result,e.request.headers);n.sessionId&&(t["mcp.session.id"]=n.sessionId),n.conversationId&&(t["mcp.conversation.id"]=n.conversationId),n.sessionId&&n.conversationId&&(t["mcp.trace.id"]=`${n.sessionId}:${n.conversationId}`),n.userId&&(t["mcp.user.id"]=n.userId),n.clientName&&(t["mcp.client.name"]=n.clientName),n.clientVersion&&(t["mcp.client.version"]=n.clientVersion),n.permissionLevel&&(t["mcp.permission.level"]=n.permissionLevel),void 0!==n.cost&&(t["mcp.cost"]=n.cost);const a=m(e.method,e.request.params);if(a.resourceType&&(t["mcp.resource.type"]=a.resourceType),a.resourceUri&&(t["mcp.resource.uri"]=a.resourceUri),a.resourceAccessCount&&(t["mcp.resource.access_count"]=a.resourceAccessCount),e.customEvents&&e.customEvents.length>0&&(t["mcp.events"]=JSON.stringify(e.customEvents),t["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{s.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){let r;t["mcp.error"]=!0,r=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:c(e.response.error)||"Unknown error",t["mcp.error.message"]=r,e.response.error instanceof Error&&s.recordException(e.response.error),s.setStatus({code:y.ERROR,message:r})}else s.setStatus({code:y.OK});i(s,t)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),s.setStatus({code:y.ERROR,message:e+""})}finally{s.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:n,params:e.params},response:{timestamp:a,result:t.result,error:t.error},duration:u,contextMetadata:d})}cleanupStale(e){const s=this.pendingRequests.get(e);s&&Date.now()-s.timestamp>this.TIMEOUT&&(this.createRequestOnlySpan(s.request,s.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,t){const n=p(e.method,e.params,void 0,void 0);!function(e){const t=s();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+e.method;t.startActiveSpan(n,s=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":c(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length};if("tools/call"===e.method&&e.request.params){const s=e.request.params.name,r=e.request.params.arguments;if(s&&(t["mcp.tool.name"]=s),r){const e=c(r);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if("cli"===e.source){const e=r();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=o();t["mcp.server.version"]=s}const n=e.contextMetadata||p(e.method,e.request.params,void 0,e.request.headers);n.sessionId&&(t["mcp.session.id"]=n.sessionId),n.conversationId&&(t["mcp.conversation.id"]=n.conversationId),n.sessionId&&n.conversationId&&(t["mcp.trace.id"]=`${n.sessionId}:${n.conversationId}`),n.userId&&(t["mcp.user.id"]=n.userId),n.clientName&&(t["mcp.client.name"]=n.clientName),n.clientVersion&&(t["mcp.client.version"]=n.clientVersion),n.permissionLevel&&(t["mcp.permission.level"]=n.permissionLevel),void 0!==n.cost&&(t["mcp.cost"]=n.cost);const a=m(e.method,e.request.params);a.resourceType&&(t["mcp.resource.type"]=a.resourceType),a.resourceUri&&(t["mcp.resource.uri"]=a.resourceUri),a.resourceAccessCount&&(t["mcp.resource.access_count"]=a.resourceAccessCount),s.setStatus({code:y.OK}),i(s,t)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),s.setStatus({code:y.ERROR,message:e+""})}finally{s.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:t,params:e.params},contextMetadata:n})}createResponseOnlySpan(e,t){const n=p(e.method||"unknown",void 0,e.result,void 0);!function(e){const t=s();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+e.method;t.startActiveSpan(n,s=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.response.id":e.response.id+"","mcp.response.method":e.method,"mcp.response.timestamp":e.response.timestamp};if(void 0!==e.response.result){const s=c(e.response.result);s&&(t["mcp.response.result"]=s,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.response.error){let r;t["mcp.error"]=!0,r=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:c(e.response.error)||"Unknown error",t["mcp.error.message"]=r,e.response.error instanceof Error&&s.recordException(e.response.error),s.setStatus({code:y.ERROR,message:r})}else s.setStatus({code:y.OK});if("cli"===e.source){const e=r();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=o();t["mcp.server.version"]=s}const n=e.contextMetadata||p(e.method,void 0,e.response.result,void 0);n.sessionId&&(t["mcp.session.id"]=n.sessionId),n.conversationId&&(t["mcp.conversation.id"]=n.conversationId),n.sessionId&&n.conversationId&&(t["mcp.trace.id"]=`${n.sessionId}:${n.conversationId}`),n.userId&&(t["mcp.user.id"]=n.userId),n.clientName&&(t["mcp.client.name"]=n.clientName),n.clientVersion&&(t["mcp.client.version"]=n.clientVersion),n.permissionLevel&&(t["mcp.permission.level"]=n.permissionLevel),void 0!==n.cost&&(t["mcp.cost"]=n.cost),i(s,t)}catch(e){console.error("[MCP Logger] Error creating response-only span:",e),s.setStatus({code:y.ERROR,message:e+""})}finally{s.end()}})}({method:e.method||"same_request_method",source:"cli",transport:"stdio",response:{id:e.id,timestamp:t,result:e.result,error:e.error},contextMetadata:n})}getStats(){return{pending:this.pendingRequests.size}}clear(){this.pendingRequests.clear()}}l.name("mcp-logger").description("Add observability to any MCP server without code changes").version("1.0.0").requiredOption("-k, --api-key <key>","Aware API key").option("-s, --service-name <name>","Service name for identification").option("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(e,s)=>{try{const r=!1;!function(e){if(L)return C;const s=e.endpoint||M.ENDPOINT,r=e.serviceName||`${M.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;w=new g({resource:new h({"service.name":r,"service.version":M.SDK_VERSION}),traceExporter:new f({url:s,headers:{"x-api-key":e.apiKey}})}),w.start(),C=v.getTracer("mcp-logger",M.SDK_VERSION),L=!0;const t=async()=>{await n()};process.once("SIGTERM",t),process.once("SIGINT",t)}({...s,debug:r}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[o,...i]=e,c=function(e){for(const s of e){const e=s.match(/^(@[^@]+\/[^@]+)@(.+)$/);if(e)return{packageName:e[1],version:e[2]};const r=s.match(/^([^@]+)@(.+)$/);if(r)return{packageName:r[1],version:r[2]};const t=s.match(/^(@[^@]+\/[^@]+)$/);if(t)return{packageName:t[1],version:"latest"};if(s.match(/^[a-z0-9-]+$/)&&!s.includes("/"))return{packageName:s,version:"latest"}}return null}(i);if(c){let e=c.version;if("latest"===c.version){const s="uvx"===o?"pip":"npm",r=function(e,s){try{let r;r="pip"===s?"pip index versions "+e:`npm view ${e} version --json`;const t=d(r,{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]});if("pip"===s){const e=t.match(/Available versions:\s*([^\n]+)/);return e?e[1].split(",").map(e=>e.trim())[0]:null}{const e=t.trim().replace(/^["']|["']$/g,""),s=e.match(/^(\d+\.\d+\.\d+(?:-[\w.]+)?)/);return s?s[1]:e}}catch(e){return null}}(c.packageName,s);r&&(e=r)}t(e)}!function(e,s){P={command:e,args:s,env:{}}}(o,i);const a=u(o,i,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),p=new A(r),m=new b("request",p,r);process.stdin.pipe(m).pipe(a.stdin);const l=new b("response",p,r);a.stdout.pipe(l).pipe(process.stdout),a.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),p.clear(),await n(),process.exit(1)}),a.on("exit",async(e,s)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${s}`),p.clear(),await n(),process.exit(e||0)});let y=!1;const I=async e=>{y||(y=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),a.kill(e),await Promise.race([new Promise(e=>a.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await n(),process.exit(0))};process.on("SIGTERM",()=>I("SIGTERM")),process.on("SIGINT",()=>I("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await n(),process.exit(1)}}),l.parse();
|
|
2
|
+
function e(e){return!M.includes(e)}function s(){return O}function t(){return L}function r(e){P=e}function o(){return P||"unknown"}async function n(){if(w)try{await w.shutdown(),w=null,O=null,x=!1,L=null,P=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function i(e,s){for(const[t,r]of Object.entries(s))null!=r&&e.setAttribute(t,r)}function c(e,s=2e5){try{const t=JSON.stringify(e);return t.length>s?t.slice(0,s)+"... (truncated)":t}catch(e){return null}}function a(e,s){return e.startsWith("resources/")?{type:e.split("/")[1],uri:s?.uri||s?.path||s?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:s?.name||s?.promptId}:"tools/call"===e?{type:"tool",uri:s?.name}:s?.resourceType&&s?.resourceUri?{type:s.resourceType,uri:s.resourceUri}:{type:void 0,uri:void 0}}function p(e,s,t,r,o){const n=function(e,s){const t=e?.getClientInfo();if(t?.name||t?.version)return{name:t.name,version:t.version};const r=e?.getHeaders()||s;if(r){const e=r["user-agent"]||r["User-Agent"];if(e){const s=e.match(/^([^\/]+)\/([^\s]+)/);if(s)return{name:s[1],version:s[2]}}}return{name:void 0,version:void 0}}(r,o),i=function(e){if(!b.has(e)){const s=N();b.set(e,s)}return b.get(e)}(`${n.name||"unknown"}-${n.version||"0"}`),c=function(e,s){const t=`${e}-${s}`;return $.has(t)||$.set(t,"conv-"+ ++k),$.get(t)}(i,e),p=function(e,s){return e?.userId||e?.user_id?e.userId||e.user_id:s?s["x-user-id"]||s["X-User-Id"]:void 0}(s,o),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(s),u=a(e,s);u.uri&&function(e){const s=(_.get(e)||0)+1;_.set(e,s)}(u.uri);const d=function(e,s){return void 0!==e?.cost?e.cost:void 0!==s?.cost?s.cost:void 0}(s,t),l={};return i&&(l.sessionId=i),c&&(l.conversationId=c),p&&(l.userId=p),n.name&&(l.clientName=n.name),n.version&&(l.clientVersion=n.version),m&&(l.permissionLevel=m),void 0!==d&&(l.cost=d),l}function m(e,s){const t=a(e,s);if(!t.uri)return{};const r=_.get(t.uri)||0;return{resourceType:t.type,resourceUri:t.uri,resourceAccessCount:r}}import{spawn as u,execSync as d}from"child_process";import{program as l}from"commander";import{NodeSDK as g}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as h}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as f}from"@opentelemetry/resources";import{trace as v,SpanStatusCode as I}from"@opentelemetry/api";import{readFileSync as y}from"fs";import{fileURLToPath as S}from"url";import{dirname as q,join as C}from"path";import{Transform as E}from"stream";import{ulid as N}from"ulid";const R={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const e=S(import.meta.url),s=q(e),t=C(s,"..","..","package.json");return JSON.parse(y(t,"utf-8")).version||"1.0.0"}catch(e){return console.error("[MCP Logger] Failed to read package version:",e),"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},M=["notifications/initialized"];let w=null,O=null,x=!1,L=null,P=null;class T extends E{buffer="";direction;logger;sessionContextStore;debug;constructor(e,s,t,r=!1){super(),this.direction=e,this.logger=s,this.sessionContextStore=t,this.debug=r}_transform(e,s,t){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),t()}catch(e){t(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const s of e){const e=s.trim();if(e)try{const s=JSON.parse(e);this.handleMessage(s)}catch(s){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){if("request"===this.direction&&"initialize"===e.method&&e.params?.clientInfo){const s=e.params;this.sessionContextStore.setClientInfo({name:s.clientInfo?.name,version:s.clientInfo?.version})}"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.onResponse(e))}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(s){e(s)}}}const b=new Map,$=new Map;let k=0;const _=new Map;class A{pendingRequests=new Map;debug;TIMEOUT=3e4;sessionContextStore;constructor(e,s=!1){this.debug=s,this.sessionContextStore=e}onRequest(s){e(s.method)&&(s.id?(this.pendingRequests.set(s.id,{request:s,timestamp:Date.now()}),"initialize"===s.method&&s.params?.clientInfo&&this.sessionContextStore.setClientInfo({name:s.params.clientInfo.name,version:s.params.clientInfo.version}),setTimeout(()=>this.cleanupStale(s.id),this.TIMEOUT)):this.createRequestOnlySpan(s,Date.now()))}onResponse(e){if(!e.id)return;const s=this.pendingRequests.get(e.id);if("initialize"===s?.request.method)if(e.result?.serverInfo?.version)r(e.result.serverInfo.version);else{const s=e.result?.capabilities?.implementation?.version;s&&r(s)}if(!s)return void this.createResponseOnlySpan(e,Date.now());const t=Date.now(),o=t-s.timestamp;this.createPairedSpan(s.request,e,s.timestamp,t,o),this.pendingRequests.delete(e.id)}onNotification(s){e(s.method)&&this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:s.method},Date.now())}createPairedSpan(e,r,n,a,u){const d=p(e.method,e.params,r.result,this.sessionContextStore,void 0);!function(e){const r=s();if(!r)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+e.method;r.startActiveSpan(n,s=>{try{const r={"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":c(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.id":e.request.id+"","mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const s=e.request.params.name,t=e.request.params.arguments;if(s&&(r["mcp.tool.name"]=s),t){const e=c(t);e&&(r["mcp.tool.arguments"]=e,r["mcp.tool.arguments.size"]=JSON.stringify(t).length)}}if(void 0!==e.response.result){const s=c(e.response.result);s&&(r["mcp.response.result"]=s,r["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const s=c(e.request.headers);s&&(r["mcp.headers"]=s,r["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=t();e&&(r["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=o();r["mcp.server.version"]=s}const n=e.contextMetadata||p(e.method,e.request.params,e.response.result,void 0,e.request.headers);n.sessionId&&(r["mcp.session.id"]=n.sessionId),n.conversationId&&(r["mcp.conversation.id"]=n.conversationId),n.sessionId&&n.conversationId&&(r["mcp.trace.id"]=`${n.sessionId}:${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 a=m(e.method,e.request.params);if(a.resourceType&&(r["mcp.resource.type"]=a.resourceType),a.resourceUri&&(r["mcp.resource.uri"]=a.resourceUri),a.resourceAccessCount&&(r["mcp.resource.access_count"]=a.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=>{s.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){let t;r["mcp.error"]=!0,t=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:c(e.response.error)||"Unknown error",r["mcp.error.message"]=t,e.response.error instanceof Error&&s.recordException(e.response.error),s.setStatus({code:I.ERROR,message:t})}else s.setStatus({code:I.OK});i(s,r)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),s.setStatus({code:I.ERROR,message:e+""})}finally{s.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:n,params:e.params},response:{timestamp:a,result:r.result,error:r.error},duration:u,contextMetadata:d})}cleanupStale(e){const s=this.pendingRequests.get(e);s&&Date.now()-s.timestamp>this.TIMEOUT&&(this.createRequestOnlySpan(s.request,s.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,r){const n=p(e.method,e.params,void 0,this.sessionContextStore,void 0);!function(e){const r=s();if(!r)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+e.method;r.startActiveSpan(n,s=>{try{const r={"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":c(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length};if("tools/call"===e.method&&e.request.params){const s=e.request.params.name,t=e.request.params.arguments;if(s&&(r["mcp.tool.name"]=s),t){const e=c(t);e&&(r["mcp.tool.arguments"]=e,r["mcp.tool.arguments.size"]=JSON.stringify(t).length)}}if("cli"===e.source){const e=t();e&&(r["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=o();r["mcp.server.version"]=s}const n=e.contextMetadata||p(e.method,e.request.params,void 0,void 0,e.request.headers);n.sessionId&&(r["mcp.session.id"]=n.sessionId),n.conversationId&&(r["mcp.conversation.id"]=n.conversationId),n.sessionId&&n.conversationId&&(r["mcp.trace.id"]=`${n.sessionId}:${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 a=m(e.method,e.request.params);a.resourceType&&(r["mcp.resource.type"]=a.resourceType),a.resourceUri&&(r["mcp.resource.uri"]=a.resourceUri),a.resourceAccessCount&&(r["mcp.resource.access_count"]=a.resourceAccessCount),s.setStatus({code:I.OK}),i(s,r)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),s.setStatus({code:I.ERROR,message:e+""})}finally{s.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:r,params:e.params},contextMetadata:n})}createResponseOnlySpan(e,r){const n=p(e.method||"unknown",void 0,e.result,this.sessionContextStore,void 0);!function(e){const r=s();if(!r)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const n="mcp."+e.method;r.startActiveSpan(n,s=>{try{const r={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.response.id":e.response.id+"","mcp.response.method":e.method,"mcp.response.timestamp":e.response.timestamp};if(void 0!==e.response.result){const s=c(e.response.result);s&&(r["mcp.response.result"]=s,r["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.response.error){let t;r["mcp.error"]=!0,t=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:c(e.response.error)||"Unknown error",r["mcp.error.message"]=t,e.response.error instanceof Error&&s.recordException(e.response.error),s.setStatus({code:I.ERROR,message:t})}else s.setStatus({code:I.OK});if("cli"===e.source){const e=t();e&&(r["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=o();r["mcp.server.version"]=s}const n=e.contextMetadata||p(e.method,void 0,e.response.result,void 0,void 0);n.sessionId&&(r["mcp.session.id"]=n.sessionId),n.conversationId&&(r["mcp.conversation.id"]=n.conversationId),n.sessionId&&n.conversationId&&(r["mcp.trace.id"]=`${n.sessionId}:${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),i(s,r)}catch(e){console.error("[MCP Logger] Error creating response-only span:",e),s.setStatus({code:I.ERROR,message:e+""})}finally{s.end()}})}({method:e.method||"same_request_method",source:"cli",transport:"stdio",response:{id:e.id,timestamp:r,result:e.result,error:e.error},contextMetadata:n})}getStats(){return{pending:this.pendingRequests.size}}clear(){this.pendingRequests.clear()}}class z{context={};getClientInfo(){return this.context.clientInfo}setClientInfo(e){this.context.clientInfo=e}getHeaders(){return this.context.headers}setHeaders(e){this.context.headers=e}snapshot(){return{clientInfo:this.context.clientInfo?{...this.context.clientInfo}:void 0,headers:this.context.headers?{...this.context.headers}:void 0}}}l.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)=>{try{const t=!1,[o,...i]=e,c=function(e){for(const s of e){const e=s.match(/^(@[^@]+\/[^@]+)@(.+)$/);if(e)return{packageName:e[1],version:e[2]};const t=s.match(/^([^@]+)@(.+)$/);if(t)return{packageName:t[1],version:t[2]};const r=s.match(/^(@[^@]+\/[^@]+)$/);if(r)return{packageName:r[1],version:"latest"};if(s.match(/^[a-z0-9-]+$/)&&!s.includes("/"))return{packageName:s,version:"latest"}}return null}(i.filter(e=>!e.startsWith("-")));let a,p;if(a=c?.packageName?c.packageName:o.split("/").pop()||o||R.SERVICE_NAME_PREFIX+"-unknown",c&&(p=c.version,"latest"===c.version)){const e="uvx"===o?"pip":"npm",s=function(e,s){try{let t;t="pip"===s?"pip index versions "+e:`npm view ${e} version --json`;const r=d(t,{encoding:"utf-8",timeout:5e3,stdio:["pipe","pipe","pipe"]});if("pip"===s){const e=r.match(/Available versions:\s*([^\n]+)/);return e?e[1].split(",").map(e=>e.trim())[0]:null}{const e=r.trim().replace(/^["']|["']$/g,""),s=e.match(/^(\d+\.\d+\.\d+(?:-[\w.]+)?)/);return s?s[1]:e}}catch(e){return null}}(c.packageName,e);s&&(p=s)}!function(e){if(x)return O;const s=e.endpoint||R.ENDPOINT,t=e.serviceName||`${R.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;w=new g({resource:new f({"service.name":t,"service.version":R.SDK_VERSION}),traceExporter:new h({url:s,headers:{"x-api-key":e.apiKey}})}),w.start(),O=v.getTracer("mcp-logger",R.SDK_VERSION),x=!0;const r=async()=>{await n()};process.once("SIGTERM",r),process.once("SIGINT",r)}({...s,serviceName:a,debug:t}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" ")),p&&r(p),function(e,s){L={command:e,args:s,env:{}}}(o,i);const m=u(o,i,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),l=new z,I=new A(l,t),y=new T("request",I,l,t);process.stdin.pipe(y).pipe(m.stdin);const S=new T("response",I,l,t);m.stdout.pipe(S).pipe(process.stdout),m.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),I.clear(),await n(),process.exit(1)}),m.on("exit",async(e,s)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${s}`),I.clear(),await n(),process.exit(e||0)});let q=!1;const C=async e=>{q||(q=!0,console.error(`[MCP Logger CLI] 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",()=>C("SIGTERM")),process.on("SIGINT",()=>C("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await n(),process.exit(1)}}),l.parse();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return!a.includes(e)}function t(e,t,r,n){const o=function(e){if(!
|
|
1
|
+
function e(e){return!a.includes(e)}function t(e,t,n,o,s){const r=function(e,t){const n=e?.getClientInfo();if(n?.name||n?.version)return{name:n.name,version:n.version};const o=e?.getHeaders()||t;if(o){const e=o["user-agent"]||o["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}}(o,s),a=function(e){if(!c.has(e)){const t=i();c.set(e,t)}return c.get(e)}(`${r.name||"unknown"}-${r.version||"0"}`),m=function(e,t){const n=`${e}-${t}`;return p.has(n)||p.set(n,"conv-"+ ++u),p.get(n)}(a,e),l=function(e){if(e?.userId||e?.user_id)return e.userId||e.user_id}(t),h=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),f=function(e,t){return e.startsWith("resources/")?{type:e.split("/")[1],uri:t?.uri||t?.path||t?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:t?.name||t?.promptId}:"tools/call"===e?{type:"tool",uri:t?.name}:t?.resourceType&&t?.resourceUri?{type:t.resourceType,uri:t.resourceUri}:{type:void 0,uri:void 0}}(e,t);f.uri&&function(e){const t=(d.get(e)||0)+1;d.set(e,t)}(f.uri);const v=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,n),g={};return a&&(g.sessionId=a),m&&(g.conversationId=m),l&&(g.userId=l),r.name&&(g.clientName=r.name),r.version&&(g.clientVersion=r.version),h&&(g.permissionLevel=h),void 0!==v&&(g.cost=v),g}import{readFileSync as n}from"fs";import{fileURLToPath as o}from"url";import{dirname as s,join as r}from"path";import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";import{ulid as i}from"ulid";!function(){try{const e=o(import.meta.url),t=s(e),i=r(t,"..","..","package.json");return JSON.parse(n(i,"utf-8")).version||"1.0.0"}catch(e){return console.error("[MCP Logger] Failed to read package version:",e),"1.0.0"}}();const a=["notifications/initialized"],c=new Map,p=new Map;let u=0;const d=new Map;class m{pendingRequests=new Map;debug;TIMEOUT=3e4;sessionContextStore;constructor(e,t=!1){this.debug=t,this.sessionContextStore=e}onRequest(t){e(t.method)&&(t.id?(this.pendingRequests.set(t.id,{request:t,timestamp:Date.now()}),"initialize"===t.method&&t.params?.clientInfo&&this.sessionContextStore.setClientInfo({name:t.params.clientInfo.name,version:t.params.clientInfo.version}),setTimeout(()=>this.cleanupStale(t.id),this.TIMEOUT)):this.createRequestOnlySpan(t,Date.now()))}onResponse(e){if(!e.id)return;const t=this.pendingRequests.get(e.id);if("initialize"===t?.request.method&&(e.result?.serverInfo?.version?e.result.serverInfo.version:e.result),!t)return void this.createResponseOnlySpan(e,Date.now());const n=Date.now(),o=n-t.timestamp;this.createPairedSpan(t.request,e,t.timestamp,n,o),this.pendingRequests.delete(e.id)}onNotification(t){e(t.method)&&this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:t.method},Date.now())}createPairedSpan(e,n,o,s,r){t(e.method,e.params,n.result,this.sessionContextStore,void 0),e.method,e.id,e.params,n.result,n.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.createRequestOnlySpan(t.request,t.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,n){t(e.method,e.params,void 0,this.sessionContextStore,void 0),e.method,e.id,e.params,console.error("[MCP Logger] Tracer not initialized, cannot create span")}createResponseOnlySpan(e,n){t(e.method||"unknown",void 0,e.result,this.sessionContextStore,void 0),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.pendingRequests.clear()}}export{m as MCPMessageLogger};
|
package/dist/core/types.d.ts
CHANGED
|
@@ -146,6 +146,32 @@ interface MCPContextMetadata {
|
|
|
146
146
|
permissionLevel?: string;
|
|
147
147
|
cost?: number;
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Session-level context shared across a single MCP connection
|
|
151
|
+
*/
|
|
152
|
+
interface SessionContext {
|
|
153
|
+
clientInfo?: {
|
|
154
|
+
name?: string;
|
|
155
|
+
version?: string;
|
|
156
|
+
};
|
|
157
|
+
headers?: Record<string, string>;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Abstraction for reading/writing session-scoped context for a single connection.
|
|
161
|
+
*/
|
|
162
|
+
interface SessionContextStore {
|
|
163
|
+
getClientInfo(): {
|
|
164
|
+
name?: string;
|
|
165
|
+
version?: string;
|
|
166
|
+
} | undefined;
|
|
167
|
+
setClientInfo(info: {
|
|
168
|
+
name?: string;
|
|
169
|
+
version?: string;
|
|
170
|
+
} | undefined): void;
|
|
171
|
+
getHeaders(): Record<string, string> | undefined;
|
|
172
|
+
setHeaders(headers: Record<string, string> | undefined): void;
|
|
173
|
+
snapshot(): SessionContext;
|
|
174
|
+
}
|
|
149
175
|
/**
|
|
150
176
|
* Paired Span 입력 데이터
|
|
151
177
|
*/
|
|
@@ -199,4 +225,4 @@ interface RequestOnlySpanInput {
|
|
|
199
225
|
contextMetadata?: MCPContextMetadata;
|
|
200
226
|
}
|
|
201
227
|
|
|
202
|
-
export type { CustomLogEntry, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse, MCPContextMetadata, MCPSpanData, MessageDirection, PairedSpanInput, RequestOnlySpanInput, ResponseOnlySpanInput, SpanAttributes, TelemetryOptions };
|
|
228
|
+
export type { CustomLogEntry, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse, MCPContextMetadata, MCPSpanData, MessageDirection, PairedSpanInput, RequestOnlySpanInput, ResponseOnlySpanInput, SessionContext, SessionContextStore, SpanAttributes, TelemetryOptions };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(){return S}function r(){return q}function t(){return N||"unknown"}async function s(){if(I)try{await I.shutdown(),I=null,S=null,w=!1,q=null,N=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,r=2e5){try{const t=JSON.stringify(e);return t.length>r?t.slice(0,r)+"... (truncated)":t}catch(e){return null}}function n(e,r){return e.startsWith("resources/")?{type:e.split("/")[1],uri:r?.uri||r?.path||r?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:r?.name||r?.promptId}:"tools/call"===e?{type:"tool",uri:r?.name}:r?.resourceType&&r?.resourceUri?{type:r.resourceType,uri:r.resourceUri}:{type:void 0,uri:void 0}}function i(e,r,t,s){const
|
|
1
|
+
function e(){return S}function r(){return q}function t(){return N||"unknown"}async function s(){if(I)try{await I.shutdown(),I=null,S=null,w=!1,q=null,N=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,r=2e5){try{const t=JSON.stringify(e);return t.length>r?t.slice(0,r)+"... (truncated)":t}catch(e){return null}}function n(e,r){return e.startsWith("resources/")?{type:e.split("/")[1],uri:r?.uri||r?.path||r?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:r?.name||r?.promptId}:"tools/call"===e?{type:"tool",uri:r?.name}:r?.resourceType&&r?.resourceUri?{type:r.resourceType,uri:r.resourceUri}:{type:void 0,uri:void 0}}function i(e,r,t,s,o){const i=function(e,r){const t=r;if(t){const e=t["user-agent"]||t["User-Agent"];if(e){const r=e.match(/^([^\/]+)\/([^\s]+)/);if(r)return{name:r[1],version:r[2]}}}return{name:void 0,version:void 0}}(0,o),c=function(e){if(!M.has(e)){const r=y();M.set(e,r)}return M.get(e)}(`${i.name||"unknown"}-${i.version||"0"}`),a=function(e,r){const t=`${e}-${r}`;return O.has(t)||O.set(t,"conv-"+ ++R),O.get(t)}(c,e),m=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,o),u=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),p=n(e,r);p.uri&&function(e){const r=(L.get(e)||0)+1;L.set(e,r)}(p.uri);const l=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,t),d={};return c&&(d.sessionId=c),a&&(d.conversationId=a),m&&(d.userId=m),i.name&&(d.clientName=i.name),i.version&&(d.clientVersion=i.version),u&&(d.permissionLevel=u),void 0!==l&&(d.cost=l),d}function c(s){const c=e();if(!c)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const a="mcp."+s.method;c.startActiveSpan(a,e=>{try{const c={"mcp.source":s.source,"mcp.transport":s.transport,"mcp.request.id":s.request.id+"","mcp.request.method":s.method,"mcp.request.timestamp":s.request.timestamp,"mcp.request.params":o(s.request.params)||"{}","mcp.request.params.size":JSON.stringify(s.request.params||{}).length,"mcp.response.id":s.request.id+"","mcp.response.timestamp":s.response.timestamp,"mcp.duration_ms":s.duration};if("tools/call"===s.method&&s.request.params){const e=s.request.params.name,r=s.request.params.arguments;if(e&&(c["mcp.tool.name"]=e),r){const e=o(r);e&&(c["mcp.tool.arguments"]=e,c["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==s.response.result){const e=o(s.response.result);e&&(c["mcp.response.result"]=e,c["mcp.response.result.size"]=JSON.stringify(s.response.result).length)}if(s.request.headers){const e=o(s.request.headers);e&&(c["mcp.headers"]=e,c["mcp.headers.size"]=JSON.stringify(s.request.headers).length)}if("cli"===s.source){const e=r();e&&(c["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=t();c["mcp.server.version"]=s}const a=s.contextMetadata||i(s.method,s.request.params,s.response.result,0,s.request.headers);a.sessionId&&(c["mcp.session.id"]=a.sessionId),a.conversationId&&(c["mcp.conversation.id"]=a.conversationId),a.sessionId&&a.conversationId&&(c["mcp.trace.id"]=`${a.sessionId}:${a.conversationId}`),a.userId&&(c["mcp.user.id"]=a.userId),a.clientName&&(c["mcp.client.name"]=a.clientName),a.clientVersion&&(c["mcp.client.version"]=a.clientVersion),a.permissionLevel&&(c["mcp.permission.level"]=a.permissionLevel),void 0!==a.cost&&(c["mcp.cost"]=a.cost);const u=function(e,r){const t=n(e,r);if(!t.uri)return{};const s=L.get(t.uri)||0;return{resourceType:t.type,resourceUri:t.uri,resourceAccessCount:s}}(s.method,s.request.params);if(u.resourceType&&(c["mcp.resource.type"]=u.resourceType),u.resourceUri&&(c["mcp.resource.uri"]=u.resourceUri),u.resourceAccessCount&&(c["mcp.resource.access_count"]=u.resourceAccessCount),s.customEvents&&s.customEvents.length>0&&(c["mcp.events"]=JSON.stringify(s.customEvents),c["mcp.events.count"]=s.customEvents.length,s.customEvents.forEach(r=>{e.addEvent("custom."+(r.level||"info"),{message:r.message,metadata:JSON.stringify(r.metadata||{}),timestamp:r.timestamp||Date.now()})})),s.response.error){let r;c["mcp.error"]=!0,r=s.response.error instanceof Error?s.response.error.message:"string"==typeof s.response.error?s.response.error:o(s.response.error)||"Unknown error",c["mcp.error.message"]=r,s.response.error instanceof Error&&e.recordException(s.response.error),e.setStatus({code:m.ERROR,message:r})}else e.setStatus({code:m.OK});!function(e,r){for(const[t,s]of Object.entries(r))null!=s&&e.setAttribute(t,s)}(e,c)}catch(r){console.error("[MCP Logger] Error creating paired span:",r),e.setStatus({code:m.ERROR,message:r+""})}finally{e.end()}})}import{trace as a,SpanStatusCode as m,context as u}from"@opentelemetry/api";import{NodeSDK as p}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as l}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as d}from"@opentelemetry/resources";import{readFileSync as g}from"fs";import{fileURLToPath as v}from"url";import{dirname as f,join as h}from"path";import{ulid as y}from"ulid";const E={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const e=v(import.meta.url),r=f(e),t=h(r,"..","..","package.json");return JSON.parse(g(t,"utf-8")).version||"1.0.0"}catch(e){return console.error("[MCP Logger] Failed to read package version:",e),"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"};let I=null,S=null,w=!1,q=null,N=null;const M=new Map,O=new Map;let R=0;const L=new Map,C=Symbol("mcp-logger.customEvents"),b=Object.assign(function(e,r){if(!r.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(e){if(w)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),S;const r=e.endpoint||E.ENDPOINT,t=e.serviceName||`${E.SERVICE_NAME_PREFIX}-${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)),I=new p({resource:new d({"service.name":t,"service.version":E.SDK_VERSION}),traceExporter:new l({url:r,headers:{"x-api-key":e.apiKey}})}),I.start(),S=a.getTracer("mcp-logger",E.SDK_VERSION),w=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await s()};process.once("SIGTERM",o),process.once("SIGINT",o)}(r),function(e,r){const t=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,s){const o=e.shape?.method,n=o?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+n),t(e,async(e,t)=>{const o=Date.now(),a=`${n}-${o}-${Math.random().toString(36).slice(2,8)}`,m=[],p=u.active().setValue(C,m);try{const l=await u.with(p,async()=>await s(e,t)),d=Date.now(),g=i(n,e.params,l,0,void 0);return c({method:n,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:d,result:l},duration:d-o,customEvents:m.length>0?m:void 0,contextMetadata:g}),r.debug&&console.log(`[mcp-logger] Request completed (${d-o}ms):`,{method:n,customEvents:m.length}),l}catch(t){const s=Date.now(),u=i(n,e.params,void 0,0,void 0);throw c({method:n,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:s,error:t},duration:s-o,customEvents:m.length>0?m:void 0,contextMetadata:u}),r.debug&&console.error("[mcp-logger] Request failed:",t),t}})},r.debug&&console.log("[mcp-logger] Server instrumentation complete")}(e,r),r.debug&&console.log("[mcp-logger] ✓ Initialization complete")},{addLog(e){const r=u.active().getValue(C);if(!r)return void console.warn("[mcp-logger] addLog called outside of handler context");const t={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};r.push(t);const s=a.getSpan(u.active());s&&s.addEvent("custom."+t.level,{message:t.message,metadata:JSON.stringify(t.metadata||{}),timestamp:t.timestamp})},async shutdown(){await s()}});export{b as trace};
|
package/dist/sdk/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(){return S}function r(){return q}function t(){return N||"unknown"}async function s(){if(I)try{await I.shutdown(),I=null,S=null,w=!1,q=null,N=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,r=2e5){try{const t=JSON.stringify(e);return t.length>r?t.slice(0,r)+"... (truncated)":t}catch(e){return null}}function n(e,r){return e.startsWith("resources/")?{type:e.split("/")[1],uri:r?.uri||r?.path||r?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:r?.name||r?.promptId}:"tools/call"===e?{type:"tool",uri:r?.name}:r?.resourceType&&r?.resourceUri?{type:r.resourceType,uri:r.resourceUri}:{type:void 0,uri:void 0}}function i(e,r,t,s){const
|
|
1
|
+
function e(){return S}function r(){return q}function t(){return N||"unknown"}async function s(){if(I)try{await I.shutdown(),I=null,S=null,w=!1,q=null,N=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,r=2e5){try{const t=JSON.stringify(e);return t.length>r?t.slice(0,r)+"... (truncated)":t}catch(e){return null}}function n(e,r){return e.startsWith("resources/")?{type:e.split("/")[1],uri:r?.uri||r?.path||r?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:r?.name||r?.promptId}:"tools/call"===e?{type:"tool",uri:r?.name}:r?.resourceType&&r?.resourceUri?{type:r.resourceType,uri:r.resourceUri}:{type:void 0,uri:void 0}}function i(e,r,t,s,o){const i=function(e,r){const t=r;if(t){const e=t["user-agent"]||t["User-Agent"];if(e){const r=e.match(/^([^\/]+)\/([^\s]+)/);if(r)return{name:r[1],version:r[2]}}}return{name:void 0,version:void 0}}(0,o),c=function(e){if(!M.has(e)){const r=y();M.set(e,r)}return M.get(e)}(`${i.name||"unknown"}-${i.version||"0"}`),a=function(e,r){const t=`${e}-${r}`;return O.has(t)||O.set(t,"conv-"+ ++R),O.get(t)}(c,e),m=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,o),u=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),p=n(e,r);p.uri&&function(e){const r=(L.get(e)||0)+1;L.set(e,r)}(p.uri);const l=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,t),d={};return c&&(d.sessionId=c),a&&(d.conversationId=a),m&&(d.userId=m),i.name&&(d.clientName=i.name),i.version&&(d.clientVersion=i.version),u&&(d.permissionLevel=u),void 0!==l&&(d.cost=l),d}function c(s){const c=e();if(!c)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const a="mcp."+s.method;c.startActiveSpan(a,e=>{try{const c={"mcp.source":s.source,"mcp.transport":s.transport,"mcp.request.id":s.request.id+"","mcp.request.method":s.method,"mcp.request.timestamp":s.request.timestamp,"mcp.request.params":o(s.request.params)||"{}","mcp.request.params.size":JSON.stringify(s.request.params||{}).length,"mcp.response.id":s.request.id+"","mcp.response.timestamp":s.response.timestamp,"mcp.duration_ms":s.duration};if("tools/call"===s.method&&s.request.params){const e=s.request.params.name,r=s.request.params.arguments;if(e&&(c["mcp.tool.name"]=e),r){const e=o(r);e&&(c["mcp.tool.arguments"]=e,c["mcp.tool.arguments.size"]=JSON.stringify(r).length)}}if(void 0!==s.response.result){const e=o(s.response.result);e&&(c["mcp.response.result"]=e,c["mcp.response.result.size"]=JSON.stringify(s.response.result).length)}if(s.request.headers){const e=o(s.request.headers);e&&(c["mcp.headers"]=e,c["mcp.headers.size"]=JSON.stringify(s.request.headers).length)}if("cli"===s.source){const e=r();e&&(c["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const s=t();c["mcp.server.version"]=s}const a=s.contextMetadata||i(s.method,s.request.params,s.response.result,0,s.request.headers);a.sessionId&&(c["mcp.session.id"]=a.sessionId),a.conversationId&&(c["mcp.conversation.id"]=a.conversationId),a.sessionId&&a.conversationId&&(c["mcp.trace.id"]=`${a.sessionId}:${a.conversationId}`),a.userId&&(c["mcp.user.id"]=a.userId),a.clientName&&(c["mcp.client.name"]=a.clientName),a.clientVersion&&(c["mcp.client.version"]=a.clientVersion),a.permissionLevel&&(c["mcp.permission.level"]=a.permissionLevel),void 0!==a.cost&&(c["mcp.cost"]=a.cost);const u=function(e,r){const t=n(e,r);if(!t.uri)return{};const s=L.get(t.uri)||0;return{resourceType:t.type,resourceUri:t.uri,resourceAccessCount:s}}(s.method,s.request.params);if(u.resourceType&&(c["mcp.resource.type"]=u.resourceType),u.resourceUri&&(c["mcp.resource.uri"]=u.resourceUri),u.resourceAccessCount&&(c["mcp.resource.access_count"]=u.resourceAccessCount),s.customEvents&&s.customEvents.length>0&&(c["mcp.events"]=JSON.stringify(s.customEvents),c["mcp.events.count"]=s.customEvents.length,s.customEvents.forEach(r=>{e.addEvent("custom."+(r.level||"info"),{message:r.message,metadata:JSON.stringify(r.metadata||{}),timestamp:r.timestamp||Date.now()})})),s.response.error){let r;c["mcp.error"]=!0,r=s.response.error instanceof Error?s.response.error.message:"string"==typeof s.response.error?s.response.error:o(s.response.error)||"Unknown error",c["mcp.error.message"]=r,s.response.error instanceof Error&&e.recordException(s.response.error),e.setStatus({code:m.ERROR,message:r})}else e.setStatus({code:m.OK});!function(e,r){for(const[t,s]of Object.entries(r))null!=s&&e.setAttribute(t,s)}(e,c)}catch(r){console.error("[MCP Logger] Error creating paired span:",r),e.setStatus({code:m.ERROR,message:r+""})}finally{e.end()}})}import{trace as a,SpanStatusCode as m,context as u}from"@opentelemetry/api";import{NodeSDK as p}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as l}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as d}from"@opentelemetry/resources";import{readFileSync as g}from"fs";import{fileURLToPath as v}from"url";import{dirname as f,join as h}from"path";import{ulid as y}from"ulid";const E={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const e=v(import.meta.url),r=f(e),t=h(r,"..","..","package.json");return JSON.parse(g(t,"utf-8")).version||"1.0.0"}catch(e){return console.error("[MCP Logger] Failed to read package version:",e),"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"};let I=null,S=null,w=!1,q=null,N=null;const M=new Map,O=new Map;let R=0;const L=new Map,C=Symbol("mcp-logger.customEvents"),b=Object.assign(function(e,r){if(!r.apiKey)throw Error("[mcp-logger] apiKey is required");if(!e)throw Error("[mcp-logger] server instance is required");!function(e){if(w)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),S;const r=e.endpoint||E.ENDPOINT,t=e.serviceName||`${E.SERVICE_NAME_PREFIX}-${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)),I=new p({resource:new d({"service.name":t,"service.version":E.SDK_VERSION}),traceExporter:new l({url:r,headers:{"x-api-key":e.apiKey}})}),I.start(),S=a.getTracer("mcp-logger",E.SDK_VERSION),w=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const o=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await s()};process.once("SIGTERM",o),process.once("SIGINT",o)}(r),function(e,r){const t=e.setRequestHandler.bind(e);e.setRequestHandler=function(e,s){const o=e.shape?.method,n=o?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+n),t(e,async(e,t)=>{const o=Date.now(),a=`${n}-${o}-${Math.random().toString(36).slice(2,8)}`,m=[],p=u.active().setValue(C,m);try{const l=await u.with(p,async()=>await s(e,t)),d=Date.now(),g=i(n,e.params,l,0,void 0);return c({method:n,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:d,result:l},duration:d-o,customEvents:m.length>0?m:void 0,contextMetadata:g}),r.debug&&console.log(`[mcp-logger] Request completed (${d-o}ms):`,{method:n,customEvents:m.length}),l}catch(t){const s=Date.now(),u=i(n,e.params,void 0,0,void 0);throw c({method:n,source:"sdk",transport:"stdio",request:{id:a,timestamp:o,params:e.params},response:{timestamp:s,error:t},duration:s-o,customEvents:m.length>0?m:void 0,contextMetadata:u}),r.debug&&console.error("[mcp-logger] Request failed:",t),t}})},r.debug&&console.log("[mcp-logger] Server instrumentation complete")}(e,r),r.debug&&console.log("[mcp-logger] ✓ Initialization complete")},{addLog(e){const r=u.active().getValue(C);if(!r)return void console.warn("[mcp-logger] addLog called outside of handler context");const t={level:e.level||"info",message:e.message,metadata:e.metadata,timestamp:e.timestamp||Date.now()};r.push(t);const s=a.getSpan(u.active());s&&s.addEvent("custom."+t.level,{message:t.message,metadata:JSON.stringify(t.metadata||{}),timestamp:t.timestamp})},async shutdown(){await s()}});export{b as trace};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t,o,r){const
|
|
1
|
+
function e(e,t,o,r,n){const s=function(e){if(!p.has(e)){const t=a();p.set(e,t)}return p.get(e)}("unknown-0"),i=function(e,t){const o=`${e}-${t}`;return m.has(o)||m.set(o,"conv-"+ ++l),m.get(o)}(s,e),c=function(e){if(e?.userId||e?.user_id)return e.userId||e.user_id}(t),u=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),g=function(e,t){return e.startsWith("resources/")?{type:e.split("/")[1],uri:t?.uri||t?.path||t?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:t?.name||t?.promptId}:"tools/call"===e?{type:"tool",uri:t?.name}:t?.resourceType&&t?.resourceUri?{type:t.resourceType,uri:t.resourceUri}:{type:void 0,uri:void 0}}(e,t);g.uri&&function(e){const t=(d.get(e)||0)+1;d.set(e,t)}(g.uri);const f=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,o),v={};return s&&(v.sessionId=s),i&&(v.conversationId=i),c&&(v.userId=c),u&&(v.permissionLevel=u),void 0!==f&&(v.cost=f),v}function t(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function o(o,r){const s=o.setRequestHandler.bind(o);o.setRequestHandler=function(o,i){const c=o.shape?.method,u=c?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+u),s(o,async(o,s)=>{const c=Date.now(),a=(Math.random().toString(36).slice(2,8),[]),p=n.active().setValue(g,a);try{const m=await n.with(p,async()=>await i(o,s)),l=Date.now();return e(u,o.params,m),t(o.params),r.debug&&console.log(`[mcp-logger] Request completed (${l-c}ms):`,{method:u,customEvents:a.length}),m}catch(n){throw e(u,o.params,void 0),t(o.params),r.debug&&console.error("[mcp-logger] Request failed:",n),n}})},r.debug&&console.log("[mcp-logger] Server instrumentation complete")}function r(){return n.active().getValue(g)}import{context as n}from"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";import{readFileSync as s}from"fs";import{fileURLToPath as i}from"url";import{dirname as c,join as u}from"path";import{ulid as a}from"ulid";!function(){try{const e=i(import.meta.url),t=c(e),o=u(t,"..","..","package.json");return JSON.parse(s(o,"utf-8")).version||"1.0.0"}catch(e){return console.error("[MCP Logger] Failed to read package version:",e),"1.0.0"}}();const p=new Map,m=new Map;let l=0;const d=new Map,g=Symbol("mcp-logger.customEvents");export{r as getCustomEventsFromContext,o as instrumentMCPServer};
|
package/package.json
CHANGED