hackshop-mcp 0.0.3 → 0.0.5

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.
Files changed (74) hide show
  1. package/README.md +88 -34
  2. package/catalog.json +1584 -25
  3. package/dist/build-plan/index.d.ts +10 -0
  4. package/dist/build-plan/index.js +550 -0
  5. package/dist/build-plan/index.js.map +1 -0
  6. package/dist/build-plan/types.d.ts +153 -0
  7. package/dist/build-plan/types.js +2 -0
  8. package/dist/build-plan/types.js.map +1 -0
  9. package/dist/catalog/schema.d.ts +523 -7
  10. package/dist/catalog/schema.js +49 -0
  11. package/dist/catalog/schema.js.map +1 -1
  12. package/dist/core/assess.d.ts +36 -0
  13. package/dist/core/assess.js +100 -0
  14. package/dist/core/assess.js.map +1 -0
  15. package/dist/core/errors.d.ts +2 -0
  16. package/dist/core/errors.js +70 -0
  17. package/dist/core/errors.js.map +1 -0
  18. package/dist/core/links.d.ts +3 -0
  19. package/dist/core/links.js +19 -0
  20. package/dist/core/links.js.map +1 -0
  21. package/dist/core/plan-gadget.d.ts +85 -0
  22. package/dist/core/plan-gadget.js +619 -0
  23. package/dist/core/plan-gadget.js.map +1 -0
  24. package/dist/core/resources.d.ts +37 -0
  25. package/dist/core/resources.js +143 -0
  26. package/dist/core/resources.js.map +1 -0
  27. package/dist/core/safety.d.ts +7 -0
  28. package/dist/core/safety.js +28 -0
  29. package/dist/core/safety.js.map +1 -0
  30. package/dist/core/tools.d.ts +247 -0
  31. package/dist/core/tools.js +164 -0
  32. package/dist/core/tools.js.map +1 -0
  33. package/dist/core/types.d.ts +163 -0
  34. package/dist/core/types.js +17 -0
  35. package/dist/core/types.js.map +1 -0
  36. package/dist/platforms/index.d.ts +26 -0
  37. package/dist/platforms/index.js +52 -0
  38. package/dist/platforms/index.js.map +1 -0
  39. package/dist/platforms/load.d.ts +4 -0
  40. package/dist/platforms/load.js +78 -0
  41. package/dist/platforms/load.js.map +1 -0
  42. package/dist/platforms/schema.d.ts +587 -0
  43. package/dist/platforms/schema.js +64 -0
  44. package/dist/platforms/schema.js.map +1 -0
  45. package/dist/sampling.d.ts +5 -0
  46. package/dist/sampling.js +44 -21
  47. package/dist/sampling.js.map +1 -1
  48. package/dist/server.d.ts +14 -1
  49. package/dist/server.js +164 -40
  50. package/dist/server.js.map +1 -1
  51. package/dist/site-url.d.ts +1 -0
  52. package/dist/site-url.js +4 -0
  53. package/dist/site-url.js.map +1 -0
  54. package/dist/telemetry.d.ts +29 -0
  55. package/dist/telemetry.js +100 -0
  56. package/dist/telemetry.js.map +1 -0
  57. package/dist/tools/assess_hackability.d.ts +2 -29
  58. package/dist/tools/assess_hackability.js +11 -43
  59. package/dist/tools/assess_hackability.js.map +1 -1
  60. package/dist/tools/get_build_plan.d.ts +6 -0
  61. package/dist/tools/get_build_plan.js +16 -0
  62. package/dist/tools/get_build_plan.js.map +1 -0
  63. package/dist/tools/plan_gadget.d.ts +4 -0
  64. package/dist/tools/plan_gadget.js +14 -0
  65. package/dist/tools/plan_gadget.js.map +1 -0
  66. package/dist/tools/propose_hardware.d.ts +5 -0
  67. package/dist/tools/propose_hardware.js +57 -8
  68. package/dist/tools/propose_hardware.js.map +1 -1
  69. package/dist/tools/simulate_assembly.d.ts +315 -0
  70. package/dist/tools/simulate_assembly.js +145 -0
  71. package/dist/tools/simulate_assembly.js.map +1 -0
  72. package/package.json +4 -1
  73. package/platforms.json +779 -0
  74. package/tags.md +1 -0
package/dist/sampling.js CHANGED
@@ -1,24 +1,12 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
- // Wraps sampling/createMessage with retry-once + Anthropic-API fallback +
3
- // degraded-response semantics. Returns either parsed JSON of shape T, or null
4
- // if every path failed.
5
- //
6
- // Order of attempts:
7
- // 1. server.createMessage (host-delegated sampling) — preferred
8
- // 2. retry server.createMessage once on transient failure
9
- // 3. direct Anthropic API call IF process.env.ANTHROPIC_API_KEY is set — fallback
10
- // 4. return null → caller produces a degraded response
11
- //
12
- // The fallback exists because not all MCP hosts implement sampling/createMessage
13
- // today. Claude Code supports it; Claude Desktop's support has been spotty;
14
- // Cursor / Cowork / other clients may not. Setting ANTHROPIC_API_KEY in your
15
- // MCP client config (under "env") gives the user reasoning even on hosts
16
- // without sampling.
17
2
  export async function sampleJson(opts) {
18
- const { server, systemPrompt, userPrompt, maxTokens = 1500 } = opts;
3
+ const { server, systemPrompt, userPrompt, maxTokens = 1500, diagnostics, hostSamplingTimeoutMs, } = opts;
4
+ const hostSamplingDeadline = hostSamplingTimeoutMs === undefined
5
+ ? undefined
6
+ : Date.now() + hostSamplingTimeoutMs;
19
7
  const tryHostSampling = async () => {
20
8
  try {
21
- const response = await server.createMessage({
9
+ const response = await withTimeout(server.createMessage({
22
10
  systemPrompt,
23
11
  messages: [
24
12
  {
@@ -31,7 +19,7 @@ export async function sampleJson(opts) {
31
19
  intelligencePriority: 0.6,
32
20
  speedPriority: 0.4,
33
21
  },
34
- });
22
+ }), remainingHostSamplingMs(hostSamplingDeadline));
35
23
  const content = response.content;
36
24
  if (content.type !== "text")
37
25
  return null;
@@ -49,13 +37,18 @@ export async function sampleJson(opts) {
49
37
  const second = await tryHostSampling();
50
38
  if (second !== null)
51
39
  return second;
52
- // Attempt 3: direct Anthropic API fallback (env-gated)
40
+ // Attempt 3: direct Anthropic API fallback (env-gated). Default model id kept
41
+ // current ('claude-sonnet-4-6'); override via HACKSHOP_MODEL. If this throws
42
+ // WHILE a key was present (e.g. the default model was retired), we record the
43
+ // error in `diagnostics` so the caller can make the failure user-visible
44
+ // instead of degrading silently to stderr only.
53
45
  const apiKey = process.env.ANTHROPIC_API_KEY;
54
46
  if (apiKey) {
47
+ const fallbackModel = process.env.HACKSHOP_MODEL ?? "claude-sonnet-4-6";
55
48
  try {
56
49
  const client = new Anthropic({ apiKey });
57
50
  const response = await client.messages.create({
58
- model: process.env.HACKSHOP_MODEL ?? "claude-sonnet-4-6",
51
+ model: fallbackModel,
59
52
  max_tokens: maxTokens,
60
53
  system: systemPrompt,
61
54
  messages: [{ role: "user", content: userPrompt }],
@@ -68,13 +61,43 @@ export async function sampleJson(opts) {
68
61
  return parsed;
69
62
  }
70
63
  }
64
+ // Key present, call succeeded, but content was unusable — note it.
65
+ if (diagnostics) {
66
+ diagnostics.apiFallbackError = `Anthropic API fallback (model "${fallbackModel}") returned no usable JSON.`;
67
+ }
71
68
  }
72
69
  catch (err) {
73
- process.stderr.write(`[hackshop-mcp] Anthropic fallback failed: ${err.message}\n`);
70
+ const msg = err.message;
71
+ process.stderr.write(`[hackshop-mcp] Anthropic fallback failed: ${msg}\n`);
72
+ if (diagnostics) {
73
+ diagnostics.apiFallbackError = `Anthropic API fallback failed (model "${fallbackModel}"): ${msg}. If the model id was retired, set HACKSHOP_MODEL to a current model.`;
74
+ }
74
75
  }
75
76
  }
76
77
  return null;
77
78
  }
79
+ function remainingHostSamplingMs(deadline) {
80
+ if (deadline === undefined)
81
+ return undefined;
82
+ return Math.max(1, deadline - Date.now());
83
+ }
84
+ async function withTimeout(promise, timeoutMs) {
85
+ if (timeoutMs === undefined)
86
+ return promise;
87
+ let timeout;
88
+ try {
89
+ return await Promise.race([
90
+ promise,
91
+ new Promise((_resolve, reject) => {
92
+ timeout = setTimeout(() => reject(new Error("host sampling timed out")), timeoutMs);
93
+ }),
94
+ ]);
95
+ }
96
+ finally {
97
+ if (timeout)
98
+ clearTimeout(timeout);
99
+ }
100
+ }
78
101
  // Strip a possible markdown code fence around JSON. Tolerant of whitespace
79
102
  // and explanatory prose around the fence.
80
103
  function parseJsonLoose(text) {
@@ -1 +1 @@
1
- {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -1,2 +1,15 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import type { DeviceEntry } from "./catalog/schema.js";
4
+ import type { Platform } from "./platforms/schema.js";
5
+ export declare function createToolRunner(context: {
6
+ devices: DeviceEntry[];
7
+ platforms: Platform[];
8
+ server?: Server;
9
+ tags?: string[];
10
+ }): (name: string, args: unknown) => Promise<{
11
+ out: unknown;
12
+ degraded?: boolean;
13
+ isError?: boolean;
14
+ text?: string;
15
+ }>;
package/dist/server.js CHANGED
@@ -1,21 +1,87 @@
1
1
  #!/usr/bin/env node
2
+ import { realpathSync } from "node:fs";
3
+ import { pathToFileURL } from "node:url";
2
4
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
6
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
7
  import { loadCatalog } from "./catalog/load.js";
8
+ import { loadPlatforms } from "./platforms/load.js";
9
+ import { printablesFor, setLoadedPlatforms } from "./platforms/index.js";
6
10
  import { probeSamplingSupport } from "./sampling.js";
7
11
  import { proposeHardware, proposeHardwareInput, } from "./tools/propose_hardware.js";
8
- import { assessHackability, assessHackabilityInput, } from "./tools/assess_hackability.js";
12
+ import { simulateAssembly, simulateAssemblyInput, } from "./tools/simulate_assembly.js";
13
+ import { siteUrlFromEnv } from "./site-url.js";
14
+ import { createTelemetry, errorKind } from "./telemetry.js";
15
+ import { coreToolDefinitions, executeCoreTool, } from "./core/tools.js";
16
+ import { getCorePrompt, listCorePrompts, listCoreResources, readCoreResource, } from "./core/resources.js";
9
17
  const NAME = "hackshop-mcp";
10
- const VERSION = "0.0.3";
18
+ const VERSION = "0.0.5";
19
+ const STDIO_INSTRUCTIONS = "Hackshop maps a natural-language project idea to hackable, repurposable, or protocol-native hardware. Use plan_gadget for Muse agent-body gadgets because it is deterministic, instant and key-free. Use propose_hardware for broader repurposing ideas and existing hardware, especially when the host can sample or ANTHROPIC_API_KEY is set. Hackshop never buys anything; show shopping lists and ask the human before any purchase. Anonymous usage telemetry (tool names and timings only) is on by default; set HACKSHOP_TELEMETRY=0 to turn it off.";
20
+ export function createToolRunner(context) {
21
+ return async (name, args) => {
22
+ if (name === "propose_hardware") {
23
+ if (!context.server) {
24
+ throw new Error("propose_hardware requires an MCP server context");
25
+ }
26
+ const input = proposeHardwareInput.parse(args);
27
+ const out = await proposeHardware(input, context.devices, context.server);
28
+ return { out, degraded: out.degraded };
29
+ }
30
+ if (name === "assess_hackability" ||
31
+ name === "plan_gadget" ||
32
+ name === "get_build_plan") {
33
+ const result = executeCoreTool(name, args, coreContext(context.devices, context.platforms, context.tags));
34
+ return runnerResult(result);
35
+ }
36
+ if (name === "simulate_assembly") {
37
+ const input = simulateAssemblyInput.parse(args);
38
+ return { out: await simulateAssembly(input) };
39
+ }
40
+ throw new Error(`Unknown tool: ${name}`);
41
+ };
42
+ }
43
+ function coreContext(catalog, platforms, tags) {
44
+ const siteUrl = siteUrlFromEnv();
45
+ return {
46
+ catalog,
47
+ platforms,
48
+ printablesFor: (device, baseUrl) => printablesFor(device, baseUrl),
49
+ siteUrl,
50
+ tags,
51
+ };
52
+ }
53
+ function runnerResult(result) {
54
+ if (result.isError) {
55
+ return { out: { error: result.text }, isError: true, text: result.text };
56
+ }
57
+ return { out: result.output };
58
+ }
11
59
  async function main() {
12
60
  // Boot validation. Refuses to start on bad catalog/tags.
13
61
  const { devices, tags } = loadCatalog();
14
- process.stderr.write(`[hackshop-mcp] Catalog loaded: ${devices.length} devices, ${tags.size} tags.\n`);
62
+ const sortedTags = [...tags].sort();
63
+ const platforms = loadPlatforms(devices);
64
+ setLoadedPlatforms(platforms);
65
+ process.stderr.write(`[hackshop-mcp] Catalog loaded: ${devices.length} devices, ${tags.size} tags, ${platforms.length} platforms.\n`);
15
66
  // Note: sampling is a CLIENT capability, not a server one. We don't declare
16
67
  // it here; we just call server.createMessage(...) and the host either
17
68
  // supports it or doesn't. probeSamplingSupport() handles the check.
18
- const server = new Server({ name: NAME, version: VERSION }, { capabilities: { tools: {} } });
69
+ const server = new Server({ name: NAME, version: VERSION }, {
70
+ capabilities: { tools: {}, resources: {}, prompts: {} },
71
+ instructions: STDIO_INSTRUCTIONS,
72
+ });
73
+ const telemetry = createTelemetry(VERSION);
74
+ const clientInfo = () => {
75
+ const client = server.getClientVersion();
76
+ return { client_name: client?.name, client_version: client?.version };
77
+ };
78
+ server.oninitialized = () => {
79
+ telemetry.send({
80
+ event: "mcp_server_started",
81
+ ...clientInfo(),
82
+ client_sampling: Boolean(server.getClientCapabilities()?.sampling),
83
+ });
84
+ };
19
85
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
20
86
  tools: [
21
87
  {
@@ -40,60 +106,118 @@ async function main() {
40
106
  required: ["idea"],
41
107
  },
42
108
  },
109
+ ...coreToolDefinitions(),
43
110
  {
44
- name: "assess_hackability",
45
- description: "Given a device name, return: hackable y/n, hack difficulty, brick risk (with safety rule applied), firmware links, community size, last verified date, and notes. Looks up by id, exact name, or substring.",
111
+ name: "simulate_assembly",
112
+ description: "Drop a proposed robot Assembly into a MuJoCo physics world and run a bounded navigation rollout. Returns whether it reached the goal plus honest failure telemetry (stuck/tipped/collisions/heading-oscillation), a natural-language post-mortem, and artifact URLs (rendered mp4, scene.xml, control.py, telemetry.json). Today simulates the diff-drive 'navigate' slice; other goal kinds return an honest 'unsupported'. Requires a running sim-worker (SIM_WORKER_URL).",
46
113
  inputSchema: {
47
114
  type: "object",
48
115
  properties: {
49
- device_name: {
50
- type: "string",
51
- description: "Device name or id.",
116
+ assembly: {
117
+ type: "object",
118
+ description: "Assembly IR: { idea, components[{ref,device_id,name,role}], edges[], goal{kind,spec,success_metric}, world{template,goal_xy?} }. Build it with the site /api/assembly output or by hand.",
119
+ },
120
+ duration_s: {
121
+ type: "number",
122
+ description: "Optional sim seconds (<=10, bounded). Default 8.",
52
123
  },
53
124
  },
54
- required: ["device_name"],
125
+ required: ["assembly"],
55
126
  },
56
127
  },
57
128
  ],
58
129
  }));
130
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
131
+ resources: listCoreResources(),
132
+ }));
133
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
134
+ const resource = readCoreResource(request.params.uri, coreContext(devices, platforms, sortedTags));
135
+ if (!resource) {
136
+ throw new Error(`Unknown resource: ${request.params.uri}`);
137
+ }
138
+ return { contents: [resource] };
139
+ });
140
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
141
+ prompts: listCorePrompts(),
142
+ }));
143
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
144
+ const prompt = getCorePrompt(request.params.name, request.params.arguments);
145
+ if (!prompt) {
146
+ throw new Error(`Unknown prompt: ${request.params.name}`);
147
+ }
148
+ return prompt;
149
+ });
150
+ const runTool = createToolRunner({ devices, platforms, server, tags: sortedTags });
59
151
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
60
152
  const { name, arguments: args } = request.params;
61
- if (name === "propose_hardware") {
62
- const input = proposeHardwareInput.parse(args);
63
- const out = await proposeHardware(input, devices, server);
153
+ const startedAt = Date.now();
154
+ try {
155
+ const { out, degraded, isError, text } = await runTool(name, args);
156
+ telemetry.send({
157
+ event: "mcp_tool_called",
158
+ tool: name,
159
+ success: !isError,
160
+ degraded,
161
+ ...(isError ? { error_kind: "tool_error" } : {}),
162
+ duration_ms: Date.now() - startedAt,
163
+ ...clientInfo(),
164
+ });
64
165
  return {
65
- content: [{ type: "text", text: JSON.stringify(out, null, 2) }],
166
+ ...(isError ? { isError: true } : {}),
167
+ content: [{ type: "text", text: text ?? JSON.stringify(out, null, 2) }],
66
168
  };
67
169
  }
68
- if (name === "assess_hackability") {
69
- const input = assessHackabilityInput.parse(args);
70
- const out = assessHackability(input, devices);
71
- return {
72
- content: [{ type: "text", text: JSON.stringify(out, null, 2) }],
73
- };
170
+ catch (err) {
171
+ telemetry.send({
172
+ event: "mcp_tool_called",
173
+ tool: name,
174
+ success: false,
175
+ error_kind: errorKind(err),
176
+ duration_ms: Date.now() - startedAt,
177
+ ...clientInfo(),
178
+ });
179
+ throw err;
74
180
  }
75
- throw new Error(`Unknown tool: ${name}`);
76
181
  });
77
182
  const transport = new StdioServerTransport();
78
183
  await server.connect(transport);
79
- // Probe sampling support after connect. Logs to stderr; never fatal.
80
- // Some hosts (Claude Desktop, Claude Code) support sampling/createMessage;
81
- // some clients don't. If unsupported, propose_hardware degrades gracefully.
82
- void probeSamplingSupport(server).then((ok) => {
83
- if (!ok) {
84
- process.stderr.write("[hackshop-mcp] Warning: this MCP host does not appear to support " +
85
- "sampling/createMessage. The propose_hardware tool will return " +
86
- "degraded responses (raw catalog matches without reasoning). " +
87
- "Hosts known to support sampling: Claude Desktop, Claude Code.\n");
88
- }
89
- else {
90
- process.stderr.write("[hackshop-mcp] Sampling probe OK.\n");
91
- }
92
- });
184
+ // Probe sampling support after connect — but ONLY when explicitly opted in
185
+ // via HACKSHOP_PROBE_SAMPLING=1. Previously this fired a real createMessage on
186
+ // every boot just to log "Sampling probe OK", spending tokens on every start.
187
+ // propose_hardware already degrades gracefully when sampling is unsupported,
188
+ // so the probe is purely informational. Logs to stderr; never fatal.
189
+ if (process.env.HACKSHOP_PROBE_SAMPLING === "1") {
190
+ void probeSamplingSupport(server).then((ok) => {
191
+ if (!ok) {
192
+ process.stderr.write("[hackshop-mcp] Warning: this MCP host does not appear to support " +
193
+ "sampling/createMessage. The propose_hardware tool will return " +
194
+ "degraded responses (raw catalog matches without reasoning). " +
195
+ "Hosts known to support sampling: Claude Desktop, Claude Code.\n");
196
+ }
197
+ else {
198
+ process.stderr.write("[hackshop-mcp] Sampling probe OK.\n");
199
+ }
200
+ });
201
+ }
93
202
  process.stderr.write(`[hackshop-mcp] ${NAME} v${VERSION} ready.\n`);
94
203
  }
95
- main().catch((err) => {
96
- process.stderr.write(`[hackshop-mcp] Fatal: ${err.message}\n`);
97
- process.exit(1);
98
- });
204
+ function isDirectRun() {
205
+ const entry = process.argv[1];
206
+ if (!entry)
207
+ return false;
208
+ if (import.meta.url === pathToFileURL(entry).href)
209
+ return true;
210
+ try {
211
+ return import.meta.url === pathToFileURL(realpathSync(entry)).href;
212
+ }
213
+ catch {
214
+ return false;
215
+ }
216
+ }
217
+ if (isDirectRun()) {
218
+ main().catch((err) => {
219
+ process.stderr.write(`[hackshop-mcp] Fatal: ${err.message}\n`);
220
+ process.exit(1);
221
+ });
222
+ }
99
223
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function siteUrlFromEnv(): string;
@@ -0,0 +1,4 @@
1
+ export function siteUrlFromEnv() {
2
+ return (process.env.HACKSHOP_SITE_URL ?? "https://www.hackshop.dev").replace(/\/+$/, "");
3
+ }
4
+ //# sourceMappingURL=site-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-url.js","sourceRoot":"","sources":["../src/site-url.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC"}
@@ -0,0 +1,29 @@
1
+ export declare const DEFAULT_TELEMETRY_URL = "https://www.hackshop.dev/api/telemetry/mcp";
2
+ export type TelemetryEvent = {
3
+ event: "mcp_server_started";
4
+ client_name?: string;
5
+ client_version?: string;
6
+ client_sampling?: boolean;
7
+ } | {
8
+ event: "mcp_tool_called";
9
+ tool: string;
10
+ success: boolean;
11
+ duration_ms: number;
12
+ degraded?: boolean;
13
+ error_kind?: string;
14
+ client_name?: string;
15
+ client_version?: string;
16
+ };
17
+ export declare function telemetryEnabled(env?: NodeJS.ProcessEnv): boolean;
18
+ /**
19
+ * Stable random install id, so repeat use by one person counts once. Falls
20
+ * back to a per-process id if the config dir is not writable.
21
+ */
22
+ export declare function loadInstallId(env?: NodeJS.ProcessEnv, onCreate?: (path: string) => void): string;
23
+ export type Telemetry = {
24
+ enabled: boolean;
25
+ send(event: TelemetryEvent): void;
26
+ };
27
+ export declare function createTelemetry(version: string, env?: NodeJS.ProcessEnv, log?: (line: string) => void): Telemetry;
28
+ /** Coarse error bucket; the message itself is not sent. */
29
+ export declare function errorKind(err: unknown): string;
@@ -0,0 +1,100 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ // Anonymous usage telemetry for the MCP server. It exists so the maintainer
6
+ // can see that the server is being used at all: stdio servers run on users'
7
+ // machines and are otherwise invisible.
8
+ //
9
+ // Sent: event name, tool name, success/degraded, duration, hackshop-mcp
10
+ // version, MCP client name/version, OS platform, Node major version, and a
11
+ // random install id. NEVER sent: tool arguments, ideas, device names, results,
12
+ // API keys, file paths, or anything typed by the user.
13
+ //
14
+ // Opt out with HACKSHOP_TELEMETRY=0 (or DO_NOT_TRACK=1).
15
+ export const DEFAULT_TELEMETRY_URL = "https://www.hackshop.dev/api/telemetry/mcp";
16
+ const SEND_TIMEOUT_MS = 1_500;
17
+ const KNOWN_TOOLS = new Set(["propose_hardware", "assess_hackability", "simulate_assembly"]);
18
+ export function telemetryEnabled(env = process.env) {
19
+ const flag = env.HACKSHOP_TELEMETRY?.trim().toLowerCase();
20
+ if (flag && ["0", "false", "off", "no", "disabled"].includes(flag))
21
+ return false;
22
+ const dnt = env.DO_NOT_TRACK?.trim().toLowerCase();
23
+ if (dnt && dnt !== "0" && dnt !== "false")
24
+ return false;
25
+ // Test runners and CI are not real usage.
26
+ if (env.VITEST || env.CI)
27
+ return false;
28
+ return true;
29
+ }
30
+ function configPath(env) {
31
+ const base = env.XDG_CONFIG_HOME?.trim() || join(homedir(), ".config");
32
+ return join(base, "hackshop-mcp", "telemetry.json");
33
+ }
34
+ /**
35
+ * Stable random install id, so repeat use by one person counts once. Falls
36
+ * back to a per-process id if the config dir is not writable.
37
+ */
38
+ export function loadInstallId(env = process.env, onCreate = () => { }) {
39
+ const path = configPath(env);
40
+ try {
41
+ const stored = JSON.parse(readFileSync(path, "utf8"));
42
+ if (typeof stored.install_id === "string" && stored.install_id.length === 36) {
43
+ return stored.install_id;
44
+ }
45
+ }
46
+ catch {
47
+ // Missing or unreadable: create below.
48
+ }
49
+ const id = randomUUID();
50
+ try {
51
+ mkdirSync(dirname(path), { recursive: true });
52
+ writeFileSync(path, JSON.stringify({ install_id: id }) + "\n", "utf8");
53
+ onCreate(path);
54
+ }
55
+ catch {
56
+ // Read-only home, sandbox, etc. A per-process id is fine.
57
+ }
58
+ return id;
59
+ }
60
+ export function createTelemetry(version, env = process.env, log = (line) => process.stderr.write(line)) {
61
+ if (!telemetryEnabled(env)) {
62
+ return { enabled: false, send: () => { } };
63
+ }
64
+ const installId = loadInstallId(env, () => log("[hackshop-mcp] Anonymous usage telemetry is on (tool names and timings only; " +
65
+ "never your ideas or tool inputs). Opt out with HACKSHOP_TELEMETRY=0.\n"));
66
+ const url = env.HACKSHOP_TELEMETRY_URL?.trim() || DEFAULT_TELEMETRY_URL;
67
+ const base = {
68
+ install_id: installId,
69
+ mcp_version: version,
70
+ platform: process.platform,
71
+ node_version: process.versions.node.split(".")[0],
72
+ };
73
+ return {
74
+ enabled: true,
75
+ send(event) {
76
+ const payload = event.event === "mcp_tool_called" && !KNOWN_TOOLS.has(event.tool)
77
+ ? { ...event, tool: "unknown" }
78
+ : event;
79
+ // Fire and forget: never awaited, never throws, never blocks a tool call.
80
+ void fetch(url, {
81
+ method: "POST",
82
+ headers: { "Content-Type": "application/json" },
83
+ body: JSON.stringify({ ...base, ...payload }),
84
+ signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
85
+ }).catch(() => { });
86
+ },
87
+ };
88
+ }
89
+ /** Coarse error bucket; the message itself is not sent. */
90
+ export function errorKind(err) {
91
+ if (err && typeof err === "object" && "name" in err) {
92
+ const name = String(err.name);
93
+ if (name === "ZodError")
94
+ return "invalid_input";
95
+ if (name === "AbortError" || name === "TimeoutError")
96
+ return "timeout";
97
+ }
98
+ return "error";
99
+ }
100
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"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"}
@@ -1,31 +1,4 @@
1
- import { z } from "zod";
2
1
  import type { DeviceEntry } from "../catalog/schema.js";
3
- import { type DeviceLinks } from "../links.js";
4
- export declare const assessHackabilityInput: z.ZodObject<{
5
- device_name: z.ZodString;
6
- }, "strip", z.ZodTypeAny, {
7
- device_name: string;
8
- }, {
9
- device_name: string;
10
- }>;
11
- export type AssessInput = z.infer<typeof assessHackabilityInput>;
12
- export interface AssessOutput {
13
- found: boolean;
14
- device?: {
15
- id: string;
16
- name: string;
17
- category: string;
18
- hackable: boolean;
19
- hack_difficulty: number;
20
- brick_risk: number | null;
21
- brick_risk_label: string;
22
- brick_risk_disclaimer: string | null;
23
- firmware_links: string[];
24
- community_size: string;
25
- last_verified: string;
26
- notes: string;
27
- links: DeviceLinks;
28
- };
29
- message?: string;
30
- }
2
+ import { assessHackabilityInput, type AssessInput, type AssessOutput } from "../core/assess.js";
3
+ export { assessHackabilityInput, type AssessInput, type AssessOutput };
31
4
  export declare function assessHackability(input: AssessInput, catalog: DeviceEntry[]): AssessOutput;