hackshop-mcp 0.0.3 → 0.0.4

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.
@@ -0,0 +1,51 @@
1
+ import { loadCatalog } from "../catalog/load.js";
2
+ import { loadPlatforms } from "./load.js";
3
+ let platformCache = null;
4
+ export function setLoadedPlatforms(platforms) {
5
+ platformCache = platforms;
6
+ }
7
+ export function getLoadedPlatforms() {
8
+ if (platformCache)
9
+ return platformCache;
10
+ const { devices } = loadCatalog();
11
+ platformCache = loadPlatforms(devices);
12
+ return platformCache;
13
+ }
14
+ export function agentPlatformsFor(deviceId) {
15
+ const matches = [];
16
+ for (const platform of getLoadedPlatforms()) {
17
+ const tierLabels = new Map(platform.tiers.map((tier) => [tier.id, tier.label]));
18
+ for (const board of platform.boards) {
19
+ if (board.device_id !== deviceId)
20
+ continue;
21
+ matches.push(toAgentPlatform(platform, board, tierLabels));
22
+ }
23
+ }
24
+ return matches;
25
+ }
26
+ export function printablesFor(device) {
27
+ const base = (process.env.HACKSHOP_SITE_URL ?? "https://www.hackshop.dev").replace(/\/$/, "");
28
+ return (device.physical?.printables ?? []).map((part) => ({
29
+ part,
30
+ title: part === "desk-stand" ? "Printable desk stand" : "Printable enclosure",
31
+ stl_url: `${base}/cad/${device.id}/${part}.stl`,
32
+ step_url: `${base}/cad/${device.id}/${part}.step`,
33
+ svg_url: `${base}/cad/${device.id}/${part}.svg`,
34
+ fab_url: `${base}/cad/${device.id}/${part}.fab.json`,
35
+ }));
36
+ }
37
+ function toAgentPlatform(platform, board, tierLabels) {
38
+ return {
39
+ platform_id: platform.id,
40
+ platform_name: platform.name,
41
+ support: board.support,
42
+ tier: board.tier,
43
+ tier_label: tierLabels.get(board.tier) ?? board.tier,
44
+ kind: board.kind,
45
+ build: board.build,
46
+ ...(board.eol ? { eol: board.eol } : {}),
47
+ features: board.features,
48
+ note: board.note,
49
+ };
50
+ }
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/platforms/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAyB1C,IAAI,aAAa,GAAsB,IAAI,CAAC;AAE5C,MAAM,UAAU,kBAAkB,CAAC,SAAqB;IACtD,aAAa,GAAG,SAAS,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,CAAC;IAClC,aAAa,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,KAAK,MAAM,QAAQ,IAAI,kBAAkB,EAAE,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChF,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,SAAS,KAAK,QAAQ;gBAAE,SAAS;YAC3C,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9F,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACxD,IAAI;QACJ,KAAK,EAAE,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,qBAAqB;QAC7E,OAAO,EAAE,GAAG,IAAI,QAAQ,MAAM,CAAC,EAAE,IAAI,IAAI,MAAM;QAC/C,QAAQ,EAAE,GAAG,IAAI,QAAQ,MAAM,CAAC,EAAE,IAAI,IAAI,OAAO;QACjD,OAAO,EAAE,GAAG,IAAI,QAAQ,MAAM,CAAC,EAAE,IAAI,IAAI,MAAM;QAC/C,OAAO,EAAE,GAAG,IAAI,QAAQ,MAAM,CAAC,EAAE,IAAI,IAAI,WAAW;KACrD,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CACtB,QAAkB,EAClB,KAAoB,EACpB,UAA+B;IAE/B,OAAO;QACL,WAAW,EAAE,QAAQ,CAAC,EAAE;QACxB,aAAa,EAAE,QAAQ,CAAC,IAAI;QAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI;QACpD,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DeviceEntry } from "../catalog/schema.js";
2
+ import { type Platform } from "./schema.js";
3
+ export declare function loadPlatforms(devices: DeviceEntry[]): Platform[];
4
+ export declare function assertPlatformInvariants(platforms: Platform[], devices: DeviceEntry[]): void;
@@ -0,0 +1,78 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join, dirname } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { Platforms } from "./schema.js";
5
+ const repoRoot = () => {
6
+ const here = dirname(fileURLToPath(import.meta.url));
7
+ return join(here, "..", "..");
8
+ };
9
+ export function loadPlatforms(devices) {
10
+ const platformsPath = join(repoRoot(), "platforms.json");
11
+ const raw = readFileSync(platformsPath, "utf8");
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(raw);
15
+ }
16
+ catch (e) {
17
+ throw new Error(`platforms.json is not valid JSON: ${e.message}\n` +
18
+ `Path: ${platformsPath}`);
19
+ }
20
+ const result = Platforms.safeParse(parsed);
21
+ if (!result.success) {
22
+ const issues = result.error.issues
23
+ .map((i) => ` - ${i.path.join(".")}: ${i.message}`)
24
+ .join("\n");
25
+ throw new Error(`platforms.json failed schema validation:\n${issues}\n` +
26
+ `Path: ${platformsPath}`);
27
+ }
28
+ assertPlatformInvariants(result.data, devices);
29
+ return result.data;
30
+ }
31
+ export function assertPlatformInvariants(platforms, devices) {
32
+ const deviceIds = new Set(devices.map((device) => device.id));
33
+ const taggedAgentDevices = new Set(devices
34
+ .filter((device) => device.idea_fit_tags.includes("agent-gadget"))
35
+ .map((device) => device.id));
36
+ const platformIds = new Set();
37
+ const platformDevicePairs = new Set();
38
+ const officialAgentDevices = new Set();
39
+ const violations = [];
40
+ for (const platform of platforms) {
41
+ if (platformIds.has(platform.id)) {
42
+ violations.push(` - duplicate platform id "${platform.id}"`);
43
+ }
44
+ platformIds.add(platform.id);
45
+ const tierIds = new Set(platform.tiers.map((tier) => tier.id));
46
+ for (const board of platform.boards) {
47
+ const pairKey = `${platform.id}:${board.device_id}`;
48
+ if (platformDevicePairs.has(pairKey)) {
49
+ violations.push(` - duplicate board pair "${board.device_id}" in platform "${platform.id}"`);
50
+ }
51
+ platformDevicePairs.add(pairKey);
52
+ if (!deviceIds.has(board.device_id)) {
53
+ violations.push(` - platform "${platform.id}" references unknown device "${board.device_id}"`);
54
+ }
55
+ if (!tierIds.has(board.tier)) {
56
+ violations.push(` - board "${board.device_id}" uses unknown tier "${board.tier}" in platform "${platform.id}"`);
57
+ }
58
+ if (board.support === "official") {
59
+ officialAgentDevices.add(board.device_id);
60
+ }
61
+ }
62
+ }
63
+ for (const deviceId of officialAgentDevices) {
64
+ if (!taggedAgentDevices.has(deviceId)) {
65
+ violations.push(` - official platform board "${deviceId}" is missing tag "agent-gadget"`);
66
+ }
67
+ }
68
+ for (const deviceId of taggedAgentDevices) {
69
+ if (!officialAgentDevices.has(deviceId)) {
70
+ violations.push(` - device "${deviceId}" has tag "agent-gadget" but is not an official platform board`);
71
+ }
72
+ }
73
+ if (violations.length > 0) {
74
+ throw new Error(`Platform invariant violation:\n${violations.join("\n")}\n\n` +
75
+ `Fix: keep platforms.json, catalog.json and tags.md in sync.`);
76
+ }
77
+ }
78
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/platforms/load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,383 @@
1
+ import { z } from "zod";
2
+ export declare const PlatformBoard: z.ZodObject<{
3
+ device_id: z.ZodString;
4
+ support: z.ZodEnum<["official", "possible"]>;
5
+ tier: z.ZodString;
6
+ kind: z.ZodString;
7
+ build: z.ZodString;
8
+ eol: z.ZodOptional<z.ZodBoolean>;
9
+ note: z.ZodString;
10
+ features: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodBoolean, z.ZodString, z.ZodNumber, z.ZodNull]>>;
11
+ }, "strip", z.ZodTypeAny, {
12
+ note: string;
13
+ device_id: string;
14
+ support: "official" | "possible";
15
+ tier: string;
16
+ kind: string;
17
+ build: string;
18
+ features: Record<string, string | number | boolean | null>;
19
+ eol?: boolean | undefined;
20
+ }, {
21
+ note: string;
22
+ device_id: string;
23
+ support: "official" | "possible";
24
+ tier: string;
25
+ kind: string;
26
+ build: string;
27
+ features: Record<string, string | number | boolean | null>;
28
+ eol?: boolean | undefined;
29
+ }>;
30
+ export declare const Platform: z.ZodObject<{
31
+ id: z.ZodString;
32
+ name: z.ZodString;
33
+ vendor: z.ZodString;
34
+ kind: z.ZodLiteral<"agent-gadget">;
35
+ launched: z.ZodString;
36
+ homepage: z.ZodString;
37
+ sdk_repo: z.ZodString;
38
+ sdk_path: z.ZodString;
39
+ docs_url: z.ZodString;
40
+ license: z.ZodString;
41
+ summary: z.ZodString;
42
+ toolchain: z.ZodString;
43
+ requires: z.ZodArray<z.ZodString, "many">;
44
+ setup_steps: z.ZodArray<z.ZodString, "many">;
45
+ agent_quickstart: z.ZodString;
46
+ commands: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
47
+ extending: z.ZodOptional<z.ZodString>;
48
+ tiers: z.ZodArray<z.ZodObject<{
49
+ id: z.ZodString;
50
+ label: z.ZodString;
51
+ description: z.ZodString;
52
+ }, "strip", z.ZodTypeAny, {
53
+ id: string;
54
+ description: string;
55
+ label: string;
56
+ }, {
57
+ id: string;
58
+ description: string;
59
+ label: string;
60
+ }>, "many">;
61
+ caveats: z.ZodArray<z.ZodString, "many">;
62
+ terms: z.ZodObject<{
63
+ url: z.ZodString;
64
+ summary: z.ZodString;
65
+ personal_noncommercial_only: z.ZodBoolean;
66
+ max_devices_per_token: z.ZodNumber;
67
+ selling_allowed: z.ZodBoolean;
68
+ revocable: z.ZodBoolean;
69
+ }, "strip", z.ZodTypeAny, {
70
+ summary: string;
71
+ url: string;
72
+ personal_noncommercial_only: boolean;
73
+ max_devices_per_token: number;
74
+ selling_allowed: boolean;
75
+ revocable: boolean;
76
+ }, {
77
+ summary: string;
78
+ url: string;
79
+ personal_noncommercial_only: boolean;
80
+ max_devices_per_token: number;
81
+ selling_allowed: boolean;
82
+ revocable: boolean;
83
+ }>;
84
+ community_url: z.ZodString;
85
+ boards: z.ZodArray<z.ZodObject<{
86
+ device_id: z.ZodString;
87
+ support: z.ZodEnum<["official", "possible"]>;
88
+ tier: z.ZodString;
89
+ kind: z.ZodString;
90
+ build: z.ZodString;
91
+ eol: z.ZodOptional<z.ZodBoolean>;
92
+ note: z.ZodString;
93
+ features: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodBoolean, z.ZodString, z.ZodNumber, z.ZodNull]>>;
94
+ }, "strip", z.ZodTypeAny, {
95
+ note: string;
96
+ device_id: string;
97
+ support: "official" | "possible";
98
+ tier: string;
99
+ kind: string;
100
+ build: string;
101
+ features: Record<string, string | number | boolean | null>;
102
+ eol?: boolean | undefined;
103
+ }, {
104
+ note: string;
105
+ device_id: string;
106
+ support: "official" | "possible";
107
+ tier: string;
108
+ kind: string;
109
+ build: string;
110
+ features: Record<string, string | number | boolean | null>;
111
+ eol?: boolean | undefined;
112
+ }>, "many">;
113
+ sources: z.ZodArray<z.ZodString, "many">;
114
+ last_verified: z.ZodString;
115
+ }, "strip", z.ZodTypeAny, {
116
+ id: string;
117
+ name: string;
118
+ last_verified: string;
119
+ kind: "agent-gadget";
120
+ vendor: string;
121
+ launched: string;
122
+ homepage: string;
123
+ sdk_repo: string;
124
+ sdk_path: string;
125
+ docs_url: string;
126
+ license: string;
127
+ summary: string;
128
+ toolchain: string;
129
+ requires: string[];
130
+ setup_steps: string[];
131
+ agent_quickstart: string;
132
+ tiers: {
133
+ id: string;
134
+ description: string;
135
+ label: string;
136
+ }[];
137
+ caveats: string[];
138
+ terms: {
139
+ summary: string;
140
+ url: string;
141
+ personal_noncommercial_only: boolean;
142
+ max_devices_per_token: number;
143
+ selling_allowed: boolean;
144
+ revocable: boolean;
145
+ };
146
+ community_url: string;
147
+ boards: {
148
+ note: string;
149
+ device_id: string;
150
+ support: "official" | "possible";
151
+ tier: string;
152
+ kind: string;
153
+ build: string;
154
+ features: Record<string, string | number | boolean | null>;
155
+ eol?: boolean | undefined;
156
+ }[];
157
+ sources: string[];
158
+ commands?: string[] | undefined;
159
+ extending?: string | undefined;
160
+ }, {
161
+ id: string;
162
+ name: string;
163
+ last_verified: string;
164
+ kind: "agent-gadget";
165
+ vendor: string;
166
+ launched: string;
167
+ homepage: string;
168
+ sdk_repo: string;
169
+ sdk_path: string;
170
+ docs_url: string;
171
+ license: string;
172
+ summary: string;
173
+ toolchain: string;
174
+ requires: string[];
175
+ setup_steps: string[];
176
+ agent_quickstart: string;
177
+ tiers: {
178
+ id: string;
179
+ description: string;
180
+ label: string;
181
+ }[];
182
+ caveats: string[];
183
+ terms: {
184
+ summary: string;
185
+ url: string;
186
+ personal_noncommercial_only: boolean;
187
+ max_devices_per_token: number;
188
+ selling_allowed: boolean;
189
+ revocable: boolean;
190
+ };
191
+ community_url: string;
192
+ boards: {
193
+ note: string;
194
+ device_id: string;
195
+ support: "official" | "possible";
196
+ tier: string;
197
+ kind: string;
198
+ build: string;
199
+ features: Record<string, string | number | boolean | null>;
200
+ eol?: boolean | undefined;
201
+ }[];
202
+ sources: string[];
203
+ commands?: string[] | undefined;
204
+ extending?: string | undefined;
205
+ }>;
206
+ export declare const Platforms: z.ZodArray<z.ZodObject<{
207
+ id: z.ZodString;
208
+ name: z.ZodString;
209
+ vendor: z.ZodString;
210
+ kind: z.ZodLiteral<"agent-gadget">;
211
+ launched: z.ZodString;
212
+ homepage: z.ZodString;
213
+ sdk_repo: z.ZodString;
214
+ sdk_path: z.ZodString;
215
+ docs_url: z.ZodString;
216
+ license: z.ZodString;
217
+ summary: z.ZodString;
218
+ toolchain: z.ZodString;
219
+ requires: z.ZodArray<z.ZodString, "many">;
220
+ setup_steps: z.ZodArray<z.ZodString, "many">;
221
+ agent_quickstart: z.ZodString;
222
+ commands: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
223
+ extending: z.ZodOptional<z.ZodString>;
224
+ tiers: z.ZodArray<z.ZodObject<{
225
+ id: z.ZodString;
226
+ label: z.ZodString;
227
+ description: z.ZodString;
228
+ }, "strip", z.ZodTypeAny, {
229
+ id: string;
230
+ description: string;
231
+ label: string;
232
+ }, {
233
+ id: string;
234
+ description: string;
235
+ label: string;
236
+ }>, "many">;
237
+ caveats: z.ZodArray<z.ZodString, "many">;
238
+ terms: z.ZodObject<{
239
+ url: z.ZodString;
240
+ summary: z.ZodString;
241
+ personal_noncommercial_only: z.ZodBoolean;
242
+ max_devices_per_token: z.ZodNumber;
243
+ selling_allowed: z.ZodBoolean;
244
+ revocable: z.ZodBoolean;
245
+ }, "strip", z.ZodTypeAny, {
246
+ summary: string;
247
+ url: string;
248
+ personal_noncommercial_only: boolean;
249
+ max_devices_per_token: number;
250
+ selling_allowed: boolean;
251
+ revocable: boolean;
252
+ }, {
253
+ summary: string;
254
+ url: string;
255
+ personal_noncommercial_only: boolean;
256
+ max_devices_per_token: number;
257
+ selling_allowed: boolean;
258
+ revocable: boolean;
259
+ }>;
260
+ community_url: z.ZodString;
261
+ boards: z.ZodArray<z.ZodObject<{
262
+ device_id: z.ZodString;
263
+ support: z.ZodEnum<["official", "possible"]>;
264
+ tier: z.ZodString;
265
+ kind: z.ZodString;
266
+ build: z.ZodString;
267
+ eol: z.ZodOptional<z.ZodBoolean>;
268
+ note: z.ZodString;
269
+ features: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodBoolean, z.ZodString, z.ZodNumber, z.ZodNull]>>;
270
+ }, "strip", z.ZodTypeAny, {
271
+ note: string;
272
+ device_id: string;
273
+ support: "official" | "possible";
274
+ tier: string;
275
+ kind: string;
276
+ build: string;
277
+ features: Record<string, string | number | boolean | null>;
278
+ eol?: boolean | undefined;
279
+ }, {
280
+ note: string;
281
+ device_id: string;
282
+ support: "official" | "possible";
283
+ tier: string;
284
+ kind: string;
285
+ build: string;
286
+ features: Record<string, string | number | boolean | null>;
287
+ eol?: boolean | undefined;
288
+ }>, "many">;
289
+ sources: z.ZodArray<z.ZodString, "many">;
290
+ last_verified: z.ZodString;
291
+ }, "strip", z.ZodTypeAny, {
292
+ id: string;
293
+ name: string;
294
+ last_verified: string;
295
+ kind: "agent-gadget";
296
+ vendor: string;
297
+ launched: string;
298
+ homepage: string;
299
+ sdk_repo: string;
300
+ sdk_path: string;
301
+ docs_url: string;
302
+ license: string;
303
+ summary: string;
304
+ toolchain: string;
305
+ requires: string[];
306
+ setup_steps: string[];
307
+ agent_quickstart: string;
308
+ tiers: {
309
+ id: string;
310
+ description: string;
311
+ label: string;
312
+ }[];
313
+ caveats: string[];
314
+ terms: {
315
+ summary: string;
316
+ url: string;
317
+ personal_noncommercial_only: boolean;
318
+ max_devices_per_token: number;
319
+ selling_allowed: boolean;
320
+ revocable: boolean;
321
+ };
322
+ community_url: string;
323
+ boards: {
324
+ note: string;
325
+ device_id: string;
326
+ support: "official" | "possible";
327
+ tier: string;
328
+ kind: string;
329
+ build: string;
330
+ features: Record<string, string | number | boolean | null>;
331
+ eol?: boolean | undefined;
332
+ }[];
333
+ sources: string[];
334
+ commands?: string[] | undefined;
335
+ extending?: string | undefined;
336
+ }, {
337
+ id: string;
338
+ name: string;
339
+ last_verified: string;
340
+ kind: "agent-gadget";
341
+ vendor: string;
342
+ launched: string;
343
+ homepage: string;
344
+ sdk_repo: string;
345
+ sdk_path: string;
346
+ docs_url: string;
347
+ license: string;
348
+ summary: string;
349
+ toolchain: string;
350
+ requires: string[];
351
+ setup_steps: string[];
352
+ agent_quickstart: string;
353
+ tiers: {
354
+ id: string;
355
+ description: string;
356
+ label: string;
357
+ }[];
358
+ caveats: string[];
359
+ terms: {
360
+ summary: string;
361
+ url: string;
362
+ personal_noncommercial_only: boolean;
363
+ max_devices_per_token: number;
364
+ selling_allowed: boolean;
365
+ revocable: boolean;
366
+ };
367
+ community_url: string;
368
+ boards: {
369
+ note: string;
370
+ device_id: string;
371
+ support: "official" | "possible";
372
+ tier: string;
373
+ kind: string;
374
+ build: string;
375
+ features: Record<string, string | number | boolean | null>;
376
+ eol?: boolean | undefined;
377
+ }[];
378
+ sources: string[];
379
+ commands?: string[] | undefined;
380
+ extending?: string | undefined;
381
+ }>, "many">;
382
+ export type Platform = z.infer<typeof Platform>;
383
+ export type PlatformBoard = z.infer<typeof PlatformBoard>;
@@ -0,0 +1,53 @@
1
+ import { z } from "zod";
2
+ const KebabId = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "id must be kebab-case");
3
+ const IsoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "date must be YYYY-MM-DD");
4
+ const FeatureValue = z.union([z.boolean(), z.string(), z.number(), z.null()]);
5
+ export const PlatformBoard = z.object({
6
+ device_id: z.string().min(1),
7
+ support: z.enum(["official", "possible"]),
8
+ tier: z.string().min(1),
9
+ kind: z.string().min(1),
10
+ build: z.string().min(1),
11
+ eol: z.boolean().optional(),
12
+ note: z.string().min(1),
13
+ features: z.record(FeatureValue),
14
+ });
15
+ export const Platform = z.object({
16
+ id: KebabId,
17
+ name: z.string().min(1),
18
+ vendor: z.string().min(1),
19
+ kind: z.literal("agent-gadget"),
20
+ launched: IsoDate,
21
+ homepage: z.string().url(),
22
+ sdk_repo: z.string().url(),
23
+ sdk_path: z.string().min(1),
24
+ docs_url: z.string().url(),
25
+ license: z.string().min(1),
26
+ summary: z.string().min(1),
27
+ toolchain: z.string().min(1),
28
+ requires: z.array(z.string().min(1)),
29
+ setup_steps: z.array(z.string().min(1)),
30
+ agent_quickstart: z.string().min(1),
31
+ commands: z.array(z.string().min(1)).optional(),
32
+ extending: z.string().optional(),
33
+ tiers: z.array(z.object({
34
+ id: z.string().min(1),
35
+ label: z.string().min(1),
36
+ description: z.string().min(1),
37
+ })).min(1),
38
+ caveats: z.array(z.string().min(1)),
39
+ terms: z.object({
40
+ url: z.string().url(),
41
+ summary: z.string().min(1),
42
+ personal_noncommercial_only: z.boolean(),
43
+ max_devices_per_token: z.number().int().positive(),
44
+ selling_allowed: z.boolean(),
45
+ revocable: z.boolean(),
46
+ }),
47
+ community_url: z.string().url(),
48
+ boards: z.array(PlatformBoard).min(1),
49
+ sources: z.array(z.string().url()),
50
+ last_verified: IsoDate,
51
+ });
52
+ export const Platforms = z.array(Platform).min(1);
53
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/platforms/schema.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,12 @@
1
1
  import type { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ export interface SamplingDiagnostics {
3
+ apiFallbackError?: string;
4
+ }
2
5
  export declare function sampleJson<T>(opts: {
3
6
  server: Server;
4
7
  systemPrompt: string;
5
8
  userPrompt: string;
6
9
  maxTokens?: number;
10
+ diagnostics?: SamplingDiagnostics;
7
11
  }): Promise<T | null>;
8
12
  export declare function probeSamplingSupport(server: Server): Promise<boolean>;
package/dist/sampling.js CHANGED
@@ -1,21 +1,6 @@
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 } = opts;
19
4
  const tryHostSampling = async () => {
20
5
  try {
21
6
  const response = await server.createMessage({
@@ -49,13 +34,18 @@ export async function sampleJson(opts) {
49
34
  const second = await tryHostSampling();
50
35
  if (second !== null)
51
36
  return second;
52
- // Attempt 3: direct Anthropic API fallback (env-gated)
37
+ // Attempt 3: direct Anthropic API fallback (env-gated). Default model id kept
38
+ // current ('claude-sonnet-4-6'); override via HACKSHOP_MODEL. If this throws
39
+ // WHILE a key was present (e.g. the default model was retired), we record the
40
+ // error in `diagnostics` so the caller can make the failure user-visible
41
+ // instead of degrading silently to stderr only.
53
42
  const apiKey = process.env.ANTHROPIC_API_KEY;
54
43
  if (apiKey) {
44
+ const fallbackModel = process.env.HACKSHOP_MODEL ?? "claude-sonnet-4-6";
55
45
  try {
56
46
  const client = new Anthropic({ apiKey });
57
47
  const response = await client.messages.create({
58
- model: process.env.HACKSHOP_MODEL ?? "claude-sonnet-4-6",
48
+ model: fallbackModel,
59
49
  max_tokens: maxTokens,
60
50
  system: systemPrompt,
61
51
  messages: [{ role: "user", content: userPrompt }],
@@ -68,9 +58,17 @@ export async function sampleJson(opts) {
68
58
  return parsed;
69
59
  }
70
60
  }
61
+ // Key present, call succeeded, but content was unusable — note it.
62
+ if (diagnostics) {
63
+ diagnostics.apiFallbackError = `Anthropic API fallback (model "${fallbackModel}") returned no usable JSON.`;
64
+ }
71
65
  }
72
66
  catch (err) {
73
- process.stderr.write(`[hackshop-mcp] Anthropic fallback failed: ${err.message}\n`);
67
+ const msg = err.message;
68
+ process.stderr.write(`[hackshop-mcp] Anthropic fallback failed: ${msg}\n`);
69
+ if (diagnostics) {
70
+ diagnostics.apiFallbackError = `Anthropic API fallback failed (model "${fallbackModel}"): ${msg}. If the model id was retired, set HACKSHOP_MODEL to a current model.`;
71
+ }
74
72
  }
75
73
  }
76
74
  return null;