mcp-telemetry-sdk 0.1.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/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # mcp-telemetry-sdk
2
+
3
+ Zero-dependency instrumentation for MCP servers. Emit `job`/`step`/`log`/`cost` telemetry from your tool handlers; any `mcp-telemetry-server` (or your own collector) picks it up over a local socket.
4
+
5
+ Full documentation, architecture, and the companion server: [github.com/arnavranjan005/mcp-telemetry](https://github.com/arnavranjan005/mcp-telemetry)
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install mcp-telemetry-sdk
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```js
16
+ import { MCPTelemetry } from 'mcp-telemetry-sdk';
17
+
18
+ const telemetry = new MCPTelemetry(); // zero config
19
+
20
+ server.tool('deploy', schema, async (args) => {
21
+ const job = telemetry.createJob({ task: 'deploy' });
22
+ job.start();
23
+ job.stepStart('build');
24
+ await runBuild();
25
+ job.stepDone('build');
26
+ await job.done(0);
27
+ return { content: [{ type: 'text', text: 'done' }] };
28
+ });
29
+ ```
30
+
31
+ If nothing is listening on the socket, every call is a fast no-op — this library never throws and never blocks your tool call.
32
+
33
+ See the [main README](https://github.com/arnavranjan005/mcp-telemetry#api-reference) for the full API reference.
34
+
35
+ ## License
36
+
37
+ MIT
@@ -0,0 +1,15 @@
1
+ export declare class QueuedConnection {
2
+ private readonly socketPath;
3
+ private socket;
4
+ private connecting;
5
+ private closed;
6
+ private nextAttemptAt;
7
+ private readonly queue;
8
+ constructor(socketPath: string);
9
+ send(line: string): void;
10
+ private flush;
11
+ private connect;
12
+ close(): void;
13
+ drain(maxWaitMs?: number): Promise<void>;
14
+ }
15
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAaA,qBAAa,gBAAgB;IAOf,OAAO,CAAC,QAAQ,CAAC,UAAU;IANvC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;gBAET,UAAU,EAAE,MAAM;IAE/C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAOxB,OAAO,CAAC,KAAK;IAab,OAAO,CAAC,OAAO;IA2Bf,KAAK,IAAI,IAAI;IAeb,KAAK,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAmBvC"}
@@ -0,0 +1,108 @@
1
+ import net from 'net';
2
+ const CONNECT_TIMEOUT_MS = 300;
3
+ const RETRY_DELAY_MS = 250;
4
+ const MAX_QUEUE = 1000;
5
+ // One persistent socket per socketPath, shared by every JobHandle created
6
+ // from the same MCPTelemetry instance — replaces the old connect-per-event
7
+ // emitter. Writes are queued and flushed in order over a single connection;
8
+ // if the collector isn't reachable yet (or drops), lines stay queued and are
9
+ // flushed on the next successful connect, up to MAX_QUEUE entries (oldest
10
+ // dropped beyond that — this is still best-effort telemetry, never a
11
+ // blocking or throwing guarantee for the caller).
12
+ export class QueuedConnection {
13
+ socketPath;
14
+ socket = null;
15
+ connecting = false;
16
+ closed = false;
17
+ nextAttemptAt = 0;
18
+ queue = [];
19
+ constructor(socketPath) {
20
+ this.socketPath = socketPath;
21
+ }
22
+ send(line) {
23
+ if (this.closed)
24
+ return;
25
+ this.queue.push(line);
26
+ if (this.queue.length > MAX_QUEUE)
27
+ this.queue.shift();
28
+ this.flush();
29
+ }
30
+ flush() {
31
+ if (this.closed || !this.queue.length)
32
+ return;
33
+ if (this.socket && this.socket.writable) {
34
+ const batch = this.queue.splice(0, this.queue.length);
35
+ for (const item of batch)
36
+ this.socket.write(item);
37
+ return;
38
+ }
39
+ if (this.connecting || Date.now() < this.nextAttemptAt)
40
+ return;
41
+ this.connect();
42
+ }
43
+ connect() {
44
+ this.connecting = true;
45
+ const socket = net.createConnection({ path: this.socketPath });
46
+ socket.unref(); // never keep the host process alive just for this
47
+ const timer = setTimeout(() => socket.destroy(), CONNECT_TIMEOUT_MS);
48
+ socket.on('connect', () => {
49
+ clearTimeout(timer);
50
+ this.connecting = false;
51
+ this.socket = socket;
52
+ this.flush();
53
+ });
54
+ socket.on('error', () => {
55
+ clearTimeout(timer);
56
+ this.connecting = false;
57
+ this.nextAttemptAt = Date.now() + RETRY_DELAY_MS;
58
+ if (this.socket === socket)
59
+ this.socket = null;
60
+ });
61
+ socket.on('close', () => {
62
+ this.connecting = false;
63
+ if (this.socket === socket)
64
+ this.socket = null;
65
+ });
66
+ }
67
+ close() {
68
+ this.closed = true;
69
+ this.queue.length = 0;
70
+ this.socket?.end();
71
+ this.socket = null;
72
+ }
73
+ // Actively retry delivery of whatever is currently queued instead of
74
+ // passively waiting for another send() to trigger the next attempt — for
75
+ // the last event in a job's lifecycle (job_done), nothing else may ever
76
+ // call send() again, so the normal retry-on-next-send behavior would mean
77
+ // that event silently never gets a second chance if the host process exits
78
+ // right after. Unlike connect()'s socket, this deliberately uses a
79
+ // ref'd setTimeout so it keeps the process alive for up to maxWaitMs —
80
+ // a bounded delay to give the last event a real chance, not indefinite.
81
+ drain(maxWaitMs = 1500) {
82
+ if (this.closed || !this.queue.length)
83
+ return Promise.resolve();
84
+ return new Promise((resolve) => {
85
+ const deadline = Date.now() + maxWaitMs;
86
+ const attempt = () => {
87
+ if (this.closed || !this.queue.length) {
88
+ resolve();
89
+ return;
90
+ }
91
+ if (this.socket && this.socket.writable) {
92
+ this.flush();
93
+ resolve();
94
+ return;
95
+ }
96
+ if (Date.now() >= deadline) {
97
+ resolve();
98
+ return;
99
+ }
100
+ if (!this.connecting)
101
+ this.connect();
102
+ setTimeout(attempt, RETRY_DELAY_MS);
103
+ };
104
+ attempt();
105
+ });
106
+ }
107
+ }
108
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { JobHandle } from './job.js';
2
+ export interface CreateJobOptions {
3
+ id?: string;
4
+ task: string;
5
+ }
6
+ export interface MCPTelemetryOptions {
7
+ socketPath?: string;
8
+ }
9
+ export declare class MCPTelemetry {
10
+ private readonly connection;
11
+ constructor(opts?: MCPTelemetryOptions);
12
+ createJob(opts: CreateJobOptions): JobHandle;
13
+ disconnect(): void;
14
+ }
15
+ export { JobHandle } from './job.js';
16
+ export { MonitorEvent, TaskStatus, getSocketPath } from './protocol.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAIrC,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,qBAAa,YAAY;IAGvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;gBAElC,IAAI,GAAE,mBAAwB;IAI1C,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS;IAQ5C,UAAU,IAAI,IAAI;CAGnB;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ import { JobHandle } from './job.js';
2
+ import { QueuedConnection } from './connection.js';
3
+ import { getSocketPath } from './protocol.js';
4
+ let counter = 0;
5
+ export class MCPTelemetry {
6
+ // One persistent connection per instance, shared by every job it creates —
7
+ // replaces opening a fresh socket for every single event.
8
+ connection;
9
+ constructor(opts = {}) {
10
+ this.connection = new QueuedConnection(opts.socketPath ?? getSocketPath());
11
+ }
12
+ createJob(opts) {
13
+ const jobId = opts.id ?? `job-${++counter}`;
14
+ return new JobHandle(jobId, opts.task, this.connection);
15
+ }
16
+ // Call when the host MCP server is done emitting telemetry (e.g. on
17
+ // shutdown, or once a job's lifecycle is fully over) to drop the
18
+ // connection instead of leaving it idle.
19
+ disconnect() {
20
+ this.connection.close();
21
+ }
22
+ }
23
+ export { JobHandle } from './job.js';
24
+ export { getSocketPath } from './protocol.js';
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAW9C,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,MAAM,OAAO,YAAY;IACvB,2EAA2E;IAC3E,0DAA0D;IACzC,UAAU,CAAmB;IAE9C,YAAY,OAA4B,EAAE;QACxC,IAAI,CAAC,UAAU,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,IAAI,aAAa,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,SAAS,CAAC,IAAsB;QAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,OAAO,EAAE,CAAC;QAC5C,OAAO,IAAI,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1D,CAAC;IAED,oEAAoE;IACpE,iEAAiE;IACjE,yCAAyC;IACzC,UAAU;QACR,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;CACF;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAA4B,aAAa,EAAE,MAAM,eAAe,CAAC"}
package/dist/job.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import type { QueuedConnection } from './connection.js';
2
+ export declare class JobHandle {
3
+ private readonly jobId;
4
+ private readonly task;
5
+ private readonly connection;
6
+ constructor(jobId: string, task: string, connection: QueuedConnection);
7
+ private send;
8
+ start(): void;
9
+ done(exitCode?: number): Promise<void>;
10
+ stepStart(step: string, meta?: Record<string, unknown>): void;
11
+ stepDone(step: string, meta?: Record<string, unknown>): void;
12
+ stepFailed(step: string, reason?: string): void;
13
+ log(line: string, stream?: 'stdout' | 'stderr'): void;
14
+ cost(amount: number, meta?: Record<string, unknown>): void;
15
+ }
16
+ //# sourceMappingURL=job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKxD,qBAAa,SAAS;IAElB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAFV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,gBAAgB;IAG/C,OAAO,CAAC,IAAI;IAIZ,KAAK,IAAI,IAAI;IAQP,IAAI,CAAC,QAAQ,SAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI7D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAI/C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,IAAI;IAIrD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAG3D"}
package/dist/job.js ADDED
@@ -0,0 +1,41 @@
1
+ function now() { return new Date().toISOString(); }
2
+ export class JobHandle {
3
+ jobId;
4
+ task;
5
+ connection;
6
+ constructor(jobId, task, connection) {
7
+ this.jobId = jobId;
8
+ this.task = task;
9
+ this.connection = connection;
10
+ }
11
+ send(event) {
12
+ this.connection.send(JSON.stringify(event) + '\n');
13
+ }
14
+ start() {
15
+ this.send({ type: 'job_start', jobId: this.jobId, task: this.task, timestamp: now() });
16
+ }
17
+ // Async because this is the terminal event — nothing else may ever call
18
+ // send() again on this job, so it actively drains instead of relying on a
19
+ // future send() to retry delivery (see QueuedConnection.drain). Safe to
20
+ // call without awaiting; the drain still runs in the background either way.
21
+ async done(exitCode = 0) {
22
+ this.send({ type: 'job_done', jobId: this.jobId, exitCode, timestamp: now() });
23
+ await this.connection.drain();
24
+ }
25
+ stepStart(step, meta) {
26
+ this.send({ type: 'step_start', jobId: this.jobId, step, meta, timestamp: now() });
27
+ }
28
+ stepDone(step, meta) {
29
+ this.send({ type: 'step_done', jobId: this.jobId, step, meta, timestamp: now() });
30
+ }
31
+ stepFailed(step, reason) {
32
+ this.send({ type: 'step_failed', jobId: this.jobId, step, reason, timestamp: now() });
33
+ }
34
+ log(line, stream) {
35
+ this.send({ type: 'log', jobId: this.jobId, line, stream, timestamp: now() });
36
+ }
37
+ cost(amount, meta) {
38
+ this.send({ type: 'cost', jobId: this.jobId, amount, meta, timestamp: now() });
39
+ }
40
+ }
41
+ //# sourceMappingURL=job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job.js","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAGA,SAAS,GAAG,KAAa,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AAE3D,MAAM,OAAO,SAAS;IAED;IACA;IACA;IAHnB,YACmB,KAAa,EACb,IAAY,EACZ,UAA4B;QAF5B,UAAK,GAAL,KAAK,CAAQ;QACb,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAkB;IAC5C,CAAC;IAEI,IAAI,CAAC,KAAmB;QAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACrD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,wEAAwE;IACxE,0EAA0E;IAC1E,wEAAwE;IACxE,4EAA4E;IAC5E,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC;QACrB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAChC,CAAC;IAED,SAAS,CAAC,IAAY,EAAE,IAA8B;QACpD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,QAAQ,CAAC,IAAY,EAAE,IAA8B;QACnD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,UAAU,CAAC,IAAY,EAAE,MAAe;QACtC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,GAAG,CAAC,IAAY,EAAE,MAA4B;QAC5C,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,IAAI,CAAC,MAAc,EAAE,IAA8B;QACjD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACjF,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ export type TaskStatus = 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
2
+ export type MonitorEvent = {
3
+ type: 'job_start';
4
+ jobId: string;
5
+ task: string;
6
+ timestamp: string;
7
+ } | {
8
+ type: 'job_done';
9
+ jobId: string;
10
+ exitCode: number;
11
+ timestamp: string;
12
+ } | {
13
+ type: 'step_start';
14
+ jobId: string;
15
+ step: string;
16
+ meta?: Record<string, unknown>;
17
+ timestamp: string;
18
+ } | {
19
+ type: 'step_done';
20
+ jobId: string;
21
+ step: string;
22
+ meta?: Record<string, unknown>;
23
+ timestamp: string;
24
+ } | {
25
+ type: 'step_failed';
26
+ jobId: string;
27
+ step: string;
28
+ reason?: string;
29
+ timestamp: string;
30
+ } | {
31
+ type: 'log';
32
+ jobId: string;
33
+ line: string;
34
+ stream?: 'stdout' | 'stderr';
35
+ timestamp: string;
36
+ } | {
37
+ type: 'cost';
38
+ jobId: string;
39
+ amount: number;
40
+ meta?: Record<string, unknown>;
41
+ timestamp: string;
42
+ };
43
+ export declare function getSocketPath(root?: string): string;
44
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,gBAAgB,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7F,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAmC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,IAAI,EAAE,UAAU,CAAC;IAAI,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAgC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1G;IAAE,IAAI,EAAE,YAAY,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAiB,SAAS,EAAE,MAAM,CAAA;CAAE,GACzG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAS,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAAM,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1G;IAAE,IAAI,EAAE,MAAM,CAAC;IAAQ,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9G,wBAAgB,aAAa,CAAC,IAAI,SAAgB,GAAG,MAAM,CAY1D"}
@@ -0,0 +1,16 @@
1
+ import { createHash } from 'crypto';
2
+ import { join, resolve } from 'path';
3
+ export function getSocketPath(root = process.cwd()) {
4
+ // Resolve (and, on Windows, lowercase) so callers pointing at the same
5
+ // directory always hash to the same pipe/socket regardless of trailing
6
+ // slashes or drive-letter casing — a mismatch here silently splits
7
+ // producers and the collector onto two different sockets with no error
8
+ // on either side. Unix paths stay case-sensitive.
9
+ const resolved = resolve(root);
10
+ if (process.platform === 'win32') {
11
+ const hash = createHash('md5').update(resolved.toLowerCase()).digest('hex').slice(0, 8);
12
+ return `\\\\.\\pipe\\mcp-telemetry-${hash}`;
13
+ }
14
+ return join(resolved, '.mcp-telemetry.sock');
15
+ }
16
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAcrC,MAAM,UAAU,aAAa,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE;IAChD,uEAAuE;IACvE,uEAAuE;IACvE,mEAAmE;IACnE,uEAAuE;IACvE,kDAAkD;IAClD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxF,OAAO,8BAA8B,IAAI,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC;AAC/C,CAAC"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "mcp-telemetry-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Zero-dependency instrumentation library for MCP servers — emit job/step/log/cost telemetry that any mcp-telemetry-server (or your own collector) can pick up.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Arnav Ranjan <arnavranjan005@gmail.com>",
8
+ "keywords": ["mcp", "model-context-protocol", "telemetry", "observability", "ai-agent", "monitoring", "progress", "notifications", "claude-code", "long-running-tasks", "mcp-server"],
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/arnavranjan005/mcp-telemetry.git",
12
+ "directory": "packages/sdk"
13
+ },
14
+ "homepage": "https://github.com/arnavranjan005/mcp-telemetry/tree/main/packages/sdk#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/arnavranjan005/mcp-telemetry/issues"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "main": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "import": "./dist/index.js",
26
+ "types": "./dist/index.d.ts"
27
+ }
28
+ },
29
+ "files": ["dist/"],
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "dev": "tsc --watch",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0",
40
+ "typescript": "^5.5.0"
41
+ }
42
+ }