@armature-tech/mcp-analytics 0.2.5 → 0.4.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @armature-tech/mcp-analytics
2
2
 
3
- Wrapper SDK that instruments MCP servers with analytics telemetry. It decorates each registered tool's input schema with optional `telemetry.*` fields, strips those fields before the original handler runs, and posts a signed ingest batch to Armature after the handler returns.
3
+ Wrapper SDK that instruments MCP servers with analytics telemetry. It decorates each registered tool's input schema with optional `telemetry.*` fields, strips those fields before the original handler runs, and posts an authenticated ingest batch to Armature after the handler returns.
4
4
 
5
5
  The SDK is a drop-in for any server built on `@modelcontextprotocol/sdk`'s `McpServer`. It does not introduce a middleware server and does not modify the arguments the original handler sees.
6
6
 
@@ -11,9 +11,13 @@ It also exposes recorder primitives for dispatcher-style MCP servers that hand-r
11
11
  ## Install
12
12
 
13
13
  ```sh
14
- npm install @armature-tech/mcp-analytics @modelcontextprotocol/sdk zod
14
+ npx skills add armature-tech/mcp-analytics --global && npm install @armature-tech/mcp-analytics @modelcontextprotocol/sdk zod
15
15
  ```
16
16
 
17
+ Then ask your coding agent (Claude Code, Cursor, Codex, Gemini, …): *"install Armature analytics on this MCP server"*. The `install-mcp-analytics` skill ([`SKILL.md`](SKILL.md)) detects which of the four integration shapes fits your repo (registry-style `McpServer`, drop-in factory, dispatcher, or Mastra `MCPServer`), makes the edits, and verifies the wiring.
18
+
19
+ Not using an agent? Just run the `npm install` part and follow the [Quick start](#quick-start) below. The `skills` CLI in the first half of the command is the open [`vercel-labs/skills`](https://github.com/vercel-labs/skills) installer — it copies `SKILL.md` into your agent's skill directory.
20
+
17
21
  ## Quick start
18
22
 
19
23
  Two ways to wire this in, depending on whether you're adopting it on a new server or layering it on top of an existing `McpServer` factory.
@@ -27,8 +31,7 @@ import { z } from "zod";
27
31
  const analytics = createAnalyticsRecorder({
28
32
  armature: {
29
33
  endpointUrl: "https://app.armature.tech/api/mcp-analytics/ingest",
30
- mcpServerId: process.env.ANALYTICS_MCP_SERVER_ID,
31
- ingestSecret: process.env.ANALYTICS_INGEST_SECRET,
34
+ apiKey: process.env.ANALYTICS_INGEST_API_KEY,
32
35
  },
33
36
  });
34
37
 
@@ -60,8 +63,7 @@ const server = createMcpAnalyticsServer(
60
63
  {
61
64
  armature: {
62
65
  endpointUrl: "https://app.armature.tech/api/mcp-analytics/ingest",
63
- mcpServerId: process.env.ANALYTICS_MCP_SERVER_ID,
64
- ingestSecret: process.env.ANALYTICS_INGEST_SECRET,
66
+ apiKey: process.env.ANALYTICS_INGEST_API_KEY,
65
67
  },
66
68
  },
67
69
  );
@@ -79,8 +81,7 @@ import { createAnalyticsRecorder } from "@armature-tech/mcp-analytics";
79
81
  const analytics = createAnalyticsRecorder({
80
82
  armature: {
81
83
  endpointUrl: "https://app.armature.tech/api/mcp-analytics/ingest",
82
- mcpServerId: process.env.ANALYTICS_MCP_SERVER_ID,
83
- ingestSecret: process.env.ANALYTICS_INGEST_SECRET,
84
+ apiKey: process.env.ANALYTICS_INGEST_API_KEY,
84
85
  delivery: "await",
85
86
  actorId: ({ ctx }) => (ctx as RequestContext).userProfileId,
86
87
  },
@@ -128,8 +129,7 @@ new MCPServer({
128
129
  tools: wrapMastraTools(createMyTools(), {
129
130
  armature: {
130
131
  endpointUrl: "https://app.armature.tech/api/mcp-analytics/ingest",
131
- mcpServerId: process.env.ANALYTICS_MCP_SERVER_ID,
132
- ingestSecret: process.env.ANALYTICS_INGEST_SECRET,
132
+ apiKey: process.env.ANALYTICS_INGEST_API_KEY,
133
133
  delivery: "await",
134
134
  },
135
135
  }),
@@ -225,7 +225,7 @@ The `telemetry` object is stripped before your handler runs. Your tool receives
225
225
 
226
226
  ### What Armature receives
227
227
 
228
- After each tool call (success or error), the SDK posts a signed batch to `endpointUrl` containing a `tool_call` event with timing, status, the input/output previews, and the telemetry fields the agent supplied. The first event for a new `sessionId` is preceded by a `session_init` event.
228
+ After each tool call (success or error), the SDK posts an authenticated batch to `endpointUrl` containing a `tool_call` event with timing, status, the input/output previews, and the telemetry fields the agent supplied. The first event for a new `sessionId` is preceded by a `session_init` event.
229
229
 
230
230
  ## Configuration
231
231
 
@@ -236,8 +236,7 @@ type McpAnalyticsConfig = {
236
236
  };
237
237
  armature?: {
238
238
  endpointUrl?: string; // default reads ANALYTICS_INGEST_URL
239
- mcpServerId?: string; // default reads ANALYTICS_MCP_SERVER_ID
240
- ingestSecret?: string; // default reads ANALYTICS_INGEST_SECRET
239
+ apiKey?: string; // default reads ANALYTICS_INGEST_API_KEY
241
240
  actorId?: string | ((input) => string | Promise<string>);
242
241
  enabled?: boolean; // default true
243
242
  delivery?: "background" | "await"; // default "background"
@@ -250,18 +249,17 @@ type McpAnalyticsConfig = {
250
249
 
251
250
  Notes:
252
251
 
253
- - If `ingestSecret` or `mcpServerId` is missing, the SDK silently skips delivery — useful for local development.
252
+ - If `apiKey` is missing, the SDK silently skips delivery — useful for local development.
254
253
  - `delivery: "background"` (default) returns the tool result immediately and posts the batch on `setImmediate`. Use `delivery: "await"` in serverless environments where the function may exit before background work finishes.
255
- - The actor id is a SHA-256 of `mcpServerId` + an actor seed. By default the seed comes from the request's auth token / client id / authorization header. Pass a static `armature.actorId` seed for a stable source, or pass a function to derive the seed from `{ ctx, extra, headers, authInfo, toolName, telemetry }`.
256
- - The signed request includes `X-Armature-Timestamp`, `X-Armature-Signature` (HMAC-SHA256 of `timestamp.body`), and `X-Armature-MCP-Server-Id` headers.
254
+ - The actor id is a SHA-256 of the actor seed. By default the seed comes from the request's auth token / client id / authorization header. Pass a static `armature.actorId` seed for a stable source, or pass a function to derive the seed from `{ ctx, extra, headers, authInfo, toolName, telemetry }`. Armature scopes the resulting actor id to your server via the API key, so the same seed under two different servers stays linked to the same person (cross-surface analytics).
255
+ - Each batch is POSTed with `Authorization: Bearer <apiKey>`. The server identity is resolved from the API key — no separate header.
257
256
 
258
257
  ## Environment variables
259
258
 
260
259
  | Variable | Purpose |
261
260
  | --- | --- |
262
261
  | `ANALYTICS_INGEST_URL` | Ingest endpoint (defaults to the local mock at `http://127.0.0.1:8787/api/mcp-analytics/ingest`) |
263
- | `ANALYTICS_MCP_SERVER_ID` | The MCP server's id registered with Armature |
264
- | `ANALYTICS_INGEST_SECRET` | Shared secret used to sign each batch |
262
+ | `ANALYTICS_INGEST_API_KEY` | Your Armature API key — identifies the MCP server and signs each batch |
265
263
 
266
264
  ## Lower-level exports
267
265
 
@@ -272,7 +270,6 @@ For custom integrations the package also exports building blocks used internally
272
270
  - `decorateInputSchemaWithTelemetry(schema, config)` / `createTelemetryInputSchema(config)` / `createTelemetryJsonSchema(config)`
273
271
  - `extractTelemetryArguments(args)` — split `{ telemetry, ...args }`
274
272
  - `buildToolCallEvent(...)`, `buildActorId(...)`, `buildEventId(...)`
275
- - `signIngestBody(body, secret, timestamp)`
276
273
  - `postTelemetryEvent(batch, config)` / `emitTelemetryEvent(batch, config)`
277
274
  - `defaultMcpAnalyticsConfig`
278
275
 
package/SKILL.md CHANGED
@@ -7,7 +7,7 @@ description: >
7
7
  "instrument my tools", "wire mcp-analytics into our server". Detects which integration
8
8
  shape fits the repo (registry-style McpServer, drop-in factory, dispatcher, or Mastra
9
9
  MCPServer), makes the edits, and verifies the wiring by checking the schema includes
10
- the telemetry block and a test tool call produces a signed batch.
10
+ the telemetry block and a test tool call produces an authenticated batch.
11
11
  ---
12
12
 
13
13
  # Install @armature-tech/mcp-analytics into an MCP server
@@ -15,7 +15,7 @@ description: >
15
15
  You are integrating the `@armature-tech/mcp-analytics` SDK into a customer's MCP server
16
16
  codebase. The SDK decorates each tool's input schema with a `telemetry.*` block (so the
17
17
  agent can pass `intent`, `context`, `frustration_level`), strips those fields before the
18
- handler runs, and posts a signed batch to Armature after each call.
18
+ handler runs, and posts an authenticated batch to Armature after each call.
19
19
 
20
20
  The hard part is picking the right integration shape and not breaking the existing server.
21
21
  Four shapes exist; pick one based on how the customer's code looks today.
@@ -50,31 +50,24 @@ npm install @armature-tech/mcp-analytics
50
50
  If they aren't, install them too. Use the customer's package manager (check for
51
51
  `pnpm-lock.yaml` / `yarn.lock` / `bun.lockb` and match it).
52
52
 
53
- The package is published to GitHub Packages under the `@armature-tech` scope. If `npm install`
54
- fails with 404, the project needs a `.npmrc`:
53
+ The package is published to the public npm registry `npm install` works without any
54
+ `.npmrc` configuration.
55
55
 
56
- ```
57
- @armature-tech:registry=https://npm.pkg.github.com
58
- ```
59
-
60
- Mention this once; don't litter the project with auth instructions.
61
-
62
- ## Step 3: Add the three environment variables
56
+ ## Step 3: Add the API key environment variable
63
57
 
64
- The SDK needs:
58
+ The SDK needs one credential, plus an optional URL override:
65
59
 
66
60
  | Variable | What it is |
67
61
  | --- | --- |
68
- | `ANALYTICS_INGEST_URL` | `https://app.armature.tech/api/mcp-analytics/ingest` for prod |
69
- | `ANALYTICS_MCP_SERVER_ID` | The MCP server id from the Armature dashboard |
70
- | `ANALYTICS_INGEST_SECRET` | The shared secret from the Armature dashboard |
62
+ | `ANALYTICS_INGEST_API_KEY` | Your Armature API key (created in the dashboard). Identifies the MCP server and signs each batch. |
63
+ | `ANALYTICS_INGEST_URL` | Optional. Defaults to the prod endpoint `https://app.armature.tech/api/mcp-analytics/ingest`. Override for a local mock or staging environment. |
71
64
 
72
- Add them to whatever env mechanism the project uses (`.env.example`, `wrangler.toml`,
73
- `vercel.json`, fly secrets, k8s manifests). Do **not** commit real values; put placeholders
74
- in `.env.example` and tell the user where to paste the real ones.
65
+ Add `ANALYTICS_INGEST_API_KEY` to whatever env mechanism the project uses (`.env.example`,
66
+ `wrangler.toml`, `vercel.json`, fly secrets, k8s manifests). Do **not** commit real values;
67
+ put a placeholder in `.env.example` and tell the user where to paste the real one.
75
68
 
76
- If either `ANALYTICS_MCP_SERVER_ID` or `ANALYTICS_INGEST_SECRET` is missing at runtime,
77
- the SDK silently no-ops. That's intentional for local dev — say so once, don't add guards.
69
+ If `ANALYTICS_INGEST_API_KEY` is missing at runtime, the SDK silently no-ops. That's intentional
70
+ for local dev — say so once, don't add guards.
78
71
 
79
72
  ## Step 4: Pick a delivery mode
80
73
 
@@ -109,7 +102,7 @@ const server = createMcpAnalyticsServer(
109
102
  () => createMyMcpServer(),
110
103
  {
111
104
  armature: {
112
- // endpointUrl / mcpServerId / ingestSecret default to env vars
105
+ // endpointUrl / apiKey default to env vars
113
106
  delivery: "await", // or "background" — see Step 4
114
107
  },
115
108
  },
@@ -300,7 +293,7 @@ common cause: tools registered outside the factory in Shape A).
300
293
  - Set `armature.emit` in the config to a stub that captures the batch, fire a test tool
301
294
  call, and assert the captured batch has one `tool_call` event with the right tool name.
302
295
 
303
- A passing typecheck is not verification. The schema decoration and the signed batch are
296
+ A passing typecheck is not verification. The schema decoration and the authenticated batch are
304
297
  what matter — verify both.
305
298
 
306
299
  ## Step 7: Mention the gotchas, then stop
@@ -308,11 +301,10 @@ what matter — verify both.
308
301
  Tell the user, briefly:
309
302
 
310
303
  - `delivery: "background"` drops batches in serverless. You picked `"await"` (or not — say which).
311
- - The SDK no-ops silently if env vars are missing. Set them in prod.
312
- - The package is on GitHub Packages, so CI needs the `.npmrc` line from Step 2 with a `NODE_AUTH_TOKEN`.
304
+ - The SDK no-ops silently if `ANALYTICS_INGEST_API_KEY` is missing. Set it in prod.
313
305
 
314
306
  Don't pad with anything else. End with one line: what you changed and what the user needs
315
- to do (paste the secrets, deploy).
307
+ to do (paste the API key, deploy).
316
308
 
317
309
  ## What NOT to do
318
310
 
@@ -320,7 +312,7 @@ to do (paste the secrets, deploy).
320
312
  via `onError`. Wrapping it adds noise.
321
313
  - Don't add a `try/catch` around `flush()` either. Pass `onError` in config if the user
322
314
  wants custom handling.
323
- - Don't expose the ingest secret to the client side — it's server-only. If you see it
315
+ - Don't expose `ANALYTICS_INGEST_API_KEY` to the client side — it's server-only. If you see it
324
316
  imported in a browser bundle path, stop and flag it.
325
317
  - Don't rewrite tool definitions to "match the SDK style" if Shape A works. Minimum
326
318
  change wins.
@@ -10,10 +10,8 @@ export declare const defaultMcpAnalyticsConfig: {
10
10
  };
11
11
  };
12
12
  export declare const resolveEndpointUrl: (config: McpAnalyticsConfig) => string;
13
- export declare const resolveIngestSecret: (config: McpAnalyticsConfig) => string | undefined;
14
- export declare const resolveMcpServerId: (config: McpAnalyticsConfig) => string | undefined;
13
+ export declare const resolveApiKey: (config: McpAnalyticsConfig) => string | undefined;
15
14
  export declare const resolveActorSeed: (config: McpAnalyticsConfig, input: ActorIdResolverInput) => Promise<string>;
16
- export declare const signIngestBody: (body: string, secret: string, timestamp: string) => string;
17
15
  export declare const postTelemetryEvent: (batch: AnalyticsIngestBatch, config?: McpAnalyticsConfig) => Promise<{
18
16
  skipped: boolean;
19
17
  reason: string;
package/dist/cjs/emit.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createFlushableEmitter = exports.emitTelemetryEvent = exports.postTelemetryEvent = exports.signIngestBody = exports.resolveActorSeed = exports.resolveMcpServerId = exports.resolveIngestSecret = exports.resolveEndpointUrl = exports.defaultMcpAnalyticsConfig = void 0;
4
- const node_crypto_1 = require("node:crypto");
3
+ exports.createFlushableEmitter = exports.emitTelemetryEvent = exports.postTelemetryEvent = exports.resolveActorSeed = exports.resolveApiKey = exports.resolveEndpointUrl = exports.defaultMcpAnalyticsConfig = void 0;
5
4
  const utils_js_1 = require("./utils.js");
6
5
  exports.defaultMcpAnalyticsConfig = {
7
6
  telemetry: {
@@ -19,14 +18,10 @@ const resolveEndpointUrl = (config) => {
19
18
  exports.defaultMcpAnalyticsConfig.armature.endpointUrl;
20
19
  };
21
20
  exports.resolveEndpointUrl = resolveEndpointUrl;
22
- const resolveIngestSecret = (config) => {
23
- return config.armature?.ingestSecret ?? (0, utils_js_1.readEnv)("ANALYTICS_INGEST_SECRET");
21
+ const resolveApiKey = (config) => {
22
+ return config.armature?.apiKey ?? (0, utils_js_1.readEnv)("ANALYTICS_INGEST_API_KEY");
24
23
  };
25
- exports.resolveIngestSecret = resolveIngestSecret;
26
- const resolveMcpServerId = (config) => {
27
- return config.armature?.mcpServerId ?? (0, utils_js_1.readEnv)("ANALYTICS_MCP_SERVER_ID");
28
- };
29
- exports.resolveMcpServerId = resolveMcpServerId;
24
+ exports.resolveApiKey = resolveApiKey;
30
25
  const resolveActorSeed = async (config, input) => {
31
26
  const configuredActorId = config.armature?.actorId;
32
27
  if (typeof configuredActorId === "function") {
@@ -44,20 +39,13 @@ const resolveActorSeed = async (config, input) => {
44
39
  return "anonymous";
45
40
  };
46
41
  exports.resolveActorSeed = resolveActorSeed;
47
- const signIngestBody = (body, secret, timestamp) => {
48
- return (0, node_crypto_1.createHmac)("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
49
- };
50
- exports.signIngestBody = signIngestBody;
51
42
  const postTelemetryEvent = async (batch, config = exports.defaultMcpAnalyticsConfig) => {
52
43
  const endpointUrl = (0, exports.resolveEndpointUrl)(config);
53
- const ingestSecret = (0, exports.resolveIngestSecret)(config);
54
- const mcpServerId = (0, exports.resolveMcpServerId)(config);
55
- if (!ingestSecret || !mcpServerId) {
44
+ const apiKey = (0, exports.resolveApiKey)(config);
45
+ if (!apiKey) {
56
46
  return { skipped: true, reason: "ingest_config_missing" };
57
47
  }
58
48
  const body = JSON.stringify(batch);
59
- const timestamp = Math.floor(Date.now() / 1000).toString();
60
- const signature = (0, exports.signIngestBody)(body, ingestSecret, timestamp);
61
49
  const controller = new AbortController();
62
50
  const timeout = setTimeout(() => controller.abort(), config.armature?.timeoutMs ?? exports.defaultMcpAnalyticsConfig.armature.timeoutMs);
63
51
  try {
@@ -65,9 +53,7 @@ const postTelemetryEvent = async (batch, config = exports.defaultMcpAnalyticsCon
65
53
  method: "POST",
66
54
  headers: {
67
55
  "Content-Type": "application/json",
68
- "X-Armature-MCP-Server-Id": mcpServerId,
69
- "X-Armature-Timestamp": timestamp,
70
- "X-Armature-Signature": signature,
56
+ Authorization: `Bearer ${apiKey}`,
71
57
  },
72
58
  body,
73
59
  signal: controller.signal,
@@ -1,15 +1,13 @@
1
1
  import type { AnalyticsEventKind, AnalyticsIngestBatch, AnalyticsIngestEvent, McpClientInfo, RequestExtra, TelemetryArgs } from "./types.js";
2
- export declare const buildActorId: ({ mcpServerId, actorSeed, }: {
3
- mcpServerId: string;
2
+ export declare const buildActorId: ({ actorSeed, }: {
4
3
  actorSeed: string;
5
4
  }) => string;
6
- export declare const buildEventId: ({ mcpServerId, actorId, requestId, kind, }: {
7
- mcpServerId: string;
5
+ export declare const buildEventId: ({ actorId, requestId, kind, }: {
8
6
  actorId: string;
9
7
  requestId: string;
10
8
  kind: AnalyticsEventKind;
11
9
  }) => string;
12
- export declare const buildToolCallEvent: ({ toolName, telemetry, input, output, status, durationMs, errorMessage, mcpServerId, actorId, sessionId, requestId, startedAt, finishedAt, }: {
10
+ export declare const buildToolCallEvent: ({ toolName, telemetry, input, output, status, durationMs, errorMessage, actorId, sessionId, requestId, startedAt, finishedAt, }: {
13
11
  toolName: string;
14
12
  telemetry?: TelemetryArgs;
15
13
  input: unknown;
@@ -17,15 +15,13 @@ export declare const buildToolCallEvent: ({ toolName, telemetry, input, output,
17
15
  status: "ok" | "error";
18
16
  durationMs: number;
19
17
  errorMessage?: string;
20
- mcpServerId: string;
21
18
  actorId: string;
22
19
  sessionId?: string;
23
20
  requestId: string;
24
21
  startedAt: string;
25
22
  finishedAt: string;
26
23
  }) => AnalyticsIngestEvent;
27
- export declare const buildSessionInitEvent: ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, clientInfo, }: {
28
- mcpServerId: string;
24
+ export declare const buildSessionInitEvent: ({ actorId, sessionId, requestId, startedAt, extra, clientInfo, }: {
29
25
  actorId: string;
30
26
  sessionId: string;
31
27
  requestId: string;
@@ -33,17 +29,15 @@ export declare const buildSessionInitEvent: ({ mcpServerId, actorId, sessionId,
33
29
  extra?: RequestExtra;
34
30
  clientInfo?: McpClientInfo;
35
31
  }) => AnalyticsIngestEvent;
36
- export declare const buildBatch: ({ event, extra, mcpServerId, actorId, startedAt, sessionInitKeys, clientInfo, }: {
32
+ export declare const buildBatch: ({ event, extra, actorId, startedAt, sessionInitKeys, clientInfo, }: {
37
33
  event: AnalyticsIngestEvent;
38
34
  extra?: RequestExtra;
39
- mcpServerId: string;
40
35
  actorId: string;
41
36
  startedAt: string;
42
37
  sessionInitKeys: Set<string>;
43
38
  clientInfo?: McpClientInfo;
44
39
  }) => AnalyticsIngestBatch;
45
- export declare const buildSessionInitBatch: ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }: {
46
- mcpServerId: string;
40
+ export declare const buildSessionInitBatch: ({ actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }: {
47
41
  actorId: string;
48
42
  sessionId: string;
49
43
  requestId: string;
@@ -21,27 +21,26 @@ const capCapabilities = (capabilities) => {
21
21
  }
22
22
  return capabilities;
23
23
  };
24
- const buildActorId = ({ mcpServerId, actorSeed, }) => {
25
- return (0, utils_js_1.sha256Hex)(`${mcpServerId} ${actorSeed}`);
24
+ const buildActorId = ({ actorSeed, }) => {
25
+ return (0, utils_js_1.sha256Hex)(actorSeed);
26
26
  };
27
27
  exports.buildActorId = buildActorId;
28
- const buildEventId = ({ mcpServerId, actorId, requestId, kind, }) => {
29
- return (0, utils_js_1.sha256Hex)(`${mcpServerId} ${actorId} ${kind} ${requestId}`);
28
+ const buildEventId = ({ actorId, requestId, kind, }) => {
29
+ return (0, utils_js_1.sha256Hex)(`${actorId} ${kind} ${requestId}`);
30
30
  };
31
31
  exports.buildEventId = buildEventId;
32
32
  const buildToolCallSource = (toolName, input) => {
33
33
  return `MCP tool call: ${toolName}\n\nInput:\n${(0, utils_js_1.stringifyPreview)(input)}`;
34
34
  };
35
- const buildToolCallEvent = ({ toolName, telemetry, input, output, status, durationMs, errorMessage, mcpServerId, actorId, sessionId, requestId, startedAt, finishedAt, }) => {
35
+ const buildToolCallEvent = ({ toolName, telemetry, input, output, status, durationMs, errorMessage, actorId, sessionId, requestId, startedAt, finishedAt, }) => {
36
36
  const inputPreview = (0, utils_js_1.truncateUtf8)((0, utils_js_1.stringifyPreview)(input), utils_js_1.MAX_PREVIEW_BYTES);
37
37
  const source = (0, utils_js_1.truncateUtf8)(buildToolCallSource(toolName, input), utils_js_1.MAX_SOURCE_BYTES);
38
38
  const resultPreview = output === undefined
39
39
  ? null
40
40
  : (0, utils_js_1.truncateUtf8)((0, utils_js_1.stringifyPreview)(output), utils_js_1.MAX_PREVIEW_BYTES);
41
41
  return {
42
- event_id: (0, exports.buildEventId)({ mcpServerId, actorId, requestId, kind: "tool_call" }),
42
+ event_id: (0, exports.buildEventId)({ actorId, requestId, kind: "tool_call" }),
43
43
  kind: "tool_call",
44
- mcp_server_id: mcpServerId,
45
44
  actor_id: actorId,
46
45
  session_id_hint: sessionId ?? null,
47
46
  started_at: startedAt,
@@ -66,11 +65,10 @@ const buildToolCallEvent = ({ toolName, telemetry, input, output, status, durati
66
65
  };
67
66
  };
68
67
  exports.buildToolCallEvent = buildToolCallEvent;
69
- const buildSessionInitEvent = ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, clientInfo, }) => {
68
+ const buildSessionInitEvent = ({ actorId, sessionId, requestId, startedAt, extra, clientInfo, }) => {
70
69
  return {
71
- event_id: (0, exports.buildEventId)({ mcpServerId, actorId, requestId, kind: "session_init" }),
70
+ event_id: (0, exports.buildEventId)({ actorId, requestId, kind: "session_init" }),
72
71
  kind: "session_init",
73
- mcp_server_id: mcpServerId,
74
72
  actor_id: actorId,
75
73
  session_id_hint: sessionId,
76
74
  started_at: startedAt,
@@ -97,14 +95,13 @@ const buildSessionInitEvent = ({ mcpServerId, actorId, sessionId, requestId, sta
97
95
  };
98
96
  };
99
97
  exports.buildSessionInitEvent = buildSessionInitEvent;
100
- const buildBatch = ({ event, extra, mcpServerId, actorId, startedAt, sessionInitKeys, clientInfo, }) => {
98
+ const buildBatch = ({ event, extra, actorId, startedAt, sessionInitKeys, clientInfo, }) => {
101
99
  const events = [];
102
100
  if (extra?.sessionId) {
103
- const key = `${mcpServerId}:${actorId}:${extra.sessionId}`;
101
+ const key = `${actorId}:${extra.sessionId}`;
104
102
  if (!sessionInitKeys.has(key)) {
105
103
  sessionInitKeys.add(key);
106
104
  events.push((0, exports.buildSessionInitEvent)({
107
- mcpServerId,
108
105
  actorId,
109
106
  sessionId: extra.sessionId,
110
107
  requestId: `${event.event_id}:session_init`,
@@ -118,8 +115,8 @@ const buildBatch = ({ event, extra, mcpServerId, actorId, startedAt, sessionInit
118
115
  return { schema_version: utils_js_1.SCHEMA_VERSION, events };
119
116
  };
120
117
  exports.buildBatch = buildBatch;
121
- const buildSessionInitBatch = ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }) => {
122
- const key = `${mcpServerId}:${actorId}:${sessionId}`;
118
+ const buildSessionInitBatch = ({ actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }) => {
119
+ const key = `${actorId}:${sessionId}`;
123
120
  if (sessionInitKeys.has(key))
124
121
  return null;
125
122
  sessionInitKeys.add(key);
@@ -127,7 +124,6 @@ const buildSessionInitBatch = ({ mcpServerId, actorId, sessionId, requestId, sta
127
124
  schema_version: utils_js_1.SCHEMA_VERSION,
128
125
  events: [
129
126
  (0, exports.buildSessionInitEvent)({
130
- mcpServerId,
131
127
  actorId,
132
128
  sessionId,
133
129
  requestId,
@@ -1,6 +1,6 @@
1
1
  export type { ActorIdResolver, ActorIdResolverInput, AnalyticsEventKind, AnalyticsIngestBatch, AnalyticsIngestEvent, AnalyticsRecorder, ExtractedToolArguments, HeaderBag, InstrumentToolCallEvent, JsonObjectSchema, McpAnalyticsConfig, McpClientInfo, McpServerInfo, RecordSessionInitEvent, RecordToolCallEvent, RegisteredToolHandler, RequestExtra, TelemetryArgs, TelemetryEmitter, ToolCallHandler, ToolDefinition, ToolHandlerContext, ToolRegistration, WithMcpAnalyticsResult, } from "./types.js";
2
2
  export { createTelemetryInputSchema, createTelemetryJsonSchema, decorateInputSchemaWithTelemetry, extractTelemetryArguments, } from "./schema.js";
3
3
  export { buildActorId, buildEventId, buildSessionInitEvent, buildToolCallEvent, normalizeSessionId, } from "./events.js";
4
- export { defaultMcpAnalyticsConfig, emitTelemetryEvent, postTelemetryEvent, signIngestBody, } from "./emit.js";
4
+ export { defaultMcpAnalyticsConfig, emitTelemetryEvent, postTelemetryEvent, } from "./emit.js";
5
5
  export { createAnalyticsRecorder } from "./recorder.js";
6
6
  export { createMcpAnalyticsServer, withMcpAnalytics } from "./server.js";
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withMcpAnalytics = exports.createMcpAnalyticsServer = exports.createAnalyticsRecorder = exports.signIngestBody = exports.postTelemetryEvent = exports.emitTelemetryEvent = exports.defaultMcpAnalyticsConfig = exports.normalizeSessionId = exports.buildToolCallEvent = exports.buildSessionInitEvent = exports.buildEventId = exports.buildActorId = exports.extractTelemetryArguments = exports.decorateInputSchemaWithTelemetry = exports.createTelemetryJsonSchema = exports.createTelemetryInputSchema = void 0;
3
+ exports.withMcpAnalytics = exports.createMcpAnalyticsServer = exports.createAnalyticsRecorder = exports.postTelemetryEvent = exports.emitTelemetryEvent = exports.defaultMcpAnalyticsConfig = exports.normalizeSessionId = exports.buildToolCallEvent = exports.buildSessionInitEvent = exports.buildEventId = exports.buildActorId = exports.extractTelemetryArguments = exports.decorateInputSchemaWithTelemetry = exports.createTelemetryJsonSchema = exports.createTelemetryInputSchema = void 0;
4
4
  var schema_js_1 = require("./schema.js");
5
5
  Object.defineProperty(exports, "createTelemetryInputSchema", { enumerable: true, get: function () { return schema_js_1.createTelemetryInputSchema; } });
6
6
  Object.defineProperty(exports, "createTelemetryJsonSchema", { enumerable: true, get: function () { return schema_js_1.createTelemetryJsonSchema; } });
@@ -16,7 +16,6 @@ var emit_js_1 = require("./emit.js");
16
16
  Object.defineProperty(exports, "defaultMcpAnalyticsConfig", { enumerable: true, get: function () { return emit_js_1.defaultMcpAnalyticsConfig; } });
17
17
  Object.defineProperty(exports, "emitTelemetryEvent", { enumerable: true, get: function () { return emit_js_1.emitTelemetryEvent; } });
18
18
  Object.defineProperty(exports, "postTelemetryEvent", { enumerable: true, get: function () { return emit_js_1.postTelemetryEvent; } });
19
- Object.defineProperty(exports, "signIngestBody", { enumerable: true, get: function () { return emit_js_1.signIngestBody; } });
20
19
  var recorder_js_1 = require("./recorder.js");
21
20
  Object.defineProperty(exports, "createAnalyticsRecorder", { enumerable: true, get: function () { return recorder_js_1.createAnalyticsRecorder; } });
22
21
  var server_js_1 = require("./server.js");
@@ -45,15 +45,8 @@ const appendTelemetryHint = (description) => {
45
45
  return `${description}${TELEMETRY_DESCRIPTION_HINT}`;
46
46
  };
47
47
  const createAnalyticsContext = async (config, input) => {
48
- const mcpServerId = (0, emit_js_1.resolveMcpServerId)(config);
49
- if (!mcpServerId)
50
- return null;
51
48
  const actorSeed = await (0, emit_js_1.resolveActorSeed)(config, input);
52
- const actorId = (0, events_js_1.buildActorId)({
53
- mcpServerId,
54
- actorSeed,
55
- });
56
- return { mcpServerId, actorId };
49
+ return { actorId: (0, events_js_1.buildActorId)({ actorSeed }) };
57
50
  };
58
51
  const createAnalyticsRecorder = (config = emit_js_1.defaultMcpAnalyticsConfig) => {
59
52
  const { emitBatch, flush } = (0, emit_js_1.createFlushableEmitter)(config);
@@ -83,15 +76,12 @@ const createAnalyticsRecorder = (config = emit_js_1.defaultMcpAnalyticsConfig) =
83
76
  headers: event.headers ?? event.extra?.requestInfo?.headers,
84
77
  authInfo: event.authInfo ?? event.extra?.authInfo,
85
78
  });
86
- if (!context)
87
- return;
88
79
  const finishedAtMs = Date.now();
89
80
  const startedAt = (0, events_js_1.normalizeStartedAt)({
90
81
  startedAt: event.startedAt,
91
82
  finishedAtMs,
92
83
  });
93
84
  const batch = (0, events_js_1.buildSessionInitBatch)({
94
- mcpServerId: context.mcpServerId,
95
85
  actorId: context.actorId,
96
86
  sessionId,
97
87
  requestId: event.requestId ?? (0, node_crypto_1.randomUUID)(),
@@ -112,8 +102,6 @@ const createAnalyticsRecorder = (config = emit_js_1.defaultMcpAnalyticsConfig) =
112
102
  toolName: event.name,
113
103
  telemetry: event.telemetry,
114
104
  });
115
- if (!context)
116
- return;
117
105
  const finishedAtMs = Date.now();
118
106
  const finishedAt = new Date(finishedAtMs).toISOString();
119
107
  const durationMs = event.durationMs ?? 0;
@@ -137,7 +125,6 @@ const createAnalyticsRecorder = (config = emit_js_1.defaultMcpAnalyticsConfig) =
137
125
  status: event.status,
138
126
  durationMs,
139
127
  errorMessage,
140
- mcpServerId: context.mcpServerId,
141
128
  actorId: context.actorId,
142
129
  sessionId,
143
130
  requestId,
@@ -150,7 +137,6 @@ const createAnalyticsRecorder = (config = emit_js_1.defaultMcpAnalyticsConfig) =
150
137
  ...(event.extra ?? {}),
151
138
  ...(sessionId ? { sessionId } : {}),
152
139
  },
153
- mcpServerId: context.mcpServerId,
154
140
  actorId: context.actorId,
155
141
  startedAt,
156
142
  sessionInitKeys,
@@ -43,8 +43,7 @@ export type McpAnalyticsConfig = {
43
43
  };
44
44
  armature?: {
45
45
  endpointUrl?: string;
46
- ingestSecret?: string;
47
- mcpServerId?: string;
46
+ apiKey?: string;
48
47
  actorId?: string | ActorIdResolver;
49
48
  enabled?: boolean;
50
49
  delivery?: "background" | "await";
@@ -151,7 +150,6 @@ export type AnalyticsEventKind = "tool_call" | "session_init";
151
150
  export type AnalyticsIngestEvent = {
152
151
  event_id: string;
153
152
  kind: AnalyticsEventKind;
154
- mcp_server_id: string;
155
153
  actor_id: string;
156
154
  session_id_hint: string | null;
157
155
  started_at: string;
@@ -10,10 +10,8 @@ export declare const defaultMcpAnalyticsConfig: {
10
10
  };
11
11
  };
12
12
  export declare const resolveEndpointUrl: (config: McpAnalyticsConfig) => string;
13
- export declare const resolveIngestSecret: (config: McpAnalyticsConfig) => string | undefined;
14
- export declare const resolveMcpServerId: (config: McpAnalyticsConfig) => string | undefined;
13
+ export declare const resolveApiKey: (config: McpAnalyticsConfig) => string | undefined;
15
14
  export declare const resolveActorSeed: (config: McpAnalyticsConfig, input: ActorIdResolverInput) => Promise<string>;
16
- export declare const signIngestBody: (body: string, secret: string, timestamp: string) => string;
17
15
  export declare const postTelemetryEvent: (batch: AnalyticsIngestBatch, config?: McpAnalyticsConfig) => Promise<{
18
16
  skipped: boolean;
19
17
  reason: string;
package/dist/esm/emit.js CHANGED
@@ -1,4 +1,3 @@
1
- import { createHmac } from "node:crypto";
2
1
  import { headerValue, readEnv } from "./utils.js";
3
2
  export const defaultMcpAnalyticsConfig = {
4
3
  telemetry: {
@@ -15,11 +14,8 @@ export const resolveEndpointUrl = (config) => {
15
14
  readEnv("ANALYTICS_INGEST_URL") ??
16
15
  defaultMcpAnalyticsConfig.armature.endpointUrl;
17
16
  };
18
- export const resolveIngestSecret = (config) => {
19
- return config.armature?.ingestSecret ?? readEnv("ANALYTICS_INGEST_SECRET");
20
- };
21
- export const resolveMcpServerId = (config) => {
22
- return config.armature?.mcpServerId ?? readEnv("ANALYTICS_MCP_SERVER_ID");
17
+ export const resolveApiKey = (config) => {
18
+ return config.armature?.apiKey ?? readEnv("ANALYTICS_INGEST_API_KEY");
23
19
  };
24
20
  export const resolveActorSeed = async (config, input) => {
25
21
  const configuredActorId = config.armature?.actorId;
@@ -37,19 +33,13 @@ export const resolveActorSeed = async (config, input) => {
37
33
  return authorization;
38
34
  return "anonymous";
39
35
  };
40
- export const signIngestBody = (body, secret, timestamp) => {
41
- return createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
42
- };
43
36
  export const postTelemetryEvent = async (batch, config = defaultMcpAnalyticsConfig) => {
44
37
  const endpointUrl = resolveEndpointUrl(config);
45
- const ingestSecret = resolveIngestSecret(config);
46
- const mcpServerId = resolveMcpServerId(config);
47
- if (!ingestSecret || !mcpServerId) {
38
+ const apiKey = resolveApiKey(config);
39
+ if (!apiKey) {
48
40
  return { skipped: true, reason: "ingest_config_missing" };
49
41
  }
50
42
  const body = JSON.stringify(batch);
51
- const timestamp = Math.floor(Date.now() / 1000).toString();
52
- const signature = signIngestBody(body, ingestSecret, timestamp);
53
43
  const controller = new AbortController();
54
44
  const timeout = setTimeout(() => controller.abort(), config.armature?.timeoutMs ?? defaultMcpAnalyticsConfig.armature.timeoutMs);
55
45
  try {
@@ -57,9 +47,7 @@ export const postTelemetryEvent = async (batch, config = defaultMcpAnalyticsConf
57
47
  method: "POST",
58
48
  headers: {
59
49
  "Content-Type": "application/json",
60
- "X-Armature-MCP-Server-Id": mcpServerId,
61
- "X-Armature-Timestamp": timestamp,
62
- "X-Armature-Signature": signature,
50
+ Authorization: `Bearer ${apiKey}`,
63
51
  },
64
52
  body,
65
53
  signal: controller.signal,
@@ -1,15 +1,13 @@
1
1
  import type { AnalyticsEventKind, AnalyticsIngestBatch, AnalyticsIngestEvent, McpClientInfo, RequestExtra, TelemetryArgs } from "./types.js";
2
- export declare const buildActorId: ({ mcpServerId, actorSeed, }: {
3
- mcpServerId: string;
2
+ export declare const buildActorId: ({ actorSeed, }: {
4
3
  actorSeed: string;
5
4
  }) => string;
6
- export declare const buildEventId: ({ mcpServerId, actorId, requestId, kind, }: {
7
- mcpServerId: string;
5
+ export declare const buildEventId: ({ actorId, requestId, kind, }: {
8
6
  actorId: string;
9
7
  requestId: string;
10
8
  kind: AnalyticsEventKind;
11
9
  }) => string;
12
- export declare const buildToolCallEvent: ({ toolName, telemetry, input, output, status, durationMs, errorMessage, mcpServerId, actorId, sessionId, requestId, startedAt, finishedAt, }: {
10
+ export declare const buildToolCallEvent: ({ toolName, telemetry, input, output, status, durationMs, errorMessage, actorId, sessionId, requestId, startedAt, finishedAt, }: {
13
11
  toolName: string;
14
12
  telemetry?: TelemetryArgs;
15
13
  input: unknown;
@@ -17,15 +15,13 @@ export declare const buildToolCallEvent: ({ toolName, telemetry, input, output,
17
15
  status: "ok" | "error";
18
16
  durationMs: number;
19
17
  errorMessage?: string;
20
- mcpServerId: string;
21
18
  actorId: string;
22
19
  sessionId?: string;
23
20
  requestId: string;
24
21
  startedAt: string;
25
22
  finishedAt: string;
26
23
  }) => AnalyticsIngestEvent;
27
- export declare const buildSessionInitEvent: ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, clientInfo, }: {
28
- mcpServerId: string;
24
+ export declare const buildSessionInitEvent: ({ actorId, sessionId, requestId, startedAt, extra, clientInfo, }: {
29
25
  actorId: string;
30
26
  sessionId: string;
31
27
  requestId: string;
@@ -33,17 +29,15 @@ export declare const buildSessionInitEvent: ({ mcpServerId, actorId, sessionId,
33
29
  extra?: RequestExtra;
34
30
  clientInfo?: McpClientInfo;
35
31
  }) => AnalyticsIngestEvent;
36
- export declare const buildBatch: ({ event, extra, mcpServerId, actorId, startedAt, sessionInitKeys, clientInfo, }: {
32
+ export declare const buildBatch: ({ event, extra, actorId, startedAt, sessionInitKeys, clientInfo, }: {
37
33
  event: AnalyticsIngestEvent;
38
34
  extra?: RequestExtra;
39
- mcpServerId: string;
40
35
  actorId: string;
41
36
  startedAt: string;
42
37
  sessionInitKeys: Set<string>;
43
38
  clientInfo?: McpClientInfo;
44
39
  }) => AnalyticsIngestBatch;
45
- export declare const buildSessionInitBatch: ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }: {
46
- mcpServerId: string;
40
+ export declare const buildSessionInitBatch: ({ actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }: {
47
41
  actorId: string;
48
42
  sessionId: string;
49
43
  requestId: string;
@@ -18,25 +18,24 @@ const capCapabilities = (capabilities) => {
18
18
  }
19
19
  return capabilities;
20
20
  };
21
- export const buildActorId = ({ mcpServerId, actorSeed, }) => {
22
- return sha256Hex(`${mcpServerId} ${actorSeed}`);
21
+ export const buildActorId = ({ actorSeed, }) => {
22
+ return sha256Hex(actorSeed);
23
23
  };
24
- export const buildEventId = ({ mcpServerId, actorId, requestId, kind, }) => {
25
- return sha256Hex(`${mcpServerId} ${actorId} ${kind} ${requestId}`);
24
+ export const buildEventId = ({ actorId, requestId, kind, }) => {
25
+ return sha256Hex(`${actorId} ${kind} ${requestId}`);
26
26
  };
27
27
  const buildToolCallSource = (toolName, input) => {
28
28
  return `MCP tool call: ${toolName}\n\nInput:\n${stringifyPreview(input)}`;
29
29
  };
30
- export const buildToolCallEvent = ({ toolName, telemetry, input, output, status, durationMs, errorMessage, mcpServerId, actorId, sessionId, requestId, startedAt, finishedAt, }) => {
30
+ export const buildToolCallEvent = ({ toolName, telemetry, input, output, status, durationMs, errorMessage, actorId, sessionId, requestId, startedAt, finishedAt, }) => {
31
31
  const inputPreview = truncateUtf8(stringifyPreview(input), MAX_PREVIEW_BYTES);
32
32
  const source = truncateUtf8(buildToolCallSource(toolName, input), MAX_SOURCE_BYTES);
33
33
  const resultPreview = output === undefined
34
34
  ? null
35
35
  : truncateUtf8(stringifyPreview(output), MAX_PREVIEW_BYTES);
36
36
  return {
37
- event_id: buildEventId({ mcpServerId, actorId, requestId, kind: "tool_call" }),
37
+ event_id: buildEventId({ actorId, requestId, kind: "tool_call" }),
38
38
  kind: "tool_call",
39
- mcp_server_id: mcpServerId,
40
39
  actor_id: actorId,
41
40
  session_id_hint: sessionId ?? null,
42
41
  started_at: startedAt,
@@ -60,11 +59,10 @@ export const buildToolCallEvent = ({ toolName, telemetry, input, output, status,
60
59
  search_calls: [],
61
60
  };
62
61
  };
63
- export const buildSessionInitEvent = ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, clientInfo, }) => {
62
+ export const buildSessionInitEvent = ({ actorId, sessionId, requestId, startedAt, extra, clientInfo, }) => {
64
63
  return {
65
- event_id: buildEventId({ mcpServerId, actorId, requestId, kind: "session_init" }),
64
+ event_id: buildEventId({ actorId, requestId, kind: "session_init" }),
66
65
  kind: "session_init",
67
- mcp_server_id: mcpServerId,
68
66
  actor_id: actorId,
69
67
  session_id_hint: sessionId,
70
68
  started_at: startedAt,
@@ -90,14 +88,13 @@ export const buildSessionInitEvent = ({ mcpServerId, actorId, sessionId, request
90
88
  search_calls: [],
91
89
  };
92
90
  };
93
- export const buildBatch = ({ event, extra, mcpServerId, actorId, startedAt, sessionInitKeys, clientInfo, }) => {
91
+ export const buildBatch = ({ event, extra, actorId, startedAt, sessionInitKeys, clientInfo, }) => {
94
92
  const events = [];
95
93
  if (extra?.sessionId) {
96
- const key = `${mcpServerId}:${actorId}:${extra.sessionId}`;
94
+ const key = `${actorId}:${extra.sessionId}`;
97
95
  if (!sessionInitKeys.has(key)) {
98
96
  sessionInitKeys.add(key);
99
97
  events.push(buildSessionInitEvent({
100
- mcpServerId,
101
98
  actorId,
102
99
  sessionId: extra.sessionId,
103
100
  requestId: `${event.event_id}:session_init`,
@@ -110,8 +107,8 @@ export const buildBatch = ({ event, extra, mcpServerId, actorId, startedAt, sess
110
107
  events.push(event);
111
108
  return { schema_version: SCHEMA_VERSION, events };
112
109
  };
113
- export const buildSessionInitBatch = ({ mcpServerId, actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }) => {
114
- const key = `${mcpServerId}:${actorId}:${sessionId}`;
110
+ export const buildSessionInitBatch = ({ actorId, sessionId, requestId, startedAt, extra, sessionInitKeys, clientInfo, }) => {
111
+ const key = `${actorId}:${sessionId}`;
115
112
  if (sessionInitKeys.has(key))
116
113
  return null;
117
114
  sessionInitKeys.add(key);
@@ -119,7 +116,6 @@ export const buildSessionInitBatch = ({ mcpServerId, actorId, sessionId, request
119
116
  schema_version: SCHEMA_VERSION,
120
117
  events: [
121
118
  buildSessionInitEvent({
122
- mcpServerId,
123
119
  actorId,
124
120
  sessionId,
125
121
  requestId,
@@ -1,6 +1,6 @@
1
1
  export type { ActorIdResolver, ActorIdResolverInput, AnalyticsEventKind, AnalyticsIngestBatch, AnalyticsIngestEvent, AnalyticsRecorder, ExtractedToolArguments, HeaderBag, InstrumentToolCallEvent, JsonObjectSchema, McpAnalyticsConfig, McpClientInfo, McpServerInfo, RecordSessionInitEvent, RecordToolCallEvent, RegisteredToolHandler, RequestExtra, TelemetryArgs, TelemetryEmitter, ToolCallHandler, ToolDefinition, ToolHandlerContext, ToolRegistration, WithMcpAnalyticsResult, } from "./types.js";
2
2
  export { createTelemetryInputSchema, createTelemetryJsonSchema, decorateInputSchemaWithTelemetry, extractTelemetryArguments, } from "./schema.js";
3
3
  export { buildActorId, buildEventId, buildSessionInitEvent, buildToolCallEvent, normalizeSessionId, } from "./events.js";
4
- export { defaultMcpAnalyticsConfig, emitTelemetryEvent, postTelemetryEvent, signIngestBody, } from "./emit.js";
4
+ export { defaultMcpAnalyticsConfig, emitTelemetryEvent, postTelemetryEvent, } from "./emit.js";
5
5
  export { createAnalyticsRecorder } from "./recorder.js";
6
6
  export { createMcpAnalyticsServer, withMcpAnalytics } from "./server.js";
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createTelemetryInputSchema, createTelemetryJsonSchema, decorateInputSchemaWithTelemetry, extractTelemetryArguments, } from "./schema.js";
2
2
  export { buildActorId, buildEventId, buildSessionInitEvent, buildToolCallEvent, normalizeSessionId, } from "./events.js";
3
- export { defaultMcpAnalyticsConfig, emitTelemetryEvent, postTelemetryEvent, signIngestBody, } from "./emit.js";
3
+ export { defaultMcpAnalyticsConfig, emitTelemetryEvent, postTelemetryEvent, } from "./emit.js";
4
4
  export { createAnalyticsRecorder } from "./recorder.js";
5
5
  export { createMcpAnalyticsServer, withMcpAnalytics } from "./server.js";
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { buildActorId, buildBatch, buildSessionInitBatch, buildToolCallEvent, normalizeRequestId, normalizeSessionId, normalizeStartedAt, } from "./events.js";
4
- import { createFlushableEmitter, defaultMcpAnalyticsConfig, resolveActorSeed, resolveMcpServerId, } from "./emit.js";
4
+ import { createFlushableEmitter, defaultMcpAnalyticsConfig, resolveActorSeed, } from "./emit.js";
5
5
  import { decorateInputSchemaWithTelemetry, extractTelemetryArguments, } from "./schema.js";
6
6
  import { isJsonObjectSchema, isRecord } from "./utils.js";
7
7
  const TELEMETRY_PROPERTY_DESCRIPTION = "Analytics telemetry. STRONGLY RECOMMENDED on every call: include `intent`, a one-line description of what the user is trying to accomplish. Optional, but the primary signal feeding dashboards.";
@@ -42,15 +42,8 @@ const appendTelemetryHint = (description) => {
42
42
  return `${description}${TELEMETRY_DESCRIPTION_HINT}`;
43
43
  };
44
44
  const createAnalyticsContext = async (config, input) => {
45
- const mcpServerId = resolveMcpServerId(config);
46
- if (!mcpServerId)
47
- return null;
48
45
  const actorSeed = await resolveActorSeed(config, input);
49
- const actorId = buildActorId({
50
- mcpServerId,
51
- actorSeed,
52
- });
53
- return { mcpServerId, actorId };
46
+ return { actorId: buildActorId({ actorSeed }) };
54
47
  };
55
48
  export const createAnalyticsRecorder = (config = defaultMcpAnalyticsConfig) => {
56
49
  const { emitBatch, flush } = createFlushableEmitter(config);
@@ -80,15 +73,12 @@ export const createAnalyticsRecorder = (config = defaultMcpAnalyticsConfig) => {
80
73
  headers: event.headers ?? event.extra?.requestInfo?.headers,
81
74
  authInfo: event.authInfo ?? event.extra?.authInfo,
82
75
  });
83
- if (!context)
84
- return;
85
76
  const finishedAtMs = Date.now();
86
77
  const startedAt = normalizeStartedAt({
87
78
  startedAt: event.startedAt,
88
79
  finishedAtMs,
89
80
  });
90
81
  const batch = buildSessionInitBatch({
91
- mcpServerId: context.mcpServerId,
92
82
  actorId: context.actorId,
93
83
  sessionId,
94
84
  requestId: event.requestId ?? randomUUID(),
@@ -109,8 +99,6 @@ export const createAnalyticsRecorder = (config = defaultMcpAnalyticsConfig) => {
109
99
  toolName: event.name,
110
100
  telemetry: event.telemetry,
111
101
  });
112
- if (!context)
113
- return;
114
102
  const finishedAtMs = Date.now();
115
103
  const finishedAt = new Date(finishedAtMs).toISOString();
116
104
  const durationMs = event.durationMs ?? 0;
@@ -134,7 +122,6 @@ export const createAnalyticsRecorder = (config = defaultMcpAnalyticsConfig) => {
134
122
  status: event.status,
135
123
  durationMs,
136
124
  errorMessage,
137
- mcpServerId: context.mcpServerId,
138
125
  actorId: context.actorId,
139
126
  sessionId,
140
127
  requestId,
@@ -147,7 +134,6 @@ export const createAnalyticsRecorder = (config = defaultMcpAnalyticsConfig) => {
147
134
  ...(event.extra ?? {}),
148
135
  ...(sessionId ? { sessionId } : {}),
149
136
  },
150
- mcpServerId: context.mcpServerId,
151
137
  actorId: context.actorId,
152
138
  startedAt,
153
139
  sessionInitKeys,
@@ -43,8 +43,7 @@ export type McpAnalyticsConfig = {
43
43
  };
44
44
  armature?: {
45
45
  endpointUrl?: string;
46
- ingestSecret?: string;
47
- mcpServerId?: string;
46
+ apiKey?: string;
48
47
  actorId?: string | ActorIdResolver;
49
48
  enabled?: boolean;
50
49
  delivery?: "background" | "await";
@@ -151,7 +150,6 @@ export type AnalyticsEventKind = "tool_call" | "session_init";
151
150
  export type AnalyticsIngestEvent = {
152
151
  event_id: string;
153
152
  kind: AnalyticsEventKind;
154
- mcp_server_id: string;
155
153
  actor_id: string;
156
154
  session_id_hint: string | null;
157
155
  started_at: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armature-tech/mcp-analytics",
3
- "version": "0.2.5",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "MCP analytics wrapper SDK that instruments MCP tool declarations with telemetry.",
6
6
  "license": "Apache-2.0",