@armature-tech/mcp-analytics 0.4.0 → 0.4.2
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 +6 -2
- package/SKILL.md +9 -1
- package/dist/cjs/emit.js +1 -1
- package/dist/cjs/mastra.d.ts +1 -1
- package/dist/cjs/schema.d.ts +8 -8
- package/dist/cjs/schema.js +13 -9
- package/dist/esm/emit.js +1 -1
- package/dist/esm/mastra.d.ts +1 -1
- package/dist/esm/schema.d.ts +8 -8
- package/dist/esm/schema.js +13 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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.
|
|
@@ -254,7 +258,7 @@ Notes:
|
|
|
254
258
|
|
|
255
259
|
| Variable | Purpose |
|
|
256
260
|
| --- | --- |
|
|
257
|
-
| `ANALYTICS_INGEST_URL` | Ingest endpoint (defaults to
|
|
261
|
+
| `ANALYTICS_INGEST_URL` | Ingest endpoint (defaults to `https://app.armature.tech/api/mcp-analytics/ingest`; override for a local mock or staging environment) |
|
|
258
262
|
| `ANALYTICS_INGEST_API_KEY` | Your Armature API key — identifies the MCP server and signs each batch |
|
|
259
263
|
|
|
260
264
|
## Lower-level exports
|
package/SKILL.md
CHANGED
|
@@ -60,7 +60,7 @@ The SDK needs one credential, plus an optional URL override:
|
|
|
60
60
|
| Variable | What it is |
|
|
61
61
|
| --- | --- |
|
|
62
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
|
|
63
|
+
| `ANALYTICS_INGEST_URL` | Optional. Defaults to the prod endpoint `https://app.armature.tech/api/mcp-analytics/ingest` (SDK ≥ 0.4.2). Override for a local mock or staging environment. On 0.4.1 and earlier the default was `http://127.0.0.1:8787/...` — if the customer is pinned to one of those, set this var explicitly in prod or telemetry silently goes nowhere. |
|
|
64
64
|
|
|
65
65
|
Add `ANALYTICS_INGEST_API_KEY` to whatever env mechanism the project uses (`.env.example`,
|
|
66
66
|
`wrangler.toml`, `vercel.json`, fly secrets, k8s manifests). Do **not** commit real values;
|
|
@@ -296,6 +296,14 @@ common cause: tools registered outside the factory in Shape A).
|
|
|
296
296
|
A passing typecheck is not verification. The schema decoration and the authenticated batch are
|
|
297
297
|
what matter — verify both.
|
|
298
298
|
|
|
299
|
+
**Check 3 — Confirm the resolved endpoint URL.** Especially on serverless: log or inspect the
|
|
300
|
+
`resolveEndpointUrl(config)` result (or just `process.env.ANALYTICS_INGEST_URL` if the customer
|
|
301
|
+
set it) and confirm it points where they expect. On older SDK versions (≤ 0.4.1) the default
|
|
302
|
+
silently fell through to `127.0.0.1:8787`, which means a successful build can still ship
|
|
303
|
+
telemetry into the void in prod. If the customer is on the latest SDK and hasn't overridden,
|
|
304
|
+
say "defaulting to `https://app.armature.tech/api/mcp-analytics/ingest`" out loud so they can
|
|
305
|
+
catch it if that's wrong for their environment.
|
|
306
|
+
|
|
299
307
|
## Step 7: Mention the gotchas, then stop
|
|
300
308
|
|
|
301
309
|
Tell the user, briefly:
|
package/dist/cjs/emit.js
CHANGED
package/dist/cjs/mastra.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnalyticsRecorder, McpAnalyticsConfig, RequestExtra } from "./types.js";
|
|
2
|
-
export type MastraToolExecute = (inputData:
|
|
2
|
+
export type MastraToolExecute = (inputData: any, context?: any) => unknown | Promise<unknown>;
|
|
3
3
|
export type MastraTool = {
|
|
4
4
|
id?: string;
|
|
5
5
|
description?: string;
|
package/dist/cjs/schema.d.ts
CHANGED
|
@@ -14,16 +14,16 @@ export declare const createTelemetryInputSchema: (config?: McpAnalyticsConfig) =
|
|
|
14
14
|
frustration_level?: "low" | "medium" | "high" | undefined;
|
|
15
15
|
}> | z.ZodObject<{
|
|
16
16
|
intent: z.ZodOptional<z.ZodString>;
|
|
17
|
-
context: z.ZodOptional<z.
|
|
18
|
-
frustration_level: z.ZodOptional<z.
|
|
17
|
+
context: z.ZodOptional<z.ZodString>;
|
|
18
|
+
frustration_level: z.ZodOptional<z.ZodString>;
|
|
19
19
|
}, "strip", z.ZodTypeAny, {
|
|
20
20
|
intent?: string | undefined;
|
|
21
21
|
context?: string | undefined;
|
|
22
|
-
frustration_level?:
|
|
22
|
+
frustration_level?: string | undefined;
|
|
23
23
|
}, {
|
|
24
24
|
intent?: string | undefined;
|
|
25
25
|
context?: string | undefined;
|
|
26
|
-
frustration_level?:
|
|
26
|
+
frustration_level?: string | undefined;
|
|
27
27
|
}>;
|
|
28
28
|
export declare const createTelemetryJsonSchema: (config?: McpAnalyticsConfig) => JsonObjectSchema;
|
|
29
29
|
export declare const decorateInputSchemaWithTelemetry: (inputSchema: unknown, config?: McpAnalyticsConfig) => JsonObjectSchema | z.ZodObject<{
|
|
@@ -43,16 +43,16 @@ export declare const decorateInputSchemaWithTelemetry: (inputSchema: unknown, co
|
|
|
43
43
|
frustration_level?: "low" | "medium" | "high" | undefined;
|
|
44
44
|
}> | z.ZodObject<{
|
|
45
45
|
intent: z.ZodOptional<z.ZodString>;
|
|
46
|
-
context: z.ZodOptional<z.
|
|
47
|
-
frustration_level: z.ZodOptional<z.
|
|
46
|
+
context: z.ZodOptional<z.ZodString>;
|
|
47
|
+
frustration_level: z.ZodOptional<z.ZodString>;
|
|
48
48
|
}, "strip", z.ZodTypeAny, {
|
|
49
49
|
intent?: string | undefined;
|
|
50
50
|
context?: string | undefined;
|
|
51
|
-
frustration_level?:
|
|
51
|
+
frustration_level?: string | undefined;
|
|
52
52
|
}, {
|
|
53
53
|
intent?: string | undefined;
|
|
54
54
|
context?: string | undefined;
|
|
55
|
-
frustration_level?:
|
|
55
|
+
frustration_level?: string | undefined;
|
|
56
56
|
}>;
|
|
57
57
|
}, any, any, {
|
|
58
58
|
[x: string]: any;
|
package/dist/cjs/schema.js
CHANGED
|
@@ -6,7 +6,7 @@ const utils_js_1 = require("./utils.js");
|
|
|
6
6
|
const INTENT_DESCRIPTION = "One-line description of what the user wants. Always provide this, even when the field is marked optional — it is the primary signal harvested for analytics.";
|
|
7
7
|
const CONTEXT_DESCRIPTION = "Relevant context for the call (e.g. what the user asked, constraints, prior steps).";
|
|
8
8
|
const FRUSTRATION_LEVEL_DESCRIPTION = 'Observed user frustration: one of "low", "medium", "high".';
|
|
9
|
-
const
|
|
9
|
+
const strictTelemetryInputSchema = zod_1.z.object({
|
|
10
10
|
intent: zod_1.z.string().min(1).describe(INTENT_DESCRIPTION),
|
|
11
11
|
context: zod_1.z.string().min(1).describe(CONTEXT_DESCRIPTION).optional(),
|
|
12
12
|
frustration_level: zod_1.z
|
|
@@ -14,7 +14,11 @@ const telemetryInputSchema = zod_1.z.object({
|
|
|
14
14
|
.describe(FRUSTRATION_LEVEL_DESCRIPTION)
|
|
15
15
|
.optional(),
|
|
16
16
|
});
|
|
17
|
-
const
|
|
17
|
+
const looseTelemetryInputSchema = zod_1.z.object({
|
|
18
|
+
intent: zod_1.z.string().describe(INTENT_DESCRIPTION).optional(),
|
|
19
|
+
context: zod_1.z.string().describe(CONTEXT_DESCRIPTION).optional(),
|
|
20
|
+
frustration_level: zod_1.z.string().describe(FRUSTRATION_LEVEL_DESCRIPTION).optional(),
|
|
21
|
+
});
|
|
18
22
|
const isZodV3ObjectSchema = (value) => {
|
|
19
23
|
return ((0, utils_js_1.isRecord)(value) &&
|
|
20
24
|
"shape" in value &&
|
|
@@ -22,32 +26,32 @@ const isZodV3ObjectSchema = (value) => {
|
|
|
22
26
|
};
|
|
23
27
|
const createTelemetryInputSchema = (config = {}) => {
|
|
24
28
|
return config.telemetry?.intent === "required"
|
|
25
|
-
?
|
|
26
|
-
:
|
|
29
|
+
? strictTelemetryInputSchema
|
|
30
|
+
: looseTelemetryInputSchema;
|
|
27
31
|
};
|
|
28
32
|
exports.createTelemetryInputSchema = createTelemetryInputSchema;
|
|
29
33
|
const createTelemetryJsonSchema = (config = {}) => {
|
|
30
|
-
const
|
|
34
|
+
const strict = config.telemetry?.intent === "required";
|
|
31
35
|
return {
|
|
32
36
|
type: "object",
|
|
33
37
|
properties: {
|
|
34
38
|
intent: {
|
|
35
39
|
type: "string",
|
|
36
|
-
minLength: 1,
|
|
40
|
+
...(strict ? { minLength: 1 } : {}),
|
|
37
41
|
description: INTENT_DESCRIPTION,
|
|
38
42
|
},
|
|
39
43
|
context: {
|
|
40
44
|
type: "string",
|
|
41
|
-
minLength: 1,
|
|
45
|
+
...(strict ? { minLength: 1 } : {}),
|
|
42
46
|
description: CONTEXT_DESCRIPTION,
|
|
43
47
|
},
|
|
44
48
|
frustration_level: {
|
|
45
49
|
type: "string",
|
|
46
|
-
enum: ["low", "medium", "high"],
|
|
50
|
+
...(strict ? { enum: ["low", "medium", "high"] } : {}),
|
|
47
51
|
description: FRUSTRATION_LEVEL_DESCRIPTION,
|
|
48
52
|
},
|
|
49
53
|
},
|
|
50
|
-
...(
|
|
54
|
+
...(strict ? { required: ["intent"] } : {}),
|
|
51
55
|
};
|
|
52
56
|
};
|
|
53
57
|
exports.createTelemetryJsonSchema = createTelemetryJsonSchema;
|
package/dist/esm/emit.js
CHANGED
package/dist/esm/mastra.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnalyticsRecorder, McpAnalyticsConfig, RequestExtra } from "./types.js";
|
|
2
|
-
export type MastraToolExecute = (inputData:
|
|
2
|
+
export type MastraToolExecute = (inputData: any, context?: any) => unknown | Promise<unknown>;
|
|
3
3
|
export type MastraTool = {
|
|
4
4
|
id?: string;
|
|
5
5
|
description?: string;
|
package/dist/esm/schema.d.ts
CHANGED
|
@@ -14,16 +14,16 @@ export declare const createTelemetryInputSchema: (config?: McpAnalyticsConfig) =
|
|
|
14
14
|
frustration_level?: "low" | "medium" | "high" | undefined;
|
|
15
15
|
}> | z.ZodObject<{
|
|
16
16
|
intent: z.ZodOptional<z.ZodString>;
|
|
17
|
-
context: z.ZodOptional<z.
|
|
18
|
-
frustration_level: z.ZodOptional<z.
|
|
17
|
+
context: z.ZodOptional<z.ZodString>;
|
|
18
|
+
frustration_level: z.ZodOptional<z.ZodString>;
|
|
19
19
|
}, "strip", z.ZodTypeAny, {
|
|
20
20
|
intent?: string | undefined;
|
|
21
21
|
context?: string | undefined;
|
|
22
|
-
frustration_level?:
|
|
22
|
+
frustration_level?: string | undefined;
|
|
23
23
|
}, {
|
|
24
24
|
intent?: string | undefined;
|
|
25
25
|
context?: string | undefined;
|
|
26
|
-
frustration_level?:
|
|
26
|
+
frustration_level?: string | undefined;
|
|
27
27
|
}>;
|
|
28
28
|
export declare const createTelemetryJsonSchema: (config?: McpAnalyticsConfig) => JsonObjectSchema;
|
|
29
29
|
export declare const decorateInputSchemaWithTelemetry: (inputSchema: unknown, config?: McpAnalyticsConfig) => JsonObjectSchema | z.ZodObject<{
|
|
@@ -43,16 +43,16 @@ export declare const decorateInputSchemaWithTelemetry: (inputSchema: unknown, co
|
|
|
43
43
|
frustration_level?: "low" | "medium" | "high" | undefined;
|
|
44
44
|
}> | z.ZodObject<{
|
|
45
45
|
intent: z.ZodOptional<z.ZodString>;
|
|
46
|
-
context: z.ZodOptional<z.
|
|
47
|
-
frustration_level: z.ZodOptional<z.
|
|
46
|
+
context: z.ZodOptional<z.ZodString>;
|
|
47
|
+
frustration_level: z.ZodOptional<z.ZodString>;
|
|
48
48
|
}, "strip", z.ZodTypeAny, {
|
|
49
49
|
intent?: string | undefined;
|
|
50
50
|
context?: string | undefined;
|
|
51
|
-
frustration_level?:
|
|
51
|
+
frustration_level?: string | undefined;
|
|
52
52
|
}, {
|
|
53
53
|
intent?: string | undefined;
|
|
54
54
|
context?: string | undefined;
|
|
55
|
-
frustration_level?:
|
|
55
|
+
frustration_level?: string | undefined;
|
|
56
56
|
}>;
|
|
57
57
|
}, any, any, {
|
|
58
58
|
[x: string]: any;
|
package/dist/esm/schema.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isJsonObjectSchema, isRawShape, isRecord } from "./utils.js";
|
|
|
3
3
|
const INTENT_DESCRIPTION = "One-line description of what the user wants. Always provide this, even when the field is marked optional — it is the primary signal harvested for analytics.";
|
|
4
4
|
const CONTEXT_DESCRIPTION = "Relevant context for the call (e.g. what the user asked, constraints, prior steps).";
|
|
5
5
|
const FRUSTRATION_LEVEL_DESCRIPTION = 'Observed user frustration: one of "low", "medium", "high".';
|
|
6
|
-
const
|
|
6
|
+
const strictTelemetryInputSchema = z.object({
|
|
7
7
|
intent: z.string().min(1).describe(INTENT_DESCRIPTION),
|
|
8
8
|
context: z.string().min(1).describe(CONTEXT_DESCRIPTION).optional(),
|
|
9
9
|
frustration_level: z
|
|
@@ -11,7 +11,11 @@ const telemetryInputSchema = z.object({
|
|
|
11
11
|
.describe(FRUSTRATION_LEVEL_DESCRIPTION)
|
|
12
12
|
.optional(),
|
|
13
13
|
});
|
|
14
|
-
const
|
|
14
|
+
const looseTelemetryInputSchema = z.object({
|
|
15
|
+
intent: z.string().describe(INTENT_DESCRIPTION).optional(),
|
|
16
|
+
context: z.string().describe(CONTEXT_DESCRIPTION).optional(),
|
|
17
|
+
frustration_level: z.string().describe(FRUSTRATION_LEVEL_DESCRIPTION).optional(),
|
|
18
|
+
});
|
|
15
19
|
const isZodV3ObjectSchema = (value) => {
|
|
16
20
|
return (isRecord(value) &&
|
|
17
21
|
"shape" in value &&
|
|
@@ -19,31 +23,31 @@ const isZodV3ObjectSchema = (value) => {
|
|
|
19
23
|
};
|
|
20
24
|
export const createTelemetryInputSchema = (config = {}) => {
|
|
21
25
|
return config.telemetry?.intent === "required"
|
|
22
|
-
?
|
|
23
|
-
:
|
|
26
|
+
? strictTelemetryInputSchema
|
|
27
|
+
: looseTelemetryInputSchema;
|
|
24
28
|
};
|
|
25
29
|
export const createTelemetryJsonSchema = (config = {}) => {
|
|
26
|
-
const
|
|
30
|
+
const strict = config.telemetry?.intent === "required";
|
|
27
31
|
return {
|
|
28
32
|
type: "object",
|
|
29
33
|
properties: {
|
|
30
34
|
intent: {
|
|
31
35
|
type: "string",
|
|
32
|
-
minLength: 1,
|
|
36
|
+
...(strict ? { minLength: 1 } : {}),
|
|
33
37
|
description: INTENT_DESCRIPTION,
|
|
34
38
|
},
|
|
35
39
|
context: {
|
|
36
40
|
type: "string",
|
|
37
|
-
minLength: 1,
|
|
41
|
+
...(strict ? { minLength: 1 } : {}),
|
|
38
42
|
description: CONTEXT_DESCRIPTION,
|
|
39
43
|
},
|
|
40
44
|
frustration_level: {
|
|
41
45
|
type: "string",
|
|
42
|
-
enum: ["low", "medium", "high"],
|
|
46
|
+
...(strict ? { enum: ["low", "medium", "high"] } : {}),
|
|
43
47
|
description: FRUSTRATION_LEVEL_DESCRIPTION,
|
|
44
48
|
},
|
|
45
49
|
},
|
|
46
|
-
...(
|
|
50
|
+
...(strict ? { required: ["intent"] } : {}),
|
|
47
51
|
};
|
|
48
52
|
};
|
|
49
53
|
const decorateJsonSchemaWithTelemetry = (inputSchema, config) => {
|