@awarecorp/mcp-logger 0.0.3 → 0.0.4-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -20
- package/dist/cli/main.js +1 -1
- package/dist/cli/mcp-message-logger.js +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/span-builder.js +1 -1
- package/dist/core/telemetry.js +1 -1
- package/dist/core/types.d.ts +28 -1
- package/dist/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/instrumentation.js +1 -1
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -18,16 +18,12 @@
|
|
|
18
18
|
|
|
19
19
|
All telemetry data is sent to OpenTelemetry-compatible endpoints with automatic request-response pairing.
|
|
20
20
|
|
|
21
|
-
---
|
|
22
|
-
|
|
23
21
|
## 📦 Installation
|
|
24
22
|
|
|
25
23
|
```bash
|
|
26
24
|
npm install @awarecorp/mcp-logger
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
---
|
|
30
|
-
|
|
31
27
|
## 🚀 Quick Start
|
|
32
28
|
|
|
33
29
|
### SDK Mode
|
|
@@ -35,11 +31,11 @@ npm install @awarecorp/mcp-logger
|
|
|
35
31
|
Instrument your MCP server with one line of code:
|
|
36
32
|
|
|
37
33
|
```typescript
|
|
38
|
-
import { trace } from
|
|
34
|
+
import { trace } from "@awarecorp/mcp-logger";
|
|
39
35
|
|
|
40
36
|
trace(server, {
|
|
41
|
-
apiKey:
|
|
42
|
-
serviceName:
|
|
37
|
+
apiKey: "YOUR_API_KEY",
|
|
38
|
+
serviceName: "my-mcp-server",
|
|
43
39
|
});
|
|
44
40
|
```
|
|
45
41
|
|
|
@@ -54,8 +50,6 @@ npx @awarecorp/mcp-logger \
|
|
|
54
50
|
npx your-mcp-server
|
|
55
51
|
```
|
|
56
52
|
|
|
57
|
-
---
|
|
58
|
-
|
|
59
53
|
## 📋 Features
|
|
60
54
|
|
|
61
55
|
### Core Capabilities
|
|
@@ -84,6 +78,7 @@ All spans include:
|
|
|
84
78
|
### Data Format
|
|
85
79
|
|
|
86
80
|
Optimized for Elasticsearch with flat structure:
|
|
81
|
+
|
|
87
82
|
- `mcp.request.method` / `mcp.response.method`: Method names for request/response
|
|
88
83
|
- `mcp.source`: `sdk` or `cli`
|
|
89
84
|
- `mcp.transport`: `stdio` or `sse`
|
|
@@ -94,8 +89,6 @@ Optimized for Elasticsearch with flat structure:
|
|
|
94
89
|
- `mcp.events`: Custom log entries (JSON array)
|
|
95
90
|
- `mcp.cli.server`: CLI execution info (JSON object)
|
|
96
91
|
|
|
97
|
-
---
|
|
98
|
-
|
|
99
92
|
## 🔧 SDK API
|
|
100
93
|
|
|
101
94
|
### `trace(server, options)`
|
|
@@ -103,6 +96,7 @@ Optimized for Elasticsearch with flat structure:
|
|
|
103
96
|
Enable automatic instrumentation on an MCP server.
|
|
104
97
|
|
|
105
98
|
**Parameters:**
|
|
99
|
+
|
|
106
100
|
- `server`: MCP Server instance
|
|
107
101
|
- `options.apiKey`: API key for authentication (required)
|
|
108
102
|
- Currently accepts any string value for testing purposes
|
|
@@ -120,6 +114,7 @@ Enable automatic instrumentation on an MCP server.
|
|
|
120
114
|
Add custom log entries within request handlers.
|
|
121
115
|
|
|
122
116
|
**Parameters:**
|
|
117
|
+
|
|
123
118
|
- `entry.level`: Log level (`info` | `warn` | `error`)
|
|
124
119
|
- `entry.message`: Log message (required)
|
|
125
120
|
- `entry.metadata`: Additional data (optional)
|
|
@@ -133,8 +128,6 @@ Gracefully shutdown telemetry and flush pending data.
|
|
|
133
128
|
|
|
134
129
|
**Returns:** `Promise<void>`
|
|
135
130
|
|
|
136
|
-
---
|
|
137
|
-
|
|
138
131
|
## 🖥️ CLI Usage
|
|
139
132
|
|
|
140
133
|
### Basic Syntax
|
|
@@ -158,11 +151,13 @@ mcp-logger [options] <command...>
|
|
|
158
151
|
### Examples
|
|
159
152
|
|
|
160
153
|
**Wrap an npx command:**
|
|
154
|
+
|
|
161
155
|
```bash
|
|
162
156
|
mcp-logger -k API_KEY -s filesystem npx -y @modelcontextprotocol/server-filesystem /path
|
|
163
157
|
```
|
|
164
158
|
|
|
165
159
|
**Use with Claude Desktop:**
|
|
160
|
+
|
|
166
161
|
```json
|
|
167
162
|
{
|
|
168
163
|
"mcpServers": {
|
|
@@ -185,8 +180,6 @@ mcp-logger -k API_KEY -s filesystem npx -y @modelcontextprotocol/server-filesyst
|
|
|
185
180
|
}
|
|
186
181
|
```
|
|
187
182
|
|
|
188
|
-
---
|
|
189
|
-
|
|
190
183
|
## 🏗️ How It Works
|
|
191
184
|
|
|
192
185
|
### Request-Response Pairing
|
|
@@ -207,15 +200,15 @@ Creates **one span per request-response pair** for complete transaction context:
|
|
|
207
200
|
### Tracked Methods
|
|
208
201
|
|
|
209
202
|
Configurable method filtering with exclusion list:
|
|
203
|
+
|
|
210
204
|
- Tracks: `tools/call`, `tools/list`, and other business-critical methods
|
|
211
205
|
- Excludes: `notifications/initialized` and other noisy protocol notifications
|
|
212
206
|
- Customizable via `EXCLUDED_METHODS` configuration
|
|
213
207
|
|
|
214
|
-
---
|
|
215
|
-
|
|
216
208
|
## 📊 Telemetry Data Structure
|
|
217
209
|
|
|
218
210
|
Each span includes:
|
|
211
|
+
|
|
219
212
|
- Request/response timestamps and payloads (with optional fields for notifications)
|
|
220
213
|
- Method name and parameters (separate fields for request/response)
|
|
221
214
|
- Tool names and arguments (for `tools/call`)
|
|
@@ -227,19 +220,36 @@ Each span includes:
|
|
|
227
220
|
### Span Attributes
|
|
228
221
|
|
|
229
222
|
All attributes use a flat structure optimized for Elasticsearch:
|
|
223
|
+
|
|
224
|
+
**Core Attributes:**
|
|
225
|
+
|
|
230
226
|
- Request attributes: `mcp.request.*` (id, method, params, timestamp)
|
|
231
227
|
- Response attributes: `mcp.response.*` (method, result, error, timestamp)
|
|
232
228
|
- Error handling: Proper serialization for Error objects, strings, and complex objects
|
|
233
229
|
- Metadata: source, transport, duration, tool information
|
|
234
230
|
|
|
235
|
-
|
|
231
|
+
**Context Tracking:**
|
|
232
|
+
|
|
233
|
+
- `mcp.session.id`: Unique session identifier
|
|
234
|
+
- `mcp.conversation.id`: Conversation/thread identifier
|
|
235
|
+
- `mcp.user.id`: User identifier (from params, headers, or environment)
|
|
236
|
+
- `mcp.client.name`: Client application name (e.g., 'Claude Desktop')
|
|
237
|
+
- `mcp.client.version`: Client version
|
|
238
|
+
|
|
239
|
+
**Permission & Security:**
|
|
240
|
+
|
|
241
|
+
- `mcp.permission.level`: Permission level (read/write/admin/elevated)
|
|
242
|
+
|
|
243
|
+
**Resource Tracking:**
|
|
244
|
+
|
|
245
|
+
- `mcp.resource.type`: Resource type (tool/file/prompt)
|
|
246
|
+
- `mcp.resource.uri`: Resource URI or identifier
|
|
247
|
+
- `mcp.resource.access_count`: Number of times resource accessed
|
|
236
248
|
|
|
237
249
|
## 📄 License
|
|
238
250
|
|
|
239
251
|
MIT © [Aware Corp](https://awarecorp.io/)
|
|
240
252
|
|
|
241
|
-
---
|
|
242
|
-
|
|
243
253
|
## 🔗 Links
|
|
244
254
|
|
|
245
255
|
- [Aware Corp](https://awarecorp.io/)
|
package/dist/cli/main.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
function e(e){return!h.includes(e)}function r(){return y}function s(){return S}async function t(){if(f)try{await f.shutdown(),f=null,y=null,q=!1,S=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function o(e,r){for(const[s,t]of Object.entries(r))null!=t&&e.setAttribute(s,t)}function n(e,r=1e4){try{const s=JSON.stringify(e);return s.length>r?s.slice(0,r)+"... (truncated)":s}catch(e){return null}}import{spawn as i}from"child_process";import{program as c}from"commander";import{NodeSDK as a}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as p}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as m}from"@opentelemetry/resources";import{trace as u,SpanStatusCode as d}from"@opentelemetry/api";import{Transform as l}from"stream";const g="1.0.0",h=["notifications/initialized"];let f=null,y=null,q=!1,S=null;class v extends l{buffer="";direction;logger;debug;constructor(e,r,s=!1){super(),this.direction=e,this.logger=r,this.debug=s}_transform(e,r,s){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),s()}catch(e){s(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const r of e){const e=r.trim();if(e)try{const r=JSON.parse(e);this.handleMessage(r)}catch(r){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){console.error("[mcp-logger CLI] Handling message:",JSON.stringify(e).slice(0,100)),"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.onResponse(e))}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(r){e(r)}}}class C{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(r){if(this.debug&&console.error(`[mcp-logger CLI] 📥 REQUEST: method="${r.method}", id=${r.id}, params=${JSON.stringify(r.params)?.slice(0,100)}`),e(r.method)){if(!r.id)return this.debug&&console.error(`[mcp-logger CLI] 🔔 Notification detected: ${r.method} → Creating request-only span`),void this.createRequestOnlySpan(r,Date.now());this.pendingRequests.set(r.id,{request:r,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${r.method} (ID: ${r.id})`),setTimeout(()=>this.cleanupStale(r.id),this.TIMEOUT)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+r.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] 📤 RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const r=this.pendingRequests.get(e.id);if(!r)return this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} → Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const s=Date.now(),t=s-r.timestamp;this.debug&&console.error(`[mcp-logger CLI] ✅ Pairing success: ${r.request.method} (${t}ms)`),this.createPairedSpan(r.request,e,r.timestamp,s,t),this.pendingRequests.delete(e.id)}onNotification(r){this.debug&&console.error(`[mcp-logger CLI] 📢 NOTIFICATION (Server→Client): method="${r.method}", params=${JSON.stringify(r.params)?.slice(0,100)}`),e(r.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:r.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+r.method)}createPairedSpan(e,t,i,c,a){!function(e){const t=r();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,r=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":n(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.id":e.request.id+"","mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const r=e.request.params.name,s=e.request.params.arguments;if(r&&(t["mcp.tool.name"]=r),s){const e=n(s);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if(void 0!==e.response.result){const r=n(e.response.result);r&&(t["mcp.response.result"]=r,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const r=n(e.request.headers);r&&(t["mcp.headers"]=r,t["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=s();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}if(e.customEvents&&e.customEvents.length>0&&(t["mcp.events"]=JSON.stringify(e.customEvents),t["mcp.events.count"]=e.customEvents.length,e.customEvents.forEach(e=>{r.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){let s;t["mcp.error"]=!0,s=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:n(e.response.error)||"Unknown error",t["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:d.ERROR,message:s})}else r.setStatus({code:d.OK});o(r,t)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:d.ERROR,message:e+""})}finally{r.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:i,params:e.params},response:{timestamp:c,result:t.result,error:t.error},duration:a})}cleanupStale(e){const r=this.pendingRequests.get(e);r&&Date.now()-r.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] ⏰ Request timeout: ${r.request.method} (ID: ${e}) → Creating request-only span`),this.createRequestOnlySpan(r.request,r.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,t){!function(e){const t=r();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,r=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.request.id":e.request.id+"","mcp.request.method":e.method,"mcp.request.timestamp":e.request.timestamp,"mcp.request.params":n(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length};if("tools/call"===e.method&&e.request.params){const r=e.request.params.name,s=e.request.params.arguments;if(r&&(t["mcp.tool.name"]=r),s){const e=n(s);e&&(t["mcp.tool.arguments"]=e,t["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if("cli"===e.source){const e=s();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}r.setStatus({code:d.OK}),o(r,t)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),r.setStatus({code:d.ERROR,message:e+""})}finally{r.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:t,params:e.params}})}createResponseOnlySpan(e,t){!function(e){const t=r();if(!t)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const i="mcp."+e.method;t.startActiveSpan(i,r=>{try{const t={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.response.id":e.response.id+"","mcp.response.method":e.method,"mcp.response.timestamp":e.response.timestamp};if(void 0!==e.response.result){const r=n(e.response.result);r&&(t["mcp.response.result"]=r,t["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.response.error){let s;t["mcp.error"]=!0,s=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:n(e.response.error)||"Unknown error",t["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:d.ERROR,message:s})}else r.setStatus({code:d.OK});if("cli"===e.source){const e=s();e&&(t["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}))}o(r,t)}catch(e){console.error("[MCP Logger] Error creating response-only span:",e),r.setStatus({code:d.ERROR,message:e+""})}finally{r.end()}})}({method:e.method||"same_request_method",source:"cli",transport:"stdio",response:{id:e.id,timestamp:t,result:e.result,error:e.error}})}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}c.name("mcp-logger").description("Add observability to any MCP server without code changes").version("1.0.0").requiredOption("-k, --api-key <key>","Aware API key").option("-s, --service-name <name>","Service name for identification").option("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(e,r)=>{try{const s=!1;!function(e){if(q)return y;const r=e.endpoint||"https://aware.mcypher.com/v1/traces",s=e.serviceName||"mcp-server-"+Math.random().toString(36).slice(2,8);f=new a({resource:new m({"service.name":s,"service.version":g}),traceExporter:new p({url:r,headers:{"x-api-key":e.apiKey}})}),f.start(),y=u.getTracer("mcp-logger",g),q=!0;const o=async()=>{await t()};process.once("SIGTERM",o),process.once("SIGINT",o)}({...r,debug:s}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[o,...n]=e;!function(e,r){S={command:e,args:r,env:{}}}(o,n);const c=i(o,n,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),d=new C(s),l=new v("request",d,s);process.stdin.pipe(l).pipe(c.stdin);const h=new v("response",d,s);c.stdout.pipe(h).pipe(process.stdout),c.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),d.clear(),await t(),process.exit(1)}),c.on("exit",async(e,r)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${r}`),d.clear(),await t(),process.exit(e||0)});let R=!1;const O=async e=>{R||(R=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),c.kill(e),await Promise.race([new Promise(e=>c.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await t(),process.exit(0))};process.on("SIGTERM",()=>O("SIGTERM")),process.on("SIGINT",()=>O("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await t(),process.exit(1)}}),c.parse();
|
|
2
|
+
function e(e){return!E.includes(e)}function r(){return L}function s(){return M}function t(){return w||"unknown"}async function o(){if(O)try{await O.shutdown(),O=null,L=null,N=!1,M=null,w=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}function n(e,r){for(const[s,t]of Object.entries(r))null!=t&&e.setAttribute(s,t)}function i(e,r=1e4){try{const s=JSON.stringify(e);return s.length>r?s.slice(0,r)+"... (truncated)":s}catch(e){return null}}function c(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 a(e,r,s,t){const o=function(e){if(e){const r=e["user-agent"]||e["User-Agent"];if(r){const e=r.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(t),n=function(e){if(!b.has(e)){const r=C();b.set(e,r)}return b.get(e)}(`${o.name||"unknown"}-${o.version||"0"}`),i=function(e,r){const s=`${e}-${r}`;return $.has(s)||$.set(s,"conv-"+ ++T),$.get(s)}(n,e),a=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,t),p=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),m=c(e,r);m.uri&&function(e){const r=(x.get(e)||0)+1;x.set(e,r)}(m.uri);const u=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,s),d={};return n&&(d.sessionId=n),i&&(d.conversationId=i),a&&(d.userId=a),o.name&&(d.clientName=o.name),o.version&&(d.clientVersion=o.version),p&&(d.permissionLevel=p),void 0!==u&&(d.cost=u),d}function p(e,r){const s=c(e,r);if(!s.uri)return{};const t=x.get(s.uri)||0;return{resourceType:s.type,resourceUri:s.uri,resourceAccessCount:t}}import{spawn as m}from"child_process";import{program as u}from"commander";import{NodeSDK as d}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as l}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as g}from"@opentelemetry/resources";import{trace as h,SpanStatusCode as f}from"@opentelemetry/api";import{readFileSync as v}from"fs";import{fileURLToPath as I}from"url";import{dirname as y,join as S}from"path";import{Transform as q}from"stream";import{ulid as C}from"ulid";const R={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const e=I(import.meta.url),r=y(e),s=S(r,"..","..","package.json");return JSON.parse(v(s,"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"},E=["notifications/initialized"];let O=null,L=null,N=!1,M=null,w=null;class P extends q{buffer="";direction;logger;debug;constructor(e,r,s=!1){super(),this.direction=e,this.logger=r,this.debug=s}_transform(e,r,s){try{this.push(e),this.buffer+=e.toString(),this.buffer.length>1e6&&(this.debug&&console.error("[mcp-logger CLI] Buffer size exceeded, clearing..."),this.buffer=""),this.tryParseMessages(),s()}catch(e){s(e)}}tryParseMessages(){const e=this.buffer.split("\n");this.buffer=e.pop()||"";for(const r of e){const e=r.trim();if(e)try{const r=JSON.parse(e);this.handleMessage(r)}catch(r){this.debug&&console.error("[mcp-logger CLI] Failed to parse:",e.slice(0,100))}}}handleMessage(e){console.error("[mcp-logger CLI] Handling message:",JSON.stringify(e).slice(0,100)),"request"===this.direction&&e.method?this.logger.onRequest(e):"response"===this.direction&&(void 0===e.id||e.method?e.method&&!e.id&&this.logger.onNotification(e):this.logger.onResponse(e))}_flush(e){try{if(this.buffer.trim())try{const e=JSON.parse(this.buffer);this.handleMessage(e)}catch(e){this.debug&&console.error("[mcp-logger CLI] Failed to parse final buffer")}if(this.debug){const e=this.logger.getStats();console.error("[mcp-logger CLI] Stream closed. Pending requests: "+e.pending)}e()}catch(r){e(r)}}}const b=new Map,$=new Map;let T=0;const x=new Map;class J{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(r){if(this.debug&&console.error(`[mcp-logger CLI] 📥 REQUEST: method="${r.method}", id=${r.id}, params=${JSON.stringify(r.params)?.slice(0,100)}`),e(r.method)){if(!r.id)return this.debug&&console.error(`[mcp-logger CLI] 🔔 Notification detected: ${r.method} → Creating request-only span`),void this.createRequestOnlySpan(r,Date.now());this.pendingRequests.set(r.id,{request:r,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${r.method} (ID: ${r.id})`),setTimeout(()=>this.cleanupStale(r.id),this.TIMEOUT)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+r.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] 📤 RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const r=this.pendingRequests.get(e.id);if("initialize"===r?.request.method&&e.result?.serverInfo?.version){const r=e.result.serverInfo.version;w=r,this.debug&&console.error("[mcp-logger CLI] 🔍 MCP Server version detected: "+r)}if(!r)return this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} → Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const s=Date.now(),t=s-r.timestamp;this.debug&&console.error(`[mcp-logger CLI] ✅ Pairing success: ${r.request.method} (${t}ms)`),this.createPairedSpan(r.request,e,r.timestamp,s,t),this.pendingRequests.delete(e.id)}onNotification(r){this.debug&&console.error(`[mcp-logger CLI] 📢 NOTIFICATION (Server→Client): method="${r.method}", params=${JSON.stringify(r.params)?.slice(0,100)}`),e(r.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:r.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+r.method)}createPairedSpan(e,o,c,m,u){const d=a(e.method,e.params,o.result,void 0);!function(e){const o=r();if(!o)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const c="mcp."+e.method;o.startActiveSpan(c,r=>{try{const o={"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":i(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length,"mcp.response.id":e.request.id+"","mcp.response.timestamp":e.response.timestamp,"mcp.duration_ms":e.duration};if("tools/call"===e.method&&e.request.params){const r=e.request.params.name,s=e.request.params.arguments;if(r&&(o["mcp.tool.name"]=r),s){const e=i(s);e&&(o["mcp.tool.arguments"]=e,o["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if(void 0!==e.response.result){const r=i(e.response.result);r&&(o["mcp.response.result"]=r,o["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.request.headers){const r=i(e.request.headers);r&&(o["mcp.headers"]=r,o["mcp.headers.size"]=JSON.stringify(e.request.headers).length)}if("cli"===e.source){const e=s();e&&(o["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const r=t();o["mcp.server.version"]=r}const c=e.contextMetadata||a(e.method,e.request.params,e.response.result,e.request.headers);c.sessionId&&(o["mcp.session.id"]=c.sessionId),c.conversationId&&(o["mcp.conversation.id"]=c.conversationId),c.sessionId&&c.conversationId&&(o["mcp.trace.id"]=`${c.sessionId}:${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=p(e.method,e.request.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=>{r.addEvent("custom."+(e.level||"info"),{message:e.message,metadata:JSON.stringify(e.metadata||{}),timestamp:e.timestamp||Date.now()})})),e.response.error){let s;o["mcp.error"]=!0,s=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:i(e.response.error)||"Unknown error",o["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:f.ERROR,message:s})}else r.setStatus({code:f.OK});n(r,o)}catch(e){console.error("[MCP Logger] Error creating paired span:",e),r.setStatus({code:f.ERROR,message:e+""})}finally{r.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:c,params:e.params},response:{timestamp:m,result:o.result,error:o.error},duration:u,contextMetadata:d})}cleanupStale(e){const r=this.pendingRequests.get(e);r&&Date.now()-r.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] ⏰ Request timeout: ${r.request.method} (ID: ${e}) → Creating request-only span`),this.createRequestOnlySpan(r.request,r.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,o){const c=a(e.method,e.params,void 0,void 0);!function(e){const o=r();if(!o)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const c="mcp."+e.method;o.startActiveSpan(c,r=>{try{const o={"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":i(e.request.params)||"{}","mcp.request.params.size":JSON.stringify(e.request.params||{}).length};if("tools/call"===e.method&&e.request.params){const r=e.request.params.name,s=e.request.params.arguments;if(r&&(o["mcp.tool.name"]=r),s){const e=i(s);e&&(o["mcp.tool.arguments"]=e,o["mcp.tool.arguments.size"]=JSON.stringify(s).length)}}if("cli"===e.source){const e=s();e&&(o["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const r=t();o["mcp.server.version"]=r}const c=e.contextMetadata||a(e.method,e.request.params,void 0,e.request.headers);c.sessionId&&(o["mcp.session.id"]=c.sessionId),c.conversationId&&(o["mcp.conversation.id"]=c.conversationId),c.sessionId&&c.conversationId&&(o["mcp.trace.id"]=`${c.sessionId}:${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=p(e.method,e.request.params);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),r.setStatus({code:f.OK}),n(r,o)}catch(e){console.error("[MCP Logger] Error creating request-only span:",e),r.setStatus({code:f.ERROR,message:e+""})}finally{r.end()}})}({method:e.method,source:"cli",transport:"stdio",request:{id:e.id,timestamp:o,params:e.params},contextMetadata:c})}createResponseOnlySpan(e,o){const c=a(e.method||"unknown",void 0,e.result,void 0);!function(e){const o=r();if(!o)return void console.error("[MCP Logger] Tracer not initialized, cannot create span");const c="mcp."+e.method;o.startActiveSpan(c,r=>{try{const o={"mcp.source":e.source,"mcp.transport":e.transport,"mcp.response.id":e.response.id+"","mcp.response.method":e.method,"mcp.response.timestamp":e.response.timestamp};if(void 0!==e.response.result){const r=i(e.response.result);r&&(o["mcp.response.result"]=r,o["mcp.response.result.size"]=JSON.stringify(e.response.result).length)}if(e.response.error){let s;o["mcp.error"]=!0,s=e.response.error instanceof Error?e.response.error.message:"string"==typeof e.response.error?e.response.error:i(e.response.error)||"Unknown error",o["mcp.error.message"]=s,e.response.error instanceof Error&&r.recordException(e.response.error),r.setStatus({code:f.ERROR,message:s})}else r.setStatus({code:f.OK});if("cli"===e.source){const e=s();e&&(o["mcp.cli.server"]=JSON.stringify({command:e.command,args:e.args,env:e.env}));const r=t();o["mcp.server.version"]=r}const c=e.contextMetadata||a(e.method,void 0,e.response.result,void 0);c.sessionId&&(o["mcp.session.id"]=c.sessionId),c.conversationId&&(o["mcp.conversation.id"]=c.conversationId),c.sessionId&&c.conversationId&&(o["mcp.trace.id"]=`${c.sessionId}:${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),n(r,o)}catch(e){console.error("[MCP Logger] Error creating response-only span:",e),r.setStatus({code:f.ERROR,message:e+""})}finally{r.end()}})}({method:e.method||"same_request_method",source:"cli",transport:"stdio",response:{id:e.id,timestamp:o,result:e.result,error:e.error},contextMetadata:c})}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}u.name("mcp-logger").description("Add observability to any MCP server without code changes").version("1.0.0").requiredOption("-k, --api-key <key>","Aware API key").option("-s, --service-name <name>","Service name for identification").option("-e, --endpoint <url>","Custom OTLP endpoint").argument("<command...>","MCP server command to wrap").action(async(e,r)=>{try{const s=!1;!function(e){if(N)return L;const r=e.endpoint||R.ENDPOINT,s=e.serviceName||`${R.SERVICE_NAME_PREFIX}-${Math.random().toString(36).slice(2,8)}`;O=new d({resource:new g({"service.name":s,"service.version":R.SDK_VERSION}),traceExporter:new l({url:r,headers:{"x-api-key":e.apiKey}})}),O.start(),L=h.getTracer("mcp-logger",R.SDK_VERSION),N=!0;const t=async()=>{await o()};process.once("SIGTERM",t),process.once("SIGINT",t)}({...r,debug:s}),console.error("[MCP Logger CLI] Starting MCP server:",e.join(" "));const[t,...n]=e;!function(e,r){M={command:e,args:r,env:{}}}(t,n);const i=m(t,n,{stdio:["pipe","pipe","inherit"],env:{...process.env,MCP_LOGGER_ENABLED:"true"}}),c=new J(s),a=new P("request",c,s);process.stdin.pipe(a).pipe(i.stdin);const p=new P("response",c,s);i.stdout.pipe(p).pipe(process.stdout),i.on("error",async e=>{console.error("[MCP Logger CLI] Failed to start MCP server:",e),c.clear(),await o(),process.exit(1)}),i.on("exit",async(e,r)=>{console.error(`[MCP Logger CLI] MCP server exited with code ${e}, signal ${r}`),c.clear(),await o(),process.exit(e||0)});let u=!1;const f=async e=>{u||(u=!0,console.error(`[MCP Logger CLI] Received ${e}, shutting down...`),i.kill(e),await Promise.race([new Promise(e=>i.once("exit",e)),new Promise(e=>setTimeout(e,5e3))]),await o(),process.exit(0))};process.on("SIGTERM",()=>f("SIGTERM")),process.on("SIGINT",()=>f("SIGINT"))}catch(e){console.error("[MCP Logger CLI] Fatal error:",e),await o(),process.exit(1)}}),u.parse();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){return!
|
|
1
|
+
function e(e){return!a.includes(e)}function t(e,t,r,o){const s=function(e){if(!c.has(e)){const t=n();c.set(e,t)}return c.get(e)}("unknown-0"),i=function(e,t){const r=`${e}-${t}`;return p.has(r)||p.set(r,"conv-"+ ++u),p.get(r)}(s,e),a=function(e){if(e?.userId||e?.user_id)return e.userId||e.user_id}(t),l=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),m=function(e,t){return e.startsWith("resources/")?{type:e.split("/")[1],uri:t?.uri||t?.path||t?.resource}:e.startsWith("prompts/")?{type:"prompt",uri:t?.name||t?.promptId}:"tools/call"===e?{type:"tool",uri:t?.name}:t?.resourceType&&t?.resourceUri?{type:t.resourceType,uri:t.resourceUri}:{type:void 0,uri:void 0}}(e,t);m.uri&&function(e){const t=(d.get(e)||0)+1;d.set(e,t)}(m.uri);const g=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,r),h={};return s&&(h.sessionId=s),i&&(h.conversationId=i),a&&(h.userId=a),l&&(h.permissionLevel=l),void 0!==g&&(h.cost=g),h}import{readFileSync as r}from"fs";import{fileURLToPath as o}from"url";import{dirname as s,join as i}from"path";import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";import{ulid as n}from"ulid";!function(){try{const e=o(import.meta.url),t=s(e),n=i(t,"..","..","package.json");return JSON.parse(r(n,"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 l{pendingRequests=new Map;debug;TIMEOUT=3e4;constructor(e=!1){this.debug=e}onRequest(t){if(this.debug&&console.error(`[mcp-logger CLI] 📥 REQUEST: method="${t.method}", id=${t.id}, params=${JSON.stringify(t.params)?.slice(0,100)}`),e(t.method)){if(!t.id)return this.debug&&console.error(`[mcp-logger CLI] 🔔 Notification detected: ${t.method} → Creating request-only span`),void this.createRequestOnlySpan(t,Date.now());this.pendingRequests.set(t.id,{request:t,timestamp:Date.now()}),this.debug&&console.error(`[mcp-logger CLI] Request stored: ${t.method} (ID: ${t.id})`),setTimeout(()=>this.cleanupStale(t.id),this.TIMEOUT)}else this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+t.method)}onResponse(e){if(this.debug&&console.error(`[mcp-logger CLI] 📤 RESPONSE: id=${e.id}, hasResult=${!!e.result}, result=${JSON.stringify(e.result)?.slice(0,100)}, hasError=${!!e.error}`),!e.id)return void(this.debug&&console.error("[mcp-logger CLI] Response without ID, skipping"));const t=this.pendingRequests.get(e.id);if("initialize"===t?.request.method&&e.result?.serverInfo?.version){const t=e.result.serverInfo.version;this.debug&&console.error("[mcp-logger CLI] 🔍 MCP Server version detected: "+t)}if(!t)return this.debug&&console.error(`[mcp-logger CLI] ⏭️ No matching request for response ID: ${e.id} → Creating response-only span`),void this.createResponseOnlySpan(e,Date.now());const r=Date.now(),o=r-t.timestamp;this.debug&&console.error(`[mcp-logger CLI] ✅ Pairing success: ${t.request.method} (${o}ms)`),this.createPairedSpan(t.request,e,t.timestamp,r,o),this.pendingRequests.delete(e.id)}onNotification(t){this.debug&&console.error(`[mcp-logger CLI] 📢 NOTIFICATION (Server→Client): method="${t.method}", params=${JSON.stringify(t.params)?.slice(0,100)}`),e(t.method)?this.createResponseOnlySpan({jsonrpc:"2.0",id:`notification-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,method:t.method},Date.now()):this.debug&&console.error("[mcp-logger CLI] 🚫 Skipping excluded method: "+t.method)}createPairedSpan(e,r,o,s,i){t(e.method,e.params,r.result),e.method,e.id,e.params,r.result,r.error,console.error("[MCP Logger] Tracer not initialized, cannot create span")}cleanupStale(e){const t=this.pendingRequests.get(e);t&&Date.now()-t.timestamp>this.TIMEOUT&&(this.debug&&console.error(`[mcp-logger CLI] ⏰ Request timeout: ${t.request.method} (ID: ${e}) → Creating request-only span`),this.createRequestOnlySpan(t.request,t.timestamp),this.pendingRequests.delete(e))}createRequestOnlySpan(e,r){t(e.method,e.params,void 0),e.method,e.id,e.params,console.error("[MCP Logger] Tracer not initialized, cannot create span")}createResponseOnlySpan(e,r){t(e.method||"unknown",void 0,e.result),e.method,e.id,e.result,e.error,console.error("[MCP Logger] Tracer not initialized, cannot create span")}getStats(){return{pending:this.pendingRequests.size}}clear(){this.debug&&console.error(`[mcp-logger CLI] Clearing ${this.pendingRequests.size} pending requests`),this.pendingRequests.clear()}}export{l as MCPMessageLogger};
|
package/dist/core/config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
function r(r){return!a.includes(r)}import{readFileSync as t}from"fs";import{fileURLToPath as o}from"url";import{dirname as e,join as i}from"path";const n={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const r=o(import.meta.url),n=e(r),a=i(n,"..","..","package.json");return JSON.parse(t(a,"utf-8")).version||"1.0.0"}catch(r){return console.error("[MCP Logger] Failed to read package version:",r),"1.0.0"}}(),SERVICE_NAME_PREFIX:"mcp-server"},a=["notifications/initialized"];export{n as CONFIG,r as shouldTrackMethod};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
function r(r){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function e(r){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function o(r){console.error("[MCP Logger] Tracer not initialized, cannot create span")}import"@opentelemetry/api";import"@opentelemetry/sdk-node";import"@opentelemetry/exporter-trace-otlp-http";import"@opentelemetry/resources";import{readFileSync as t}from"fs";import{fileURLToPath as n}from"url";import{dirname as i,join as a}from"path";import"ulid";!function(){try{const r=n(import.meta.url),e=i(r),o=a(e,"..","..","package.json");return JSON.parse(t(o,"utf-8")).version||"1.0.0"}catch(r){return console.error("[MCP Logger] Failed to read package version:",r),"1.0.0"}}();export{r as createPairedSpan,e as createRequestOnlySpan,o as createResponseOnlySpan};
|
package/dist/core/telemetry.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e){if(
|
|
1
|
+
function e(e){if(I)return e.debug&&console.error("[MCP Logger] Telemetry already initialized, returning existing tracer"),S;const r=e.endpoint||y.ENDPOINT,o=e.serviceName||`${y.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: "+o)),E=new s({resource:new a({"service.name":o,"service.version":y.SDK_VERSION}),traceExporter:new u({url:r,headers:{"x-api-key":e.apiKey}})}),E.start(),S=m.getTracer("mcp-logger",y.SDK_VERSION),I=!0,e.debug&&console.error("[MCP Logger] Telemetry initialized successfully");const n=async()=>{e.debug&&console.error("[MCP Logger] Shutting down telemetry..."),await l()};return process.once("SIGTERM",n),process.once("SIGINT",n),S}function r(){return S}function o(){return I}function n(e,r,o){M={command:e,args:r,env:o}}function t(){return M}function i(e){N=e}function c(){return N||"unknown"}async function l(){if(E)try{await E.shutdown(),E=null,S=null,I=!1,M=null,N=null}catch(e){console.error("[MCP Logger] Error shutting down telemetry:",e)}}import{NodeSDK as s}from"@opentelemetry/sdk-node";import{OTLPTraceExporter as u}from"@opentelemetry/exporter-trace-otlp-http";import{Resource as a}from"@opentelemetry/resources";import{trace as m}from"@opentelemetry/api";import{readFileSync as g}from"fs";import{fileURLToPath as p}from"url";import{dirname as f,join as d}from"path";const y={ENDPOINT:"https://aware.mcypher.com/v1/traces",SDK_VERSION:function(){try{const e=p(import.meta.url),r=f(e),o=d(r,"..","..","package.json");return JSON.parse(g(o,"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 E=null,S=null,I=!1,M=null,N=null;export{t as getCLIServerInfo,c as getMCPServerVersion,r as getTracer,e as initializeTelemetry,o as isInitializedTelemetry,n as setCLIServerInfo,i as setMCPServerVersion,l as shutdownTelemetry};
|
package/dist/core/types.d.ts
CHANGED
|
@@ -97,9 +97,21 @@ interface MCPSpanData {
|
|
|
97
97
|
'mcp.events'?: string;
|
|
98
98
|
'mcp.events.count'?: number;
|
|
99
99
|
'mcp.cli.server'?: string;
|
|
100
|
+
'mcp.server.version'?: string;
|
|
100
101
|
'mcp.error'?: boolean;
|
|
101
102
|
'mcp.error.code'?: number;
|
|
102
103
|
'mcp.error.message'?: string;
|
|
104
|
+
'mcp.session.id'?: string;
|
|
105
|
+
'mcp.conversation.id'?: string;
|
|
106
|
+
'mcp.trace.id'?: string;
|
|
107
|
+
'mcp.user.id'?: string;
|
|
108
|
+
'mcp.client.name'?: string;
|
|
109
|
+
'mcp.client.version'?: string;
|
|
110
|
+
'mcp.permission.level'?: string;
|
|
111
|
+
'mcp.resource.type'?: string;
|
|
112
|
+
'mcp.resource.uri'?: string;
|
|
113
|
+
'mcp.resource.access_count'?: number;
|
|
114
|
+
'mcp.cost'?: number;
|
|
103
115
|
}
|
|
104
116
|
/**
|
|
105
117
|
* 커스텀 로그 엔트리
|
|
@@ -122,6 +134,18 @@ interface CustomLogEntry {
|
|
|
122
134
|
*/
|
|
123
135
|
timestamp?: number;
|
|
124
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* MCP 컨텍스트 메타데이터
|
|
139
|
+
*/
|
|
140
|
+
interface MCPContextMetadata {
|
|
141
|
+
sessionId?: string;
|
|
142
|
+
conversationId?: string;
|
|
143
|
+
userId?: string;
|
|
144
|
+
clientName?: string;
|
|
145
|
+
clientVersion?: string;
|
|
146
|
+
permissionLevel?: string;
|
|
147
|
+
cost?: number;
|
|
148
|
+
}
|
|
125
149
|
/**
|
|
126
150
|
* Paired Span 입력 데이터
|
|
127
151
|
*/
|
|
@@ -142,6 +166,7 @@ interface PairedSpanInput {
|
|
|
142
166
|
};
|
|
143
167
|
duration: number;
|
|
144
168
|
customEvents?: CustomLogEntry[];
|
|
169
|
+
contextMetadata?: MCPContextMetadata;
|
|
145
170
|
}
|
|
146
171
|
/**
|
|
147
172
|
* Response-only Span 입력 데이터 (notification 등 response가 없는 경우)
|
|
@@ -156,6 +181,7 @@ interface ResponseOnlySpanInput {
|
|
|
156
181
|
result?: any;
|
|
157
182
|
error?: any;
|
|
158
183
|
};
|
|
184
|
+
contextMetadata?: MCPContextMetadata;
|
|
159
185
|
}
|
|
160
186
|
/**
|
|
161
187
|
* Request-only Span 입력 데이터 (timeout 등으로 response가 없는 경우)
|
|
@@ -170,6 +196,7 @@ interface RequestOnlySpanInput {
|
|
|
170
196
|
params: any;
|
|
171
197
|
headers?: Record<string, string>;
|
|
172
198
|
};
|
|
199
|
+
contextMetadata?: MCPContextMetadata;
|
|
173
200
|
}
|
|
174
201
|
|
|
175
|
-
export type { CustomLogEntry, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse, MCPSpanData, MessageDirection, PairedSpanInput, RequestOnlySpanInput, ResponseOnlySpanInput, SpanAttributes, TelemetryOptions };
|
|
202
|
+
export type { CustomLogEntry, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse, MCPContextMetadata, MCPSpanData, MessageDirection, PairedSpanInput, RequestOnlySpanInput, ResponseOnlySpanInput, SpanAttributes, TelemetryOptions };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(){return
|
|
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=1e4){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 o=function(e){if(e){const r=e["user-agent"]||e["User-Agent"];if(r){const e=r.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(s),i=function(e){if(!M.has(e)){const r=y();M.set(e,r)}return M.get(e)}(`${o.name||"unknown"}-${o.version||"0"}`),c=function(e,r){const t=`${e}-${r}`;return O.has(t)||O.set(t,"conv-"+ ++R),O.get(t)}(i,e),a=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,s),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),u=n(e,r);u.uri&&function(e){const r=(L.get(e)||0)+1;L.set(e,r)}(u.uri);const p=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,t),l={};return i&&(l.sessionId=i),c&&(l.conversationId=c),a&&(l.userId=a),o.name&&(l.clientName=o.name),o.version&&(l.clientVersion=o.version),m&&(l.permissionLevel=m),void 0!==p&&(l.cost=p),l}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,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,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,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
|
|
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=1e4){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 o=function(e){if(e){const r=e["user-agent"]||e["User-Agent"];if(r){const e=r.match(/^([^\/]+)\/([^\s]+)/);if(e)return{name:e[1],version:e[2]}}}return{name:void 0,version:void 0}}(s),i=function(e){if(!M.has(e)){const r=y();M.set(e,r)}return M.get(e)}(`${o.name||"unknown"}-${o.version||"0"}`),c=function(e,r){const t=`${e}-${r}`;return O.has(t)||O.set(t,"conv-"+ ++R),O.get(t)}(i,e),a=function(e,r){return e?.userId||e?.user_id?e.userId||e.user_id:r?r["x-user-id"]||r["X-User-Id"]:void 0}(r,s),m=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(r),u=n(e,r);u.uri&&function(e){const r=(L.get(e)||0)+1;L.set(e,r)}(u.uri);const p=function(e,r){return void 0!==e?.cost?e.cost:void 0!==r?.cost?r.cost:void 0}(r,t),l={};return i&&(l.sessionId=i),c&&(l.conversationId=c),a&&(l.userId=a),o.name&&(l.clientName=o.name),o.version&&(l.clientVersion=o.version),m&&(l.permissionLevel=m),void 0!==p&&(l.cost=p),l}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,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,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,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){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function
|
|
1
|
+
function e(e,t,o,r){const n=function(e){if(!p.has(e)){const t=a();p.set(e,t)}return p.get(e)}("unknown-0"),s=function(e,t){const o=`${e}-${t}`;return m.has(o)||m.set(o,"conv-"+ ++l),m.get(o)}(n,e),i=function(e){if(e?.userId||e?.user_id)return e.userId||e.user_id}(t),c=function(e){if(e?.permission||e?.permissionLevel)return e.permission||e.permissionLevel}(t),u=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);u.uri&&function(e){const t=(d.get(e)||0)+1;d.set(e,t)}(u.uri);const g=function(e,t){return void 0!==e?.cost?e.cost:void 0!==t?.cost?t.cost:void 0}(t,o),f={};return n&&(f.sessionId=n),s&&(f.conversationId=s),i&&(f.userId=i),c&&(f.permissionLevel=c),void 0!==g&&(f.cost=g),f}function t(e){console.error("[MCP Logger] Tracer not initialized, cannot create span")}function o(o,r){const s=o.setRequestHandler.bind(o);o.setRequestHandler=function(o,i){const c=o.shape?.method,u=c?._def?.value||"unknown";return r.debug&&console.log("[mcp-logger] Instrumenting handler: "+u),s(o,async(o,s)=>{const c=Date.now(),a=(Math.random().toString(36).slice(2,8),[]),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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awarecorp/mcp-logger",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4-dev.1",
|
|
4
4
|
"description": "Unified MCP observability solution - SDK for code integration and CLI for zero-config wrapping",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -28,7 +28,11 @@
|
|
|
28
28
|
"version:patch": "npm version patch",
|
|
29
29
|
"version:minor": "npm version minor",
|
|
30
30
|
"version:major": "npm version major",
|
|
31
|
-
"version:dev": "npm version prerelease --preid=dev"
|
|
31
|
+
"version:dev": "npm version prerelease --preid=dev",
|
|
32
|
+
"version:stable": "npm version patch --no-git-tag-version && node -e \"const pkg=require('./package.json'); pkg.version=pkg.version.replace(/-dev\\.\\d+$/,''); require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\n')\"",
|
|
33
|
+
"version:next-dev:patch": "node -e \"const pkg=require('./package.json'); const match=pkg.version.match(/^(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)/); const newVer=match[1]+'.'+match[2]+'.'+(parseInt(match[3])+1)+'-dev.0'; pkg.version=newVer; require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\\\n'); console.log('Version updated to',newVer)\"",
|
|
34
|
+
"version:next-dev:minor": "node -e \"const pkg=require('./package.json'); const match=pkg.version.match(/^(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)/); const newVer=match[1]+'.'+(parseInt(match[2])+1)+'.0-dev.0'; pkg.version=newVer; require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\\\n'); console.log('Version updated to',newVer)\"",
|
|
35
|
+
"version:next-dev:major": "node -e \"const pkg=require('./package.json'); const match=pkg.version.match(/^(\\\\d+)\\\\.(\\\\d+)\\\\.(\\\\d+)/); const newVer=(parseInt(match[1])+1)+'.0.0-dev.0'; pkg.version=newVer; require('fs').writeFileSync('package.json',JSON.stringify(pkg,null,2)+'\\\\n'); console.log('Version updated to',newVer)\""
|
|
32
36
|
},
|
|
33
37
|
"keywords": [
|
|
34
38
|
"mcp",
|
|
@@ -65,7 +69,8 @@
|
|
|
65
69
|
"@opentelemetry/resources": "^1.25.0",
|
|
66
70
|
"@opentelemetry/sdk-node": "^0.52.0",
|
|
67
71
|
"@opentelemetry/semantic-conventions": "^1.25.0",
|
|
68
|
-
"commander": "^11.1.0"
|
|
72
|
+
"commander": "^11.1.0",
|
|
73
|
+
"ulid": "^3.0.1"
|
|
69
74
|
},
|
|
70
75
|
"devDependencies": {
|
|
71
76
|
"@modelcontextprotocol/sdk": "^0.5.0",
|