@shippp/mcp 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +290 -99
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -18,7 +18,208 @@ function loadConfig() {
18
18
  return { apiUrl, apiKey };
19
19
  }
20
20
 
21
+ // ../../src/lib/repoSync/wireReceipt.ts
22
+ import { createHash } from "node:crypto";
23
+ import { z as z2 } from "zod";
24
+
25
+ // ../../src/lib/repoSync/receiptContract.ts
26
+ import { z } from "zod";
27
+ var hash = z.string().regex(/^sha256:[0-9a-f]{64}$/);
28
+ var receiptFileEntrySchema = z.object({
29
+ path: z.string().min(1),
30
+ hash,
31
+ bytes: z.number().int().nonnegative()
32
+ });
33
+ var syncReceiptSchema = z.object({
34
+ shippp_receipt: z.literal("v2"),
35
+ // `revision`/`versionId`: MC-9.2 kompatibilis bővítés — hiányuk a bővítés
36
+ // előtti receipt érvényes olvasata; a zod-objektum strippelne, ezért a
37
+ // mezők itt is deklaráltak, hogy a repóba írt receipt megőrizze őket.
38
+ source: z.object({
39
+ file: z.string(),
40
+ unit: z.string(),
41
+ version: hash,
42
+ revision: z.number().int().nullable().optional(),
43
+ versionId: z.string().optional()
44
+ }),
45
+ target: z.object({
46
+ outputProfile: z.string().min(1),
47
+ profileSchemaVersion: z.number().int().positive(),
48
+ adapterVersion: z.string().min(1),
49
+ packagerVersion: z.string().min(1),
50
+ packages: z.object({
51
+ dependencies: z.record(z.string(), z.string()),
52
+ devDependencies: z.record(z.string(), z.string())
53
+ })
54
+ }),
55
+ config: z.object({ engine: z.string().min(1), irVersion: z.string().min(1), tokens: hash }),
56
+ output: z.object({ hash, files: z.array(receiptFileEntrySchema) }),
57
+ reproducible: z.literal(true),
58
+ generated_at: z.string()
59
+ });
60
+ var ReceiptParseError = class extends Error {
61
+ };
62
+ function parseSyncReceipt(raw) {
63
+ let json;
64
+ try {
65
+ json = JSON.parse(raw);
66
+ } catch {
67
+ throw new ReceiptParseError("A receipt nem \xE9rv\xE9nyes JSON.");
68
+ }
69
+ const parsed = syncReceiptSchema.safeParse(json);
70
+ if (!parsed.success) {
71
+ throw new ReceiptParseError(
72
+ `A receipt nem felel meg a v2 kontraktusnak: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
73
+ );
74
+ }
75
+ return parsed.data;
76
+ }
77
+ function serializeSyncReceipt(receipt) {
78
+ return `${JSON.stringify(receipt, null, 2)}
79
+ `;
80
+ }
81
+ var compilerEnvOfReceipt = (r) => ({
82
+ engine: r.config.engine,
83
+ irVersion: r.config.irVersion,
84
+ adapterVersion: r.target.adapterVersion,
85
+ packagerVersion: r.target.packagerVersion
86
+ });
87
+ function compilerDeltaOf(previous, next) {
88
+ const delta = [];
89
+ if (previous.config.engine !== next.engine) delta.push(`engine: ${previous.config.engine} \u2192 ${next.engine}`);
90
+ if (previous.config.irVersion !== next.irVersion) delta.push(`IR: ${previous.config.irVersion} \u2192 ${next.irVersion}`);
91
+ if (previous.target.adapterVersion !== next.adapterVersion) {
92
+ delta.push(`adapter: ${previous.target.adapterVersion} \u2192 ${next.adapterVersion}`);
93
+ }
94
+ if (previous.target.packagerVersion !== next.packagerVersion) {
95
+ delta.push(`packager: ${previous.target.packagerVersion} \u2192 ${next.packagerVersion}`);
96
+ }
97
+ return delta;
98
+ }
99
+
100
+ // ../../src/lib/repoSync/wireReceipt.ts
101
+ var wireReceiptRefEntrySchema = z2.object({
102
+ path: z2.string().min(1),
103
+ bytes: z2.number().int().nonnegative(),
104
+ ref: z2.object({ uploadId: z2.string().min(1) })
105
+ });
106
+ var wireReceiptOutputEntrySchema = z2.union([receiptFileEntrySchema, wireReceiptRefEntrySchema]);
107
+ var wireSyncReceiptSchema = syncReceiptSchema.extend({
108
+ output: z2.object({ hash, files: z2.array(wireReceiptOutputEntrySchema) })
109
+ });
110
+ var WireReceiptError = class extends Error {
111
+ };
112
+ function parseWireSyncReceipt(raw) {
113
+ let json;
114
+ try {
115
+ json = JSON.parse(raw);
116
+ } catch {
117
+ throw new WireReceiptError("A wire-receipt nem \xE9rv\xE9nyes JSON.");
118
+ }
119
+ const parsed = wireSyncReceiptSchema.safeParse(json);
120
+ if (!parsed.success) {
121
+ throw new WireReceiptError(
122
+ `A wire-receipt nem felel meg a v2 kontraktusnak: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
123
+ );
124
+ }
125
+ return parsed.data;
126
+ }
127
+ var sha = (s) => `sha256:${createHash("sha256").update(s).digest("hex")}`;
128
+ function outputCanonicalOf(entries) {
129
+ return [...entries].sort((a, b) => a.path.localeCompare(b.path)).map((e) => "ref" in e ? `${e.path}\0ref:${e.ref.uploadId}:${e.bytes}` : `${e.path}\0${e.hash}`).join("\0");
130
+ }
131
+ function outputHashOfEntries(entries) {
132
+ return sha(outputCanonicalOf(entries));
133
+ }
134
+ function completeWireReceipt(wire, bytesByPath) {
135
+ const files = wire.output.files.map((entry) => {
136
+ if (!("ref" in entry)) return entry;
137
+ const bytes = bytesByPath.get(entry.path);
138
+ if (!bytes) {
139
+ throw new WireReceiptError(`A wire-receipt ref-sora feloldatlan maradt: ${entry.path} (${entry.ref.uploadId}).`);
140
+ }
141
+ if (bytes.byteLength !== entry.bytes) {
142
+ throw new WireReceiptError(
143
+ `A feloldott asset m\xE9rete nem egyezik a receipt kontraktus\xE1val: ${entry.path} \u2014 v\xE1rt ${entry.bytes}, kapott ${bytes.byteLength} b\xE1jt.`
144
+ );
145
+ }
146
+ return { path: entry.path, hash: sha(bytes), bytes: bytes.byteLength };
147
+ });
148
+ const completed = { ...wire, output: { hash: outputHashOfEntries(files), files } };
149
+ const parsed = syncReceiptSchema.safeParse(completed);
150
+ if (!parsed.success) {
151
+ throw new WireReceiptError(
152
+ `A kieg\xE9sz\xEDtett receipt nem felel meg a sync-kontraktusnak: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
153
+ );
154
+ }
155
+ return parsed.data;
156
+ }
157
+
158
+ // ../../src/lib/repoSync/exportArtifactContract.ts
159
+ var ExportArtifactContractError = class extends Error {
160
+ };
161
+ function bytesOfBase64(b64) {
162
+ if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") {
163
+ return new Uint8Array(Buffer.from(b64, "base64"));
164
+ }
165
+ const binary = atob(b64);
166
+ const out = new Uint8Array(binary.length);
167
+ for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
168
+ return out;
169
+ }
170
+ function inlineFileOf(entry) {
171
+ if (typeof entry.path !== "string" || typeof entry.content !== "string") {
172
+ throw new ExportArtifactContractError("The export artifact container has a malformed file row.");
173
+ }
174
+ return entry.encoding === "base64" ? { path: entry.path, content: bytesOfBase64(entry.content) } : { path: entry.path, content: entry.content };
175
+ }
176
+ function refOf(entry) {
177
+ const ref = entry.ref;
178
+ if (typeof entry.path !== "string" || !ref || typeof ref.uploadId !== "string" || typeof ref.downloadPath !== "string" || typeof ref.bytes !== "number" || !Number.isFinite(ref.bytes) || ref.bytes < 0) {
179
+ throw new ExportArtifactContractError("The export artifact container has a malformed asset-reference row.");
180
+ }
181
+ return {
182
+ path: entry.path,
183
+ uploadId: ref.uploadId,
184
+ downloadPath: ref.downloadPath,
185
+ bytes: ref.bytes,
186
+ contentType: typeof ref.contentType === "string" ? ref.contentType : null
187
+ };
188
+ }
189
+ function parseExportArtifactJson(json) {
190
+ let parsed;
191
+ try {
192
+ parsed = JSON.parse(json);
193
+ } catch {
194
+ throw new ExportArtifactContractError("The export artifact container is not valid JSON.");
195
+ }
196
+ const container = parsed;
197
+ const version = container.shippp_artifact;
198
+ if (version !== "v1" && version !== "v2" || !Array.isArray(container.files)) {
199
+ throw new ExportArtifactContractError(
200
+ `Unknown export artifact container shape (shippp_artifact: ${JSON.stringify(version ?? null)}) \u2014 this reader supports v1 and v2.`
201
+ );
202
+ }
203
+ const files = [];
204
+ const refs = [];
205
+ for (const raw of container.files) {
206
+ if (raw?.encoding === "ref") {
207
+ if (version === "v1") {
208
+ throw new ExportArtifactContractError("A v1 export artifact container cannot carry asset-reference rows.");
209
+ }
210
+ refs.push(refOf(raw));
211
+ continue;
212
+ }
213
+ files.push(inlineFileOf(raw ?? {}));
214
+ }
215
+ return { version, files, refs };
216
+ }
217
+
21
218
  // src/client.ts
219
+ var REQUEST_TIMEOUT_MS = 6e4;
220
+ var GENERATE_TIMEOUT_MS = 12e4;
221
+ var ASSET_FETCH_TIMEOUT_MS = 18e4;
222
+ var ASSET_FETCH_CONCURRENCY = 4;
22
223
  var ShipppApiError = class extends Error {
23
224
  constructor(status, message) {
24
225
  super(message);
@@ -26,20 +227,16 @@ var ShipppApiError = class extends Error {
26
227
  this.name = "ShipppApiError";
27
228
  }
28
229
  };
29
- function decodeFiles(files) {
30
- return files.map(
31
- (f) => f.encoding === "base64" ? { path: f.path, content: new Uint8Array(Buffer.from(f.content, "base64")) } : { path: f.path, content: f.content }
32
- );
33
- }
34
230
  var ShipppClient = class {
35
231
  constructor(config) {
36
232
  this.config = config;
37
233
  }
38
- async request(path2, init) {
234
+ async request(path2, init, timeoutMs = REQUEST_TIMEOUT_MS) {
39
235
  let res;
40
236
  try {
41
237
  res = await fetch(`${this.config.apiUrl}${path2}`, {
42
238
  ...init,
239
+ signal: AbortSignal.timeout(timeoutMs),
43
240
  headers: {
44
241
  Authorization: `Bearer ${this.config.apiKey}`,
45
242
  "Content-Type": "application/json",
@@ -47,7 +244,11 @@ var ShipppClient = class {
47
244
  }
48
245
  });
49
246
  } catch (e) {
50
- throw new ShipppApiError(0, `Could not reach Shippp at ${this.config.apiUrl}: ${e.message}`);
247
+ const err = e;
248
+ if (err.name === "TimeoutError") {
249
+ throw new ShipppApiError(0, `Shippp did not answer ${path2} within ${Math.round(timeoutMs / 1e3)}s.`);
250
+ }
251
+ throw new ShipppApiError(0, `Could not reach Shippp at ${this.config.apiUrl}: ${err.message}`);
51
252
  }
52
253
  const body = await res.json().catch(() => ({}));
53
254
  if (!res.ok) throw new ShipppApiError(res.status, body?.error || `HTTP ${res.status}`);
@@ -57,42 +258,98 @@ var ShipppClient = class {
57
258
  manifest(fileId2) {
58
259
  return this.request(`/api/mcp/manifest?fileId=${encodeURIComponent(fileId2)}`, { method: "GET" });
59
260
  }
60
- /** Generate code for a unit (or a diff against a baseline). */
261
+ /** Resolves the versioned (v2) asset references of a generate response:
262
+ * each referenced binary is fetched from its authorized download path with
263
+ * the same Bearer credential, under bounded parallelism and a per-asset
264
+ * deadline, and must match the reference's byte size exactly — a partial
265
+ * result is never a success; the failure names every affected asset. */
266
+ async downloadAssetRefs(refs) {
267
+ const out = /* @__PURE__ */ new Map();
268
+ const failures = [];
269
+ let next = 0;
270
+ const worker = async () => {
271
+ for (; ; ) {
272
+ const i = next++;
273
+ if (i >= refs.length) return;
274
+ const ref = refs[i];
275
+ try {
276
+ const res = await fetch(`${this.config.apiUrl}${ref.downloadPath}`, {
277
+ headers: { Authorization: `Bearer ${this.config.apiKey}` },
278
+ signal: AbortSignal.timeout(ASSET_FETCH_TIMEOUT_MS)
279
+ });
280
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
281
+ const bytes = new Uint8Array(await res.arrayBuffer());
282
+ if (bytes.byteLength !== ref.bytes) {
283
+ throw new Error(`size mismatch: expected ${ref.bytes} bytes, got ${bytes.byteLength}`);
284
+ }
285
+ out.set(ref.path, bytes);
286
+ } catch (e) {
287
+ const err = e;
288
+ failures.push(`${ref.path} (${err.name === "TimeoutError" ? "timed out" : err.message})`);
289
+ }
290
+ }
291
+ };
292
+ await Promise.all(Array.from({ length: Math.min(ASSET_FETCH_CONCURRENCY, refs.length) }, worker));
293
+ if (failures.length > 0) {
294
+ throw new ShipppApiError(
295
+ 0,
296
+ `Failed to download ${failures.length} of ${refs.length} referenced export assets: ${failures.join("; ")}`
297
+ );
298
+ }
299
+ return out;
300
+ }
301
+ /** Generate code for a unit (or a diff against a baseline). This client
302
+ * declares the versioned asset-reference capability (artifact contract v2):
303
+ * above the server's inline budget the image mass arrives as authorized
304
+ * download references, which are resolved here into byte-complete files and
305
+ * a hash-complete receipt — identical to what an inline delivery would have
306
+ * produced, so the sync core and every consumer see one artifact shape. */
61
307
  async generate(fileId2, request, baseline) {
62
308
  const body = await this.request(
63
309
  "/api/mcp/generate",
64
310
  {
65
311
  method: "POST",
66
- body: JSON.stringify(baseline ? { fileId: fileId2, request, baseline } : { fileId: fileId2, request })
67
- }
312
+ body: JSON.stringify(
313
+ baseline ? { fileId: fileId2, request, baseline, acceptAssetRefs: true } : { fileId: fileId2, request, acceptAssetRefs: true }
314
+ )
315
+ },
316
+ GENERATE_TIMEOUT_MS
68
317
  );
69
- if ("files" in body) return { ...body, files: decodeFiles(body.files) };
70
- return body;
318
+ if (!("files" in body)) return body;
319
+ const { shippp_artifact, files: rawFiles, receipt: rawReceipt, ...rest } = body;
320
+ const parsed = parseExportArtifactJson(JSON.stringify({ shippp_artifact: shippp_artifact ?? "v1", files: rawFiles }));
321
+ if (parsed.refs.length === 0) {
322
+ return { ...rest, files: parsed.files, receipt: rawReceipt };
323
+ }
324
+ const bytesByPath = await this.downloadAssetRefs(parsed.refs);
325
+ const receipt = completeWireReceipt(parseWireSyncReceipt(JSON.stringify(rawReceipt)), bytesByPath);
326
+ const resolved = parsed.refs.map((r) => ({ path: r.path, content: bytesByPath.get(r.path) }));
327
+ return { ...rest, files: [...parsed.files, ...resolved], receipt };
71
328
  }
72
329
  };
73
330
 
74
331
  // src/tools.ts
75
- import { z as z3 } from "zod";
332
+ import { z as z4 } from "zod";
76
333
 
77
334
  // src/runner.ts
78
335
  import { exec } from "node:child_process";
79
336
  import { promisify } from "node:util";
80
337
 
81
338
  // ../../src/lib/repoSync/applySync.ts
82
- import { createHash } from "node:crypto";
339
+ import { createHash as createHash2 } from "node:crypto";
83
340
 
84
341
  // ../../src/lib/repoSync/targetConfig.ts
85
- import { z } from "zod";
342
+ import { z as z3 } from "zod";
86
343
  var TARGET_CONFIG_PATH = "shippp.target.json";
87
344
  var TARGET_CONFIG_SCHEMA_VERSION = 1;
88
345
  var DEFAULT_SYNC_UNIT = "project";
89
- var targetConfigSchema = z.object({
90
- schemaVersion: z.literal(TARGET_CONFIG_SCHEMA_VERSION),
91
- profile: z.string().min(1),
92
- profileSchemaVersion: z.number().int().positive(),
93
- generatedRoot: z.string().min(1),
94
- project: z.object({ fileId: z.string().min(1), name: z.string().min(1) }).optional(),
95
- unit: z.enum(["project", "design-system"]).optional()
346
+ var targetConfigSchema = z3.object({
347
+ schemaVersion: z3.literal(TARGET_CONFIG_SCHEMA_VERSION),
348
+ profile: z3.string().min(1),
349
+ profileSchemaVersion: z3.number().int().positive(),
350
+ generatedRoot: z3.string().min(1),
351
+ project: z3.object({ fileId: z3.string().min(1), name: z3.string().min(1) }).optional(),
352
+ unit: z3.enum(["project", "design-system"]).optional()
96
353
  });
97
354
  var TargetConfigError = class extends Error {
98
355
  };
@@ -120,72 +377,6 @@ function parseTargetConfig(raw) {
120
377
  return parsed.data;
121
378
  }
122
379
 
123
- // ../../src/lib/repoSync/receiptContract.ts
124
- import { z as z2 } from "zod";
125
- var hash = z2.string().regex(/^sha256:[0-9a-f]{64}$/);
126
- var receiptFileEntrySchema = z2.object({
127
- path: z2.string().min(1),
128
- hash,
129
- bytes: z2.number().int().nonnegative()
130
- });
131
- var syncReceiptSchema = z2.object({
132
- shippp_receipt: z2.literal("v2"),
133
- source: z2.object({ file: z2.string(), unit: z2.string(), version: hash }),
134
- target: z2.object({
135
- outputProfile: z2.string().min(1),
136
- profileSchemaVersion: z2.number().int().positive(),
137
- adapterVersion: z2.string().min(1),
138
- packagerVersion: z2.string().min(1),
139
- packages: z2.object({
140
- dependencies: z2.record(z2.string(), z2.string()),
141
- devDependencies: z2.record(z2.string(), z2.string())
142
- })
143
- }),
144
- config: z2.object({ engine: z2.string().min(1), irVersion: z2.string().min(1), tokens: hash }),
145
- output: z2.object({ hash, files: z2.array(receiptFileEntrySchema) }),
146
- reproducible: z2.literal(true),
147
- generated_at: z2.string()
148
- });
149
- var ReceiptParseError = class extends Error {
150
- };
151
- function parseSyncReceipt(raw) {
152
- let json;
153
- try {
154
- json = JSON.parse(raw);
155
- } catch {
156
- throw new ReceiptParseError("A receipt nem \xE9rv\xE9nyes JSON.");
157
- }
158
- const parsed = syncReceiptSchema.safeParse(json);
159
- if (!parsed.success) {
160
- throw new ReceiptParseError(
161
- `A receipt nem felel meg a v2 kontraktusnak: ${parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
162
- );
163
- }
164
- return parsed.data;
165
- }
166
- function serializeSyncReceipt(receipt) {
167
- return `${JSON.stringify(receipt, null, 2)}
168
- `;
169
- }
170
- var compilerEnvOfReceipt = (r) => ({
171
- engine: r.config.engine,
172
- irVersion: r.config.irVersion,
173
- adapterVersion: r.target.adapterVersion,
174
- packagerVersion: r.target.packagerVersion
175
- });
176
- function compilerDeltaOf(previous, next) {
177
- const delta = [];
178
- if (previous.config.engine !== next.engine) delta.push(`engine: ${previous.config.engine} \u2192 ${next.engine}`);
179
- if (previous.config.irVersion !== next.irVersion) delta.push(`IR: ${previous.config.irVersion} \u2192 ${next.irVersion}`);
180
- if (previous.target.adapterVersion !== next.adapterVersion) {
181
- delta.push(`adapter: ${previous.target.adapterVersion} \u2192 ${next.adapterVersion}`);
182
- }
183
- if (previous.target.packagerVersion !== next.packagerVersion) {
184
- delta.push(`packager: ${previous.target.packagerVersion} \u2192 ${next.packagerVersion}`);
185
- }
186
- return delta;
187
- }
188
-
189
380
  // ../../src/lib/repoSync/syncPlan.ts
190
381
  function computeSyncPlan(input) {
191
382
  const entries = [];
@@ -234,7 +425,7 @@ function computeSyncPlan(input) {
234
425
 
235
426
  // ../../src/lib/repoSync/applySync.ts
236
427
  var RECEIPT_FILENAME = "shippp.receipt.json";
237
- var sha256 = (content) => `sha256:${createHash("sha256").update(content).digest("hex")}`;
428
+ var sha256 = (content) => `sha256:${createHash2("sha256").update(content).digest("hex")}`;
238
429
  var refusal = (code, message, conflicts) => ({
239
430
  status: "refused",
240
431
  code,
@@ -588,7 +779,7 @@ ${changed}
588
779
  --- RECEIPT ---
589
780
  ${JSON.stringify(result.receipt, null, 2)}`;
590
781
  }
591
- var fileId = z3.string().min(1).describe("The Shippp file id (from the file's URL, or shippp_list_units).");
782
+ var fileId = z4.string().min(1).describe("The Shippp file id (from the file's URL, or shippp_list_units).");
592
783
  function registerTools(server, client) {
593
784
  server.registerTool(
594
785
  "shippp_list_units",
@@ -613,8 +804,8 @@ function registerTools(server, client) {
613
804
  description: "Get ONE component's deterministic Chakra UI code + a reproducibility receipt. `unitType` is 'master' (a design-system component) or 'screen' (a depth-0 artboard). Get ids from shippp_list_units.",
614
805
  inputSchema: {
615
806
  fileId,
616
- unitId: z3.string().min(1).describe("The master/screen id from shippp_list_units."),
617
- unitType: z3.enum(["master", "screen"])
807
+ unitId: z4.string().min(1).describe("The master/screen id from shippp_list_units."),
808
+ unitType: z4.enum(["master", "screen"])
618
809
  }
619
810
  },
620
811
  async ({ fileId: fileId2, unitId, unitType }) => {
@@ -677,10 +868,10 @@ function registerTools(server, client) {
677
868
  description: "Diff your CURRENT code against a fresh deterministic generation of a unit \u2014 shows exactly what a designer changed. Pass the same unit params as the get tools, plus `baseline` (the files you currently have for that unit).",
678
869
  inputSchema: {
679
870
  fileId,
680
- unit: z3.enum(["tokens", "design-system", "project", "component"]),
681
- unitId: z3.string().optional().describe("Required when unit='component'."),
682
- unitType: z3.enum(["master", "screen"]).optional().describe("Required when unit='component'."),
683
- baseline: z3.array(z3.object({ path: z3.string(), content: z3.string() })).describe("Your current files for this unit (path + content).")
871
+ unit: z4.enum(["tokens", "design-system", "project", "component"]),
872
+ unitId: z4.string().optional().describe("Required when unit='component'."),
873
+ unitType: z4.enum(["master", "screen"]).optional().describe("Required when unit='component'."),
874
+ baseline: z4.array(z4.object({ path: z4.string(), content: z4.string() })).describe("Your current files for this unit (path + content).")
684
875
  }
685
876
  },
686
877
  async ({ fileId: fileId2, unit, unitId, unitType, baseline }) => {
@@ -721,10 +912,10 @@ function registerTools(server, client) {
721
912
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
722
913
  description: "Run the full repository sync with the bundled deterministic runner: fetch the bound unit's artifact, plan against the real on-disk state, apply atomically with rollback, run the repo's checks (SHIPPP_SYNC_CHECKS env), and write the receipt ONLY on full success. Writes are confined to the generated root of the repo's own binding; hand-modified generated files are structured conflicts and are never overwritten. If the plan or a refusal reports a compilerDelta, present it to the user and re-call with approvedCompilerDelta set to EXACTLY those lines ONLY after their explicit approval. After an approved re-bind, pass acceptReboundBaseline to adopt the previous binding's receipt as baseline.",
723
914
  inputSchema: {
724
- approvedCompilerDelta: z3.array(z3.string().min(1).max(500)).max(20).optional().describe(
915
+ approvedCompilerDelta: z4.array(z4.string().min(1).max(500)).max(20).optional().describe(
725
916
  "The exact compilerDelta lines the user explicitly approved \u2014 a mismatch with the current delta refuses."
726
917
  ),
727
- acceptReboundBaseline: z3.boolean().optional().describe("Explicit adoption of a previous binding's leftover receipt as hash baseline after a re-bind.")
918
+ acceptReboundBaseline: z4.boolean().optional().describe("Explicit adoption of a previous binding's leftover receipt as hash baseline after a re-bind.")
728
919
  }
729
920
  },
730
921
  async ({ approvedCompilerDelta, acceptReboundBaseline }) => {
@@ -801,7 +992,7 @@ async function main() {
801
992
  process.exit(1);
802
993
  }
803
994
  const config = loadConfig();
804
- const server = new McpServer({ name: "shippp", version: "0.2.0" }, { instructions: INSTRUCTIONS });
995
+ const server = new McpServer({ name: "shippp", version: "0.3.0" }, { instructions: INSTRUCTIONS });
805
996
  registerTools(server, new ShipppClient(config));
806
997
  await server.connect(new StdioServerTransport());
807
998
  console.error(`[shippp-mcp] connected via stdio \u2192 ${config.apiUrl}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shippp/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "mcpName": "ai.shippp/mcp",
5
5
  "description": "Shippp MCP server + local sync runner — pull deterministic, production-perfect UI code from your Shippp design files, and keep your repository current over the receipt-tracked sync.",
6
6
  "type": "module",