mcpspan 0.1.0 → 0.2.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 CHANGED
@@ -208,8 +208,9 @@ out of this as well.
208
208
  mode, not in debug.
209
209
 
210
210
  What is collected: the tool name, how long it took, whether it succeeded, the
211
- error type and a truncated message when it did not, which client called, and
212
- the SDK version. For a resource or a prompt, the same, under the name it was
211
+ error type and a truncated message when it did not, how large the answer was
212
+ in bytes (its size only, never its content), which client called, and the SDK
213
+ version. For a resource or a prompt, the same, under the name it was
213
214
  registered with: never the address a client read, only its template or, for
214
215
  an address the server does not have, its scheme.
215
216
 
package/dist/index.cjs CHANGED
@@ -146,7 +146,7 @@ var EventQueue = class {
146
146
  * Single source of truth: `package.json` follows this constant, not the other
147
147
  * way round, and a unit test fails if the two ever drift apart.
148
148
  */
149
- const SDK_VERSION = "0.1.0";
149
+ const SDK_VERSION = "0.2.0";
150
150
  //#endregion
151
151
  //#region src/transport.ts
152
152
  /** Path the ingest API accepts batches on, appended to the configured endpoint. */
@@ -782,15 +782,21 @@ function track(toolName, handler) {
782
782
  recorded = true;
783
783
  return;
784
784
  }
785
+ const size = responseBytes(value);
786
+ const measured = size === void 0 ? {} : { responseBytes: size };
785
787
  if (!isErrorResult(value)) {
786
- emit({ success: true });
788
+ emit({
789
+ success: true,
790
+ ...measured
791
+ });
787
792
  return;
788
793
  }
789
794
  const errorMessage = describeErrorResult(value);
790
795
  emit({
791
796
  success: false,
792
797
  errorSource: "result",
793
- ...errorMessage !== void 0 && { errorMessage }
798
+ ...errorMessage !== void 0 && { errorMessage },
799
+ ...measured
794
800
  });
795
801
  };
796
802
  const fail = (error) => {
@@ -903,7 +909,8 @@ function recordPrimitiveCall(call) {
903
909
  ...parameters !== void 0 && { parameters },
904
910
  timestamp: call.timestamp,
905
911
  sdkVersion: SDK_VERSION,
906
- ...call.sessionId !== void 0 && { sessionId: call.sessionId }
912
+ ...call.sessionId !== void 0 && { sessionId: call.sessionId },
913
+ ...call.responseBytes !== void 0 && { responseBytes: call.responseBytes }
907
914
  });
908
915
  } catch {}
909
916
  }
@@ -911,6 +918,21 @@ function recordPrimitiveCall(call) {
911
918
  function isRecording() {
912
919
  return sink !== void 0;
913
920
  }
921
+ /** The largest size an event carries; anything larger is sent as this (contract, 3.7). */
922
+ const MAX_RESPONSE_BYTES = 2147483647;
923
+ /**
924
+ * Size of an answer, in bytes of its compact JSON (contract, 3.7), or undefined
925
+ * when it cannot be encoded. The JSON is counted and dropped: nothing of it is
926
+ * kept or sent.
927
+ */
928
+ function responseBytes(value) {
929
+ try {
930
+ const json = JSON.stringify(value);
931
+ return json === void 0 ? void 0 : Math.min(Buffer.byteLength(json, "utf8"), MAX_RESPONSE_BYTES);
932
+ } catch {
933
+ return;
934
+ }
935
+ }
914
936
  /** A result the 2026-07-28 protocol calls interim: the tool needs more input first. */
915
937
  function isInputRequired(value) {
916
938
  return typeof value === "object" && value !== null && value.resultType === "input_required";
@@ -1226,7 +1248,8 @@ function watchPrimitive(handler, server) {
1226
1248
  durationMs: performance.now() - startedAt,
1227
1249
  sessionId: typeof context === "object" && context !== null ? sessionFor(server, context) : void 0,
1228
1250
  client: clientFor(server, typeof context === "object" && context !== null ? context : void 0),
1229
- serverVersion: serverVersionOf(server)
1251
+ serverVersion: serverVersionOf(server),
1252
+ responseBytes: outcome.responseBytes
1230
1253
  });
1231
1254
  } catch {}
1232
1255
  };
@@ -1249,7 +1272,10 @@ function watchPrimitive(handler, server) {
1249
1272
  });
1250
1273
  throw error;
1251
1274
  }
1252
- if (!(typeof result === "object" && result !== null && result.resultType === "input_required")) record({ success: true });
1275
+ if (!(typeof result === "object" && result !== null && result.resultType === "input_required")) record({
1276
+ success: true,
1277
+ responseBytes: responseBytes(result)
1278
+ });
1253
1279
  return result;
1254
1280
  };
1255
1281
  }
package/dist/index.d.cts CHANGED
@@ -194,6 +194,11 @@ interface ToolCallEvent {
194
194
  * the version the MCP server gives itself.
195
195
  */
196
196
  serverVersion?: string;
197
+ /**
198
+ * Size of the answer, in bytes of compact JSON (contract, 3.7). Only when the
199
+ * call returned one; the content is counted, never kept.
200
+ */
201
+ responseBytes?: number;
197
202
  /** When the call started, as an ISO 8601 timestamp. */
198
203
  timestamp: string;
199
204
  /** Version of the mcpspan package that produced the event. */
package/dist/index.d.mts CHANGED
@@ -194,6 +194,11 @@ interface ToolCallEvent {
194
194
  * the version the MCP server gives itself.
195
195
  */
196
196
  serverVersion?: string;
197
+ /**
198
+ * Size of the answer, in bytes of compact JSON (contract, 3.7). Only when the
199
+ * call returned one; the content is counted, never kept.
200
+ */
201
+ responseBytes?: number;
197
202
  /** When the call started, as an ISO 8601 timestamp. */
198
203
  timestamp: string;
199
204
  /** Version of the mcpspan package that produced the event. */
package/dist/index.mjs CHANGED
@@ -145,7 +145,7 @@ var EventQueue = class {
145
145
  * Single source of truth: `package.json` follows this constant, not the other
146
146
  * way round, and a unit test fails if the two ever drift apart.
147
147
  */
148
- const SDK_VERSION = "0.1.0";
148
+ const SDK_VERSION = "0.2.0";
149
149
  //#endregion
150
150
  //#region src/transport.ts
151
151
  /** Path the ingest API accepts batches on, appended to the configured endpoint. */
@@ -781,15 +781,21 @@ function track(toolName, handler) {
781
781
  recorded = true;
782
782
  return;
783
783
  }
784
+ const size = responseBytes(value);
785
+ const measured = size === void 0 ? {} : { responseBytes: size };
784
786
  if (!isErrorResult(value)) {
785
- emit({ success: true });
787
+ emit({
788
+ success: true,
789
+ ...measured
790
+ });
786
791
  return;
787
792
  }
788
793
  const errorMessage = describeErrorResult(value);
789
794
  emit({
790
795
  success: false,
791
796
  errorSource: "result",
792
- ...errorMessage !== void 0 && { errorMessage }
797
+ ...errorMessage !== void 0 && { errorMessage },
798
+ ...measured
793
799
  });
794
800
  };
795
801
  const fail = (error) => {
@@ -902,7 +908,8 @@ function recordPrimitiveCall(call) {
902
908
  ...parameters !== void 0 && { parameters },
903
909
  timestamp: call.timestamp,
904
910
  sdkVersion: SDK_VERSION,
905
- ...call.sessionId !== void 0 && { sessionId: call.sessionId }
911
+ ...call.sessionId !== void 0 && { sessionId: call.sessionId },
912
+ ...call.responseBytes !== void 0 && { responseBytes: call.responseBytes }
906
913
  });
907
914
  } catch {}
908
915
  }
@@ -910,6 +917,21 @@ function recordPrimitiveCall(call) {
910
917
  function isRecording() {
911
918
  return sink !== void 0;
912
919
  }
920
+ /** The largest size an event carries; anything larger is sent as this (contract, 3.7). */
921
+ const MAX_RESPONSE_BYTES = 2147483647;
922
+ /**
923
+ * Size of an answer, in bytes of its compact JSON (contract, 3.7), or undefined
924
+ * when it cannot be encoded. The JSON is counted and dropped: nothing of it is
925
+ * kept or sent.
926
+ */
927
+ function responseBytes(value) {
928
+ try {
929
+ const json = JSON.stringify(value);
930
+ return json === void 0 ? void 0 : Math.min(Buffer.byteLength(json, "utf8"), MAX_RESPONSE_BYTES);
931
+ } catch {
932
+ return;
933
+ }
934
+ }
913
935
  /** A result the 2026-07-28 protocol calls interim: the tool needs more input first. */
914
936
  function isInputRequired(value) {
915
937
  return typeof value === "object" && value !== null && value.resultType === "input_required";
@@ -1225,7 +1247,8 @@ function watchPrimitive(handler, server) {
1225
1247
  durationMs: performance.now() - startedAt,
1226
1248
  sessionId: typeof context === "object" && context !== null ? sessionFor(server, context) : void 0,
1227
1249
  client: clientFor(server, typeof context === "object" && context !== null ? context : void 0),
1228
- serverVersion: serverVersionOf(server)
1250
+ serverVersion: serverVersionOf(server),
1251
+ responseBytes: outcome.responseBytes
1229
1252
  });
1230
1253
  } catch {}
1231
1254
  };
@@ -1248,7 +1271,10 @@ function watchPrimitive(handler, server) {
1248
1271
  });
1249
1272
  throw error;
1250
1273
  }
1251
- if (!(typeof result === "object" && result !== null && result.resultType === "input_required")) record({ success: true });
1274
+ if (!(typeof result === "object" && result !== null && result.resultType === "input_required")) record({
1275
+ success: true,
1276
+ responseBytes: responseBytes(result)
1277
+ });
1252
1278
  return result;
1253
1279
  };
1254
1280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcpspan",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Self-hosted analytics for MCP servers: which tools, resources and prompts get used, by which client, how fast, and why they fail.",
5
5
  "keywords": [
6
6
  "mcp",