vector-mirror 1.0.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.
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/package.json +65 -0
- package/src/adapters/emitter/prose.js +689 -0
- package/src/adapters/emitter/structured.js +649 -0
- package/src/adapters/renderer/playwright.js +7345 -0
- package/src/core/arbitrate.js +266 -0
- package/src/core/constraints/_schema.js +89 -0
- package/src/core/constraints/aligned.js +42 -0
- package/src/core/constraints/centered-in.js +29 -0
- package/src/core/constraints/color.js +63 -0
- package/src/core/constraints/distance.js +233 -0
- package/src/core/constraints/fill.js +22 -0
- package/src/core/constraints/inside.js +52 -0
- package/src/core/constraints/loader.js +65 -0
- package/src/core/constraints/no-overlap.js +50 -0
- package/src/core/constraints/positional.js +46 -0
- package/src/core/constraints/registry.js +98 -0
- package/src/core/constraints/same-size.js +35 -0
- package/src/core/diff.js +118 -0
- package/src/core/element_vocabulary.js +241 -0
- package/src/core/grid.js +240 -0
- package/src/core/honesty.js +214 -0
- package/src/core/sanitizer/auto_ids.js +104 -0
- package/src/core/tolerance.js +22 -0
- package/src/core/use_graph.js +541 -0
- package/src/interface/claims.js +439 -0
- package/src/interface/schema.js +626 -0
- package/src/interface/server.js +57 -0
- package/src/interface/tools.js +437 -0
- package/src/lib/bbox.js +17 -0
- package/src/lib/breaker.js +240 -0
- package/src/lib/geom.js +144 -0
- package/src/lib/palette.js +236 -0
- package/src/lib/transforms.js +111 -0
- package/src/pipeline.js +1983 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* schema.js - Zod Schemas for MCP Tool Input/Output
|
|
3
|
+
* Vector Mirror v2.0 Phase 2
|
|
4
|
+
*
|
|
5
|
+
* Interface module: Defines all tool schemas (transport-agnostic)
|
|
6
|
+
* BAUPLAN ref: Sektion 5 (Tools) + Sektion 7.2 (SDK-Pattern) + Sektion 10.4 (structuredContent)
|
|
7
|
+
* DEPENDS: zod
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
|
|
11
|
+
// ── INPUT SCHEMAS ──────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export const analyzeInput = {
|
|
14
|
+
svg: z.string().describe('Vollstaendiger SVG-String'),
|
|
15
|
+
constraints: z
|
|
16
|
+
.array(z.string())
|
|
17
|
+
.optional()
|
|
18
|
+
.describe(
|
|
19
|
+
'Regeln im Format: "#subject TYPE #reference [value]". Typen: CENTERED-IN, NO-OVERLAP, INSIDE, ALIGNED-LEFT, ALIGNED-TOP, LEFT-OF, ABOVE, DISTANCE-FROM, SAME-SIZE, COLOR',
|
|
20
|
+
),
|
|
21
|
+
previousIssueCount: z
|
|
22
|
+
.number()
|
|
23
|
+
.int()
|
|
24
|
+
.nonnegative()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe(
|
|
27
|
+
'Anzahl Fehler des vorherigen Aufrufs (fuer Konvergenz-Tracking)',
|
|
28
|
+
),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const compareInput = {
|
|
32
|
+
svg: z.string().describe('Vollstaendiger SVG-String (neuer Zustand)'),
|
|
33
|
+
constraints: z
|
|
34
|
+
.array(z.string())
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Optionale Constraints fuer Re-Check'),
|
|
37
|
+
// §1.1 Stateless RPC: analysisId ist Pflichtfeld (Caller-Pflicht).
|
|
38
|
+
// §1.4 Disjunktion: akzeptiert jetzt UUID (grids) ODER Bookmark-Name
|
|
39
|
+
// (bookmarks). z.string().min(1) bleibt permissiv (KEIN ZodEffects →
|
|
40
|
+
// JSON-Schema-clean). Disambiguierung WRITE-seitig: bookmarkInput.name
|
|
41
|
+
// verbietet UUID-Form → Keyspaces disjunkt. §1.1-Invariante (kein
|
|
42
|
+
// .optional()) bleibt — Pflichtfeld.
|
|
43
|
+
analysisId: z
|
|
44
|
+
.string()
|
|
45
|
+
.min(1)
|
|
46
|
+
.describe(
|
|
47
|
+
'analysisId (UUID aus analyze) ODER Bookmark-Name (aus vector_mirror_bookmark). Server löst UUID→grids, Name→bookmarks auf. Pflichtfeld (§1.1).',
|
|
48
|
+
),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// §1.4 Globale Bookmarks (B-3): Input für vector_mirror_bookmark.
|
|
52
|
+
// name verbietet UUID-Form via Negative-Lookahead — garantiert Keyspace-
|
|
53
|
+
// Disjunktion zum grids-Keyspace (UUIDs). Empirie-belegt: ein UUID v4 mit
|
|
54
|
+
// Hex-Letter-Start (a-f) würde sonst BEIDE Muster treffen (Restambiguität);
|
|
55
|
+
// der Lookahead schließt das strukturell aus (alle 5 Hex-Gruppen 8-4-4-4-12).
|
|
56
|
+
export const bookmarkInput = {
|
|
57
|
+
name: z
|
|
58
|
+
.string()
|
|
59
|
+
.min(1)
|
|
60
|
+
.max(64)
|
|
61
|
+
.regex(
|
|
62
|
+
/^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)[A-Za-z][A-Za-z0-9_.-]{0,63}$/,
|
|
63
|
+
'Name: Buchstabe-Start, [A-Za-z0-9_.-], kein UUID-Format',
|
|
64
|
+
)
|
|
65
|
+
.describe('Bookmark-Name (kein UUID-Format; Keyspace disjunkt von grids).'),
|
|
66
|
+
analysisId: z
|
|
67
|
+
.string()
|
|
68
|
+
.uuid()
|
|
69
|
+
.describe('analysisId aus vector_mirror_analyze'),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const inspectInput = {
|
|
73
|
+
svg: z.string().describe('Vollstaendiger SVG-String'),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const paletteInput = {
|
|
77
|
+
svg: z.string().describe('Vollstaendiger SVG-String'),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const arrangeElementSchema = z.object({
|
|
81
|
+
id: z.string(),
|
|
82
|
+
tag: z.string(),
|
|
83
|
+
r: z.number().nonnegative().finite().optional(),
|
|
84
|
+
width: z.number().nonnegative().finite().optional(),
|
|
85
|
+
height: z.number().nonnegative().finite().optional(),
|
|
86
|
+
content: z.string().optional(),
|
|
87
|
+
x: z.number().finite().optional(),
|
|
88
|
+
y: z.number().finite().optional(),
|
|
89
|
+
transform: z.string().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export const arrangeInput = {
|
|
93
|
+
canvas: z
|
|
94
|
+
.object({
|
|
95
|
+
width: z.number().positive().finite(),
|
|
96
|
+
height: z.number().positive().finite(),
|
|
97
|
+
})
|
|
98
|
+
.describe('Canvas-Dimensionen'),
|
|
99
|
+
elements: z
|
|
100
|
+
.array(arrangeElementSchema)
|
|
101
|
+
.refine(
|
|
102
|
+
(elements) =>
|
|
103
|
+
new Set(elements.map((element) => element.id)).size === elements.length,
|
|
104
|
+
{
|
|
105
|
+
message: 'Element-IDs muessen eindeutig sein',
|
|
106
|
+
},
|
|
107
|
+
)
|
|
108
|
+
.describe('Elemente mit ID, Tag und optionalen Dimensionen'),
|
|
109
|
+
constraints: z
|
|
110
|
+
.array(z.string())
|
|
111
|
+
.describe(
|
|
112
|
+
'Constraints im Format "#subject TYPE #reference". Reihenfolge ist semantisch relevant.',
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const constraintsInput = {};
|
|
117
|
+
|
|
118
|
+
export const statusInput = {};
|
|
119
|
+
|
|
120
|
+
// §1.9 Eichkörper-Selftest: optionaler full-Flag (true → zusätzlich N=10-Mini-
|
|
121
|
+
// Determinismus-Check pro Lauf). Default false (Kalibrierung allein).
|
|
122
|
+
export const selftestInput = {
|
|
123
|
+
full: z
|
|
124
|
+
.boolean()
|
|
125
|
+
.optional()
|
|
126
|
+
.describe(
|
|
127
|
+
'true → zusätzlich N=10-Mini-Determinismus-Check (langsamer). Default: nur Kalibrierung.',
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// ── OUTPUT SCHEMAS (structuredContent) ────────────────────
|
|
132
|
+
|
|
133
|
+
// §6 RELAIS Fehler-Kanal (an internal spec §6, R9a #13/#14): additiver,
|
|
134
|
+
// optionaler Top-Level-Schlüssel `error: {code, hint}` — NUR präsent, wenn
|
|
135
|
+
// die Antwort isError:true trägt (gleiches additive-optional-Muster wie
|
|
136
|
+
// canvas_validity). code = Ursachen-Name (bestehendes Vokabular: Renderer-
|
|
137
|
+
// Error-Codes bzw. NO_BASELINE/ANALYSIS_NOT_FOUND/ARRANGE_FAILED aus der
|
|
138
|
+
// pipeline-Quelle); hint = der Navigations-Satz, wortidentisch in der Prosa
|
|
139
|
+
// (Parity-Pin: tests/relais_red). BEWUSST KEIN severity/level/weight-Feld
|
|
140
|
+
// (maintainer gate §7.2 Severity-Form). Plain zod, kein ZodEffects.
|
|
141
|
+
const errorEnvelopeSchema = z
|
|
142
|
+
.object({
|
|
143
|
+
code: z.string(),
|
|
144
|
+
hint: z.string(),
|
|
145
|
+
})
|
|
146
|
+
.optional();
|
|
147
|
+
|
|
148
|
+
// §1.5 Block E (R-E): fixSchema.attribute ist von z.string() auf z.enum gehärtet.
|
|
149
|
+
// Die Enum-Liste ist gegen die TATSÄCHLICHE buildFix/buildElementFixes-Wertemenge
|
|
150
|
+
// kalibriert (NICHT FIX_PLANs spekulative x2/y2):
|
|
151
|
+
// - whitelist-Pfad (deltaToAttribute map-Werte): cx,cy,r,rx,ry,x,y,x1,y1,width,height
|
|
152
|
+
// - Transform-Fallback (path/polygon/...): transform
|
|
153
|
+
// - tspan native relativer Shift (R-C): dx,dy (NUR für tspan valide)
|
|
154
|
+
// BEWUSST AUSGESCHLOSSEN: dw,dh (werden NIE als attribute emittiert — Größen-Fix
|
|
155
|
+
// auf Non-Whitelist-Tags wird zu reason='SIZE_FIX_UNSUPPORTED_FOR_TAG', C3/C5).
|
|
156
|
+
// Damit ist <path dw=...>/<path dh=...> schemamechanisch unmöglich (C5). dx/dy
|
|
157
|
+
// bleiben enum-zulässig (tspan), aber der Producer (structured.js) emittiert sie
|
|
158
|
+
// für path/polygon NIE — dort läuft alles über 'transform'.
|
|
159
|
+
const FIX_ATTRIBUTE_ENUM = [
|
|
160
|
+
'cx',
|
|
161
|
+
'cy',
|
|
162
|
+
'r',
|
|
163
|
+
'rx',
|
|
164
|
+
'ry',
|
|
165
|
+
'x',
|
|
166
|
+
'y',
|
|
167
|
+
'x1',
|
|
168
|
+
'y1',
|
|
169
|
+
'width',
|
|
170
|
+
'height',
|
|
171
|
+
'transform',
|
|
172
|
+
'dx',
|
|
173
|
+
'dy',
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const fixSchema = z.object({
|
|
177
|
+
attribute: z.enum(FIX_ATTRIBUTE_ENUM),
|
|
178
|
+
current: z.string(),
|
|
179
|
+
target: z.string(),
|
|
180
|
+
// §1.5: low-effort known-limitation-Hinweis am transform-Fix (praezision_2,
|
|
181
|
+
// CSS-Override). Optional — nur der Transform-Fallback setzt ihn.
|
|
182
|
+
warning: z.string().optional(),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const correctionSchema = z.object({
|
|
186
|
+
element: z.string(),
|
|
187
|
+
tag: z.string().optional(),
|
|
188
|
+
constraint: z.string(),
|
|
189
|
+
reference: z.string().nullable(),
|
|
190
|
+
dx: z.number().int().optional(),
|
|
191
|
+
dy: z.number().int().optional(),
|
|
192
|
+
dw: z.number().int().optional(),
|
|
193
|
+
dh: z.number().int().optional(),
|
|
194
|
+
fix: fixSchema.optional(),
|
|
195
|
+
fixes: z.array(fixSchema).optional(),
|
|
196
|
+
// §1.5 (praezision_3): Größen-Fix auf Tags ohne Size-Mapping (path/polygon/...)
|
|
197
|
+
// ist nicht via Attribut darstellbar → reason statt erfundenem scale(). Optional.
|
|
198
|
+
reason: z.enum(['SIZE_FIX_UNSUPPORTED_FOR_TAG']).optional(),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// §1.2b L-004 / R-β-2: elementSchema wird exportiert, damit test_schema.js
|
|
202
|
+
// den Schema-Vertrag direkt testen kann (statt nur indirekt via analyzeOutput).
|
|
203
|
+
// API-Erweiterung ist additiv — bestehende Konsumenten (analyzeOutput, inspectOutput,
|
|
204
|
+
// die z.array(elementSchema) bauen) bleiben unverändert.
|
|
205
|
+
export const elementSchema = z.object({
|
|
206
|
+
id: z.string(),
|
|
207
|
+
tag: z.string(),
|
|
208
|
+
cell: z.string(),
|
|
209
|
+
color: z.string(),
|
|
210
|
+
status: z.enum(['ok', 'fail', 'warn']),
|
|
211
|
+
// §1.2 3D-Detection Pre-Gate (FIX_PLAN §1.2 + ADR-026 §3):
|
|
212
|
+
// 'not_measurable' wenn ein Vorfahre matrix3d(...)/perspective(...)/preserve-3d trägt
|
|
213
|
+
// (Spotter darf darauf keine Pixel-Deltas berechnen). 'reliable' bei reiner 2D-Kette.
|
|
214
|
+
// §1.2b L-003 (Sprint-β1, Pflicht-Promotion): Datenkette grid.js (mappedElements)
|
|
215
|
+
// + structured.js (sceneElements) propagieren das Feld vollständig vom Renderer
|
|
216
|
+
// durch — REGEL-3 Spotter-Anti-Lüge ist jetzt auch auf MCP-Caller-Ebene erfüllt.
|
|
217
|
+
// bbox_reliability ist daher Pflicht; warnings bleibt .optional() (nur bei
|
|
218
|
+
// 3D-Treffer populiert).
|
|
219
|
+
// §1.4
|
|
220
|
+
// (ADR-026 §6 Reliability-Trichter + ADR-032 §Entscheidung). 'approximate'
|
|
221
|
+
// signalisiert: Element ist messbar, aber bekannte Naeherung (z.B. 2D-CSS-
|
|
222
|
+
// transform mit Float-Drift, dominant-baseline-Text mit Glyph-Ascent,
|
|
223
|
+
// opacity-Grauzone). Spotter MUSS approximate-Elemente wie reliable
|
|
224
|
+
// behandeln im Pass-Pfad, aber bei fail KEINE Pixel-Korrektur emittieren
|
|
225
|
+
// (superRefine + structured.js Gate adressieren das). Pessimismus-Prinzip:
|
|
226
|
+
// 'not_measurable' > 'approximate' > 'reliable'.
|
|
227
|
+
bbox_reliability: z.enum(['reliable', 'approximate', 'not_measurable']),
|
|
228
|
+
// §1.5 Block E (befund_3): Parent-Kontext für tspan/textPath. Der §1.5-tspan-Fix
|
|
229
|
+
// nutzt natives dx/dy RELATIV zur Eltern-<text>-Position (R-C) — der Caller braucht
|
|
230
|
+
// parent_id/parent_tag, um zu wissen, woran der tspan hängt. .optional(), weil nur
|
|
231
|
+
// verschachtelte Elemente (tspan, textPath) sie tragen; Top-Level-Elemente nicht.
|
|
232
|
+
parent_id: z.string().optional(),
|
|
233
|
+
parent_tag: z.string().optional(),
|
|
234
|
+
// §1.5 Block H / Patch P1 (F-2): Autor-transform-Attribut. Vom Renderer surfaced,
|
|
235
|
+
// damit der Transform-Fallback (buildTransformFix) den Autor-scale/rotate erhält.
|
|
236
|
+
// .optional() — nur Elemente mit transform-Attribut tragen es.
|
|
237
|
+
transform: z.string().optional(),
|
|
238
|
+
warnings: z.array(z.string()).optional(),
|
|
239
|
+
// §E4 Paint-Extent-Ehrlichkeit (F-AT-004, DoD-3): ein gefiltertes Element malt
|
|
240
|
+
// Tinte (Glow/Schatten/Blur) AUSSERHALB seiner geom-bbox. bbox_reliability bleibt
|
|
241
|
+
// 'reliable' (die Geometrie IST exakt) — die Ehrlichkeit trägt diese zwei Felder.
|
|
242
|
+
// has_paint_overflow: true, wenn die W3C-Filter-Region über die geom-bbox
|
|
243
|
+
// hinausragt (url(#id)→<filter>) ODER der Overflow existiert, aber unmessbar
|
|
244
|
+
// ist (CSS-Filter-Funktion ohne Region). .optional() — filterlose Elemente
|
|
245
|
+
// tragen es NICHT (Negativ-Kontrolle: kein Over-Flag).
|
|
246
|
+
has_paint_overflow: z.boolean().optional(),
|
|
247
|
+
// visual_bbox: die Filter-Region als AABB-Hülle im user-space ({x,y,w,h}) ODER
|
|
248
|
+
// das Literal 'not_measurable' (CSS-Filter-Funktion → spec-seitig keine
|
|
249
|
+
// Region). .nullable().optional() — KEIN Enum-Bruch (additiv). Union aus
|
|
250
|
+
// bbox-Form und String-Literal.
|
|
251
|
+
visual_bbox: z
|
|
252
|
+
.union([
|
|
253
|
+
z.object({
|
|
254
|
+
x: z.number().finite(),
|
|
255
|
+
y: z.number().finite(),
|
|
256
|
+
w: z.number().finite(),
|
|
257
|
+
h: z.number().finite(),
|
|
258
|
+
}),
|
|
259
|
+
z.literal('not_measurable'),
|
|
260
|
+
])
|
|
261
|
+
.nullable()
|
|
262
|
+
.optional(),
|
|
263
|
+
// §HEAL-R6 / T1 Paint-Presence (F-AT-6-01, CRIT, DoD-2): der Sichtbarkeits-Walk
|
|
264
|
+
// prüfte fill-opacity/stroke-opacity NICHT — ein fill-opacity:0-Element (0 Pixel)
|
|
265
|
+
// wurde als reliable+sichtbar emittiert (die Lüge). Drei additive Felder tragen
|
|
266
|
+
// jetzt die Tinten-Wahrheit (additiv → bestehende Konsumenten unverändert):
|
|
267
|
+
// fill_paint_factor / stroke_paint_factor: die effektiven, permanenz-bewussten
|
|
268
|
+
// Kanal-Alpha-Faktoren (composedOpacity-frei; Element-opacity steckt im
|
|
269
|
+
// opacity-Feld). Diagnostik für den Konsumenten.
|
|
270
|
+
// paint_visible: 3-wertiger Tinten-Vertrag (§HEAL-R6 Variante 1, F-AT-6-07).
|
|
271
|
+
// false — KEIN Kanal malt ODER ein RÄUMLICHER Operator löscht die
|
|
272
|
+
// Tinte beweisbar (CTM-det=0 / Vorfahr-Viewport-Clip-leer).
|
|
273
|
+
// 'indeterminate'— ein nicht-aufzählbarer räumlicher Operator (clip-path/mask/
|
|
274
|
+
// pattern/filter, oder nicht-endliche CTM) ist present, aber
|
|
275
|
+
// die Tinten-Präsenz ist raster-frei NICHT entscheidbar →
|
|
276
|
+
// ehrlich unbestimmt statt falsch-reliable (Blind-Trust). Trägt
|
|
277
|
+
// die Warning PAINT_PRESENCE_INDETERMINATE.
|
|
278
|
+
// absent — normal sichtbar (Negativ-Kontrolle: Feld fehlt).
|
|
279
|
+
// bbox_reliability bleibt 'reliable' (die Geometrie IST exakt; NUR die Tinten-
|
|
280
|
+
// Behauptung wird graduiert). Die SDK registriert diese als MCP-outputSchema
|
|
281
|
+
// (tools.js) — ohne die Union verwirft sie 'indeterminate' zur LAUFZEIT (KRIT).
|
|
282
|
+
fill_paint_factor: z.number().optional(),
|
|
283
|
+
stroke_paint_factor: z.number().optional(),
|
|
284
|
+
paint_visible: z
|
|
285
|
+
.union([z.literal(false), z.literal('indeterminate')])
|
|
286
|
+
.optional(),
|
|
287
|
+
// §D5 / R6-STATE Zustands-Abhängigkeit (state_dependent): reines Flag — ein
|
|
288
|
+
// interaktiver Pseudo-Selektor (:hover/:focus/:focus-within/:focus-visible/
|
|
289
|
+
// :active/:target) self-oder-Vorfahr ODER ein SMIL <set/animate begin|end mit
|
|
290
|
+
// Event-Token zielt auf das Element. Die t=0-Geometrie bleibt EXAKT wahr (KEIN
|
|
291
|
+
// bbox_reliability-Degrade) — Alt-Zustände sind ZUSÄTZLICHE Wahrheiten. Plain
|
|
292
|
+
// zod (KEIN ZodEffects) → automatisch in beiden registrierten outputSchemas,
|
|
293
|
+
// kein Laufzeit-Reject. true-only (statische Elemente tragen das Feld NICHT).
|
|
294
|
+
state_dependent: z.boolean().optional(),
|
|
295
|
+
// §F-AT-6-09 / R6-MEDIA + §HEAL-4 Viewport-Abhängigkeit (media_dependent):
|
|
296
|
+
// reines true-only-Flag — das Element ist VIEWPORT-DIVERGENT, erkannt über
|
|
297
|
+
// EINE von ZWEI ODER-verknüpften Quellen (Semantik-Weitung Heal 4, additiv):
|
|
298
|
+
// (a) STATISCH: ein Selektor INNERHALB einer @media-(Conditional-Group-)
|
|
299
|
+
// Regel trifft das Element UND deren Bedingung nennt ein VIEWPORT-
|
|
300
|
+
// Feature (width/height/orientation/aspect-ratio/resolution/device-*),
|
|
301
|
+
// ODER element-lokale vw/vh/vmin/vmax-Geometrie (authored-Scan, I1).
|
|
302
|
+
// Rein statisch @t=0, KEIN matches-Gate — beide Divergenz-Richtungen.
|
|
303
|
+
// (b) GEMESSEN (§HEAL-4, an internal spec): der lean
|
|
304
|
+
// 2-Viewport-Mess-Diff ([1920,400] ∪ px-Breakpoint-Straddles) in
|
|
305
|
+
// analyze()/inspect() belegt reale Divergenz auf einer der 3 Achsen
|
|
306
|
+
// (Geometrie in root-user-units / computed paint / Paint-Server-
|
|
307
|
+
// Closure) — fängt auch @container, %-in-nested-svg, <style>-Regel-vw,
|
|
308
|
+
// transform-vw und font-size-vw, die (a) nicht sieht.
|
|
309
|
+
// Die Verknüpfung ist STRIKT ADDITIV (OR-only): Messung fügt nur hinzu,
|
|
310
|
+
// nimmt nie ein statisches true weg (F-AT-7-14 use-Shadow-Blindfleck —
|
|
311
|
+
// die Statik bleibt load-bearing). Mess-Ausfall ist LAUT (scene-level
|
|
312
|
+
// MEDIA_MEASURE_UNAVAILABLE im Prosa-Kanal), nie still.
|
|
313
|
+
// Die t=0-Geometrie bleibt EXAKT wahr (KEIN bbox_reliability-Degrade).
|
|
314
|
+
// Orthogonal zu state_dependent/Motion (drei stille Achsen). Plain zod
|
|
315
|
+
// (KEIN ZodEffects) → kein Laufzeit-Reject. true-only.
|
|
316
|
+
media_dependent: z.boolean().optional(),
|
|
317
|
+
// §HEAL-5 / F-AT-2-005 Zeit-Achse (motion_dependent): reines true-only-Flag —
|
|
318
|
+
// die Subjekt-Geometrie ist ZEIT-VARIANT (clock-rooted SMIL-GEOMETRIE:
|
|
319
|
+
// animate/set auf einem Geometrie-Attribut · animateTransform · animateMotion,
|
|
320
|
+
// deren begin-ATTRIBUT GANZ FEHLT (Blink-Default 0s) ODER ≥1 validen
|
|
321
|
+
// Offset-/Clock-Token trägt; leere/malformed begin-Werte laufen in Blink NIE
|
|
322
|
+
// — Boden-Wahrheit mgr/malformed_begin_gt.mjs + empty_begin_gt.mjs, Mikro-
|
|
323
|
+
// Patch R1). Die t0-Messung bleibt EXAKT — an anderem t anders
|
|
324
|
+
// (KEIN bbox_reliability-Degrade; T3a/Heal-4-Präzedenz: Alt-Zeitpunkte sind
|
|
325
|
+
// ZUSÄTZLICHE Wahrheiten). Vierte stille Achse, orthogonal zu state_dependent/
|
|
326
|
+
// media_dependent/paint_visible. z.literal(true) kodiert true-only AM VERTRAG
|
|
327
|
+
// (Spec-Tabelle Edit #1; Emission ist ohnehin true-only-Spread). Plain zod
|
|
328
|
+
// (KEIN ZodEffects) → automatisch in beiden registrierten outputSchemas, kein
|
|
329
|
+
// Laufzeit-Reject. EDIT #1 ZUERST (R6-Präzedenz, empirisch erzwungen: zod
|
|
330
|
+
// unknownKeys='strip' strippt unbekannte Felder STILL — Witness S, 4c8a6ed).
|
|
331
|
+
motion_dependent: z.literal(true).optional(),
|
|
332
|
+
// §H10 R11-06 Paint-Zeit-Achse (paint_time_variant): reines true-only-Flag —
|
|
333
|
+
// eine PAINT-/Darstellungs-Eigenschaft des Elements ist zeit-variant
|
|
334
|
+
// (clock-rooted SMIL auf NICHT-Geometrie-Kanal: fill/stroke/opacity/… bzw.
|
|
335
|
+
// animateColor). Die t0-Messung (Farbe/Opacity @t0) bleibt EXAKT — an
|
|
336
|
+
// anderem t anders. Fünfte Achse, orthogonal zu motion_dependent
|
|
337
|
+
// (Geometrie-Zeit ≠ Paint-Zeit). Selbes true-only-Literal-Muster.
|
|
338
|
+
paint_time_variant: z.literal(true).optional(),
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const diffEntrySchema = z.object({
|
|
342
|
+
type: z.string(),
|
|
343
|
+
id: z.string(),
|
|
344
|
+
from: z.string().optional(),
|
|
345
|
+
to: z.string().optional(),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const iterationSchema = z.object({
|
|
349
|
+
sequence: z.number().int(),
|
|
350
|
+
previous_issues: z.number().int(),
|
|
351
|
+
current_issues: z.number().int(),
|
|
352
|
+
total_issues: z.number().int(),
|
|
353
|
+
returned_issues: z.number().int(),
|
|
354
|
+
suppressed: z.number().int(),
|
|
355
|
+
// §H9 K-12: BASELINE = erste Messung mit Issues (sequence=1, keine
|
|
356
|
+
// Historie) — die Trend-Wörter STAGNATING/DIVERGING sind echten
|
|
357
|
+
// Vergleichen (previousIssueCount vorhanden) vorbehalten. Additiv.
|
|
358
|
+
// §H9 P2: .nullable() — die Error-Hülle (analyzeErrorStructured) trägt
|
|
359
|
+
// convergence:null („keine Aussage": im Fehlerfall gibt es keine Messung,
|
|
360
|
+
// das frühere 'SOLVED' widersprach isError:true). Erfolgs-Pfade emittieren
|
|
361
|
+
// weiterhin IMMER einen Enum-Wert (computeConvergence). Additiv-nullable,
|
|
362
|
+
// kein ZodEffects (Muster: breakerStatsSchema/calibrationSchema).
|
|
363
|
+
convergence: z
|
|
364
|
+
.enum(['IMPROVING', 'STAGNATING', 'DIVERGING', 'SOLVED', 'BASELINE'])
|
|
365
|
+
.nullable(),
|
|
366
|
+
// §1.3 Schicht 2: Server-Garantie — auf JEDEM Erfolgs-Pfad emittiert
|
|
367
|
+
// (UUID v4 via crypto.randomUUID). §H9 P2 (Wahrheits-Rekalibrierung der
|
|
368
|
+
// Garantie): die Error-Hülle trägt analysisId:null statt einer ERFUNDENEN
|
|
369
|
+
// frischen UUID, die kein Grid referenziert — das Feld bleibt Pflicht
|
|
370
|
+
// (nullable ≠ optional), null ist das ehrliche „keine Analyse gespeichert".
|
|
371
|
+
analysisId: z.string().uuid().nullable(),
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const uncheckedEntrySchema = z.object({
|
|
375
|
+
element: z.string().nullable(),
|
|
376
|
+
constraint: z.string(),
|
|
377
|
+
reasonCategory: z.string(),
|
|
378
|
+
reasonCode: z.string(),
|
|
379
|
+
hint: z.string(),
|
|
380
|
+
suggestedCorrection: z.string().optional(),
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// §1.4
|
|
384
|
+
// Kanal fuer Warnings auf Elementen, die das slice(0,7)-Cap aus BAUPLAN
|
|
385
|
+
// OBL-008 verbergen wuerde. structured.js (Edit F) befuellt das, wenn
|
|
386
|
+
// elements.length > 7 UND warning-tragende Elemente auf Position >7 stehen.
|
|
387
|
+
// Caller sieht damit das Reliability-Signal auch jenseits des Scene-Cap
|
|
388
|
+
// (REGEL-3 Spotter-Anti-Luege ueberstimmt BAUPLAN-Token-Limit).
|
|
389
|
+
const truncatedWarningEntrySchema = z.object({
|
|
390
|
+
element_id: z.string(),
|
|
391
|
+
warnings: z.array(z.string()),
|
|
392
|
+
position: z.number().int().nonnegative(),
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// §1.4
|
|
396
|
+
// Schluessel im Output. Aktuell traegt er nur truncated_warnings; weitere
|
|
397
|
+
// Hoist-Kanaele (z.B. truncated_corrections) koennten hier additiv ergaenzt
|
|
398
|
+
// werden ohne Schema-Breaking-Change.
|
|
399
|
+
const metaSchema = z
|
|
400
|
+
.object({
|
|
401
|
+
truncated_warnings: z.array(truncatedWarningEntrySchema).optional(),
|
|
402
|
+
})
|
|
403
|
+
.optional();
|
|
404
|
+
|
|
405
|
+
// §H10 R11-01: EINE Schema-Quelle für das Existenz-Register (analyze + inspect).
|
|
406
|
+
// Plain zod, optional-by-default (Feld fehlt, wenn nichts geskippt wurde).
|
|
407
|
+
const hiddenElementsSchema = z
|
|
408
|
+
.array(
|
|
409
|
+
z.object({
|
|
410
|
+
id: z.string().nullable(),
|
|
411
|
+
tag: z.string(),
|
|
412
|
+
axis: z.enum(['display:none', 'visibility:hidden', 'opacity:0']),
|
|
413
|
+
}),
|
|
414
|
+
)
|
|
415
|
+
.optional();
|
|
416
|
+
|
|
417
|
+
export const analyzeOutput = {
|
|
418
|
+
status: z.enum(['PASS', 'FAIL', 'PARTIAL']),
|
|
419
|
+
iteration: iterationSchema,
|
|
420
|
+
scene: z.object({
|
|
421
|
+
width: z.number(),
|
|
422
|
+
height: z.number(),
|
|
423
|
+
grid: z.string(),
|
|
424
|
+
elements: z.array(elementSchema),
|
|
425
|
+
// §HEAL-R6 / T1 "das Kabel" (F-AT-6-08): Canvas-Validität aus dem
|
|
426
|
+
// honesty.js#classifyCanvas-Verdikt. 'lossy' ⇒ DOMPurify hat Semantik
|
|
427
|
+
// entfernt (resolved.sanitize_loss non-empty) → referenzierende Messung
|
|
428
|
+
// kann von der Quelle abweichen. OPTIONAL (kein required → keine
|
|
429
|
+
// Fixture-Brüche); die Garantie sichern Tests, nicht required. Plain zod
|
|
430
|
+
// (KEIN ZodEffects) → automatisch in den registrierten outputSchemas, kein
|
|
431
|
+
// Laufzeit-Reject. Selbes additives Muster wie inspectOutput.scene.suppressed.
|
|
432
|
+
canvas_validity: z
|
|
433
|
+
.enum(['valid', 'default_replaced', 'degenerate', 'lossy'])
|
|
434
|
+
.optional(),
|
|
435
|
+
// §H10 R11-01: Existenz-Register — css-unsichtbar geskippte Elemente
|
|
436
|
+
// (id+Achse), NICHT Teil von scene.elements (Emissions-Menge byte-stabil).
|
|
437
|
+
// Optional-by-default (Muster canvas_validity); id null bei Auto-id-losen.
|
|
438
|
+
hidden_elements: hiddenElementsSchema,
|
|
439
|
+
// §HEAL-7/C (Codex MCP-Wahrheitsgrenze): scene-level Mess-Ausfall-Marker
|
|
440
|
+
// (§HEAL-4 MK3, pipeline.js#withMeasureUnavailable). OHNE Deklaration
|
|
441
|
+
// strippte der zod-Boundary-Parse (SDK mcp.js: safeParseAsync gegen
|
|
442
|
+
// z.object(outputSchema), unknownKeys='strip') das Feld STILL und der
|
|
443
|
+
// tools/list-JSON-Schema-Dump (additionalProperties:false) verwarf es
|
|
444
|
+
// ajv-seitig (-32602-Klasse) — der laute Ausfall war an der MCP-Grenze
|
|
445
|
+
// unsichtbar (nur der Prosa-Kanal trug ihn). z.literal: genau EIN Wert;
|
|
446
|
+
// optional (Normalfall: Feld absent). Plain zod, kein ZodEffects.
|
|
447
|
+
media_measure: z.literal('MEDIA_MEASURE_UNAVAILABLE').optional(),
|
|
448
|
+
}),
|
|
449
|
+
corrections: z.array(correctionSchema),
|
|
450
|
+
unchecked: z.array(uncheckedEntrySchema),
|
|
451
|
+
diff: z.array(diffEntrySchema),
|
|
452
|
+
meta: metaSchema,
|
|
453
|
+
// §6 RELAIS: isError-Pfade von analyze/compare (Render-Fehler ohne Verlust,
|
|
454
|
+
// compare-No-Baseline) tragen error{code,hint} — sonst absent.
|
|
455
|
+
error: errorEnvelopeSchema,
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
export const inspectOutput = {
|
|
459
|
+
scene: z.object({
|
|
460
|
+
width: z.number(),
|
|
461
|
+
height: z.number(),
|
|
462
|
+
grid: z.string(),
|
|
463
|
+
elements: z.array(elementSchema),
|
|
464
|
+
// §E1 D-008 (F-TF-008): ehrlicher ELEMENT-Trunkierungs-Zaehler, required
|
|
465
|
+
// (wie analyze iterationSchema.suppressed). inspect hat KEINEN iteration-
|
|
466
|
+
// Block, daher gehoert der Zaehler in scene (er beschreibt scene.elements,
|
|
467
|
+
// die bei >SCENE_MAX_ELEMENTS via slice(0,7) gekuerzt werden). ≤7 → 0.
|
|
468
|
+
suppressed: z.number().int(),
|
|
469
|
+
// §HEAL-R6 / T1 "das Kabel" (F-AT-6-08): Canvas-Validität (classifyCanvas).
|
|
470
|
+
// Selbes additive optional-Muster wie analyzeOutput.scene.canvas_validity —
|
|
471
|
+
// inspect speist es ebenfalls aus resolved.sanitize_loss (Anti-LECK-3).
|
|
472
|
+
canvas_validity: z
|
|
473
|
+
.enum(['valid', 'default_replaced', 'degenerate', 'lossy'])
|
|
474
|
+
.optional(),
|
|
475
|
+
// §H10 R11-01: Existenz-Register auch im inspect-Pfad (Kanal-Parität).
|
|
476
|
+
hidden_elements: hiddenElementsSchema,
|
|
477
|
+
// §HEAL-7/C: Mess-Ausfall-Marker auch im inspect-Pfad (withMeasureUnavailable
|
|
478
|
+
// läuft in analyze() UND inspect()) — selbes Muster wie analyzeOutput.scene.
|
|
479
|
+
media_measure: z.literal('MEDIA_MEASURE_UNAVAILABLE').optional(),
|
|
480
|
+
}),
|
|
481
|
+
meta: metaSchema,
|
|
482
|
+
// §6 RELAIS: isError-Pfad von inspect (Render-Fehler ohne Verlust).
|
|
483
|
+
error: errorEnvelopeSchema,
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// §1.4
|
|
487
|
+
// Validator-Variante des analyzeOutput-Shapes mit REGEL-3-Postcondition:
|
|
488
|
+
// Wenn corrections[i].element === scene.elements[j].id (modulo '#'-Praefix)
|
|
489
|
+
// UND scene.elements[j].bbox_reliability ∈ {'not_measurable','approximate'}
|
|
490
|
+
// → corrections[i] DARF KEINE dx/dy enthalten.
|
|
491
|
+
// Wird NICHT als outputSchema im MCP-Server-Pfad verwendet (MCP-SDK's
|
|
492
|
+
// normalizeObjectSchema kann mit ZodEffects nicht umgehen), sondern dient
|
|
493
|
+
// als Drift-Verhinderungs-Gate fuer test_regel3_invariants.js (Edit H) und
|
|
494
|
+
// als Compound-Schema fuer Property-Based-Tests. Der tatsaechliche Defekt-
|
|
495
|
+
// Fix fuer β-002 sitzt in structured.js (Edit F) — dort wird das dx/dy
|
|
496
|
+
// proaktiv unterdrueckt, BEVOR es das Schema erreichen kann. Das hier ist
|
|
497
|
+
// der Mechanismus, der diese Unterdrueckung mechanisch UEBERPRUEFT.
|
|
498
|
+
export const analyzeOutputCompound = z
|
|
499
|
+
.object(analyzeOutput)
|
|
500
|
+
.superRefine((data, ctx) => {
|
|
501
|
+
if (!data.scene || !Array.isArray(data.scene.elements)) return;
|
|
502
|
+
if (!Array.isArray(data.corrections)) return;
|
|
503
|
+
const reliabilityById = new Map();
|
|
504
|
+
for (const el of data.scene.elements) {
|
|
505
|
+
reliabilityById.set(el.id, el.bbox_reliability);
|
|
506
|
+
}
|
|
507
|
+
for (let i = 0; i < data.corrections.length; i++) {
|
|
508
|
+
const c = data.corrections[i];
|
|
509
|
+
if (!c || typeof c.element !== 'string') continue;
|
|
510
|
+
const id = c.element.replace(/^#/, '');
|
|
511
|
+
const reliability = reliabilityById.get(id);
|
|
512
|
+
// Element-Lookup-Fehlschlag (z.B. correction.element zeigt auf nicht-
|
|
513
|
+
// gelistete Scene-Element-ID wegen slice(0,7)-Cap): das ist ein
|
|
514
|
+
// separater REGEL-3-Bruch (β-003), wird in structured.js Edit F
|
|
515
|
+
// angegangen. Hier kein addIssue — sonst doppelte Meldung.
|
|
516
|
+
if (reliability === undefined) continue;
|
|
517
|
+
if (reliability === 'not_measurable' || reliability === 'approximate') {
|
|
518
|
+
const hasDx = c.dx !== undefined;
|
|
519
|
+
const hasDy = c.dy !== undefined;
|
|
520
|
+
if (hasDx || hasDy) {
|
|
521
|
+
ctx.addIssue({
|
|
522
|
+
code: z.ZodIssueCode.custom,
|
|
523
|
+
path: ['corrections', i],
|
|
524
|
+
message:
|
|
525
|
+
`REGEL-3: corrections[${i}].element='${c.element}' verweist auf scene.elements mit ` +
|
|
526
|
+
`bbox_reliability='${reliability}' — dx/dy duerfen nicht emittiert werden (Spotter-Anti-Luege).`,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
export const paletteOutput = {
|
|
534
|
+
colors: z.array(
|
|
535
|
+
z.object({
|
|
536
|
+
id: z.string(),
|
|
537
|
+
fill: z.string(),
|
|
538
|
+
stroke: z.string().nullable(),
|
|
539
|
+
}),
|
|
540
|
+
),
|
|
541
|
+
// §6 RELAIS: isError-Pfad von palette (Render-Fehler).
|
|
542
|
+
error: errorEnvelopeSchema,
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
const constraintTypeSchema = z.object({
|
|
546
|
+
type: z.string(),
|
|
547
|
+
syntax: z.string(),
|
|
548
|
+
hasArrange: z.boolean(),
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
export const constraintsOutput = {
|
|
552
|
+
types: z.array(constraintTypeSchema),
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
export const arrangeOutput = {
|
|
556
|
+
attributes: z.record(
|
|
557
|
+
z.string(),
|
|
558
|
+
z.record(z.string(), z.union([z.number(), z.string()])),
|
|
559
|
+
),
|
|
560
|
+
warnings: z.array(z.string()),
|
|
561
|
+
// §6 RELAIS: isError-Pfad von arrange (Handler-Catch, ARRANGE_FAILED).
|
|
562
|
+
error: errorEnvelopeSchema,
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
// §1.4 Globale Bookmarks (B-3): Output für vector_mirror_bookmark.
|
|
566
|
+
// analysisId bleibt UUID (Quell-ID; KORR-2 — kein Name im Output).
|
|
567
|
+
export const bookmarkOutput = {
|
|
568
|
+
name: z.string(),
|
|
569
|
+
analysisId: z.string().uuid(),
|
|
570
|
+
stored: z.boolean(),
|
|
571
|
+
bookmarkCount: z.number().int().nonnegative(),
|
|
572
|
+
// §6 RELAIS: isError-Pfad von bookmark (unbekannte/verdrängte analysisId).
|
|
573
|
+
error: errorEnvelopeSchema,
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
const breakerStatsSchema = z
|
|
577
|
+
.object({
|
|
578
|
+
name: z.string(),
|
|
579
|
+
state: z.enum(['closed', 'open', 'half-open']),
|
|
580
|
+
fires: z.number().int(),
|
|
581
|
+
successes: z.number().int(),
|
|
582
|
+
failures: z.number().int(),
|
|
583
|
+
timeouts: z.number().int(),
|
|
584
|
+
rejects: z.number().int(),
|
|
585
|
+
fallbacks: z.number().int(),
|
|
586
|
+
semaphoreRejections: z.number().int(),
|
|
587
|
+
latencyMean: z.number(),
|
|
588
|
+
})
|
|
589
|
+
.nullable();
|
|
590
|
+
|
|
591
|
+
// §1.9 Eichkörper-Selftest: Kalibrierungs-Stand im status-Output. nullable +
|
|
592
|
+
// optional → kein Bruch bestehender E2E-Roundtrip-Asserts (Feld fehlt/null bis
|
|
593
|
+
// der erste Selftest lief). PENDING = Auto-Selftest läuft noch (fire-and-forget
|
|
594
|
+
// nach connect). MCP-SDK-tauglich (kein ZodEffects).
|
|
595
|
+
const calibrationSchema = z
|
|
596
|
+
.object({
|
|
597
|
+
status: z.enum(['PASS', 'FAIL', 'PENDING']),
|
|
598
|
+
calibrated: z.number().int(),
|
|
599
|
+
total: z.number().int(),
|
|
600
|
+
timestamp: z.string(),
|
|
601
|
+
})
|
|
602
|
+
.nullable()
|
|
603
|
+
.optional();
|
|
604
|
+
|
|
605
|
+
export const statusOutput = {
|
|
606
|
+
version: z.string(),
|
|
607
|
+
browser: z.enum(['running', 'stopped']),
|
|
608
|
+
lastAnalysis: z.boolean(),
|
|
609
|
+
constraintTypes: z.number().int(),
|
|
610
|
+
breaker: breakerStatsSchema,
|
|
611
|
+
calibration: calibrationSchema,
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
// §1.9 Eichkörper-Selftest: Output-Schema für vector_mirror_selftest. Trägt das
|
|
615
|
+
// Kalibrierungs-Verdikt + die Abweichungs-Liste (anti-zirk Spec-Mismatches).
|
|
616
|
+
export const selftestOutput = {
|
|
617
|
+
status: z.enum(['PASS', 'FAIL']),
|
|
618
|
+
calibrated: z.number().int(),
|
|
619
|
+
total: z.number().int(),
|
|
620
|
+
failures: z.array(
|
|
621
|
+
z.object({
|
|
622
|
+
ek: z.string(),
|
|
623
|
+
reason: z.string(),
|
|
624
|
+
}),
|
|
625
|
+
),
|
|
626
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* server.js - MCP Server Entry Point (stdio)
|
|
4
|
+
* Vector Mirror v2.0 Phase 2
|
|
5
|
+
*
|
|
6
|
+
* Interface module: ONLY transport setup. No business logic.
|
|
7
|
+
* BAUPLAN ref: Sektion 7.2 (SDK-Pattern), 7.3 (Architecture Separation), 7.4 (Config)
|
|
8
|
+
* DEPENDS: @modelcontextprotocol/sdk, interface/tools.js, pipeline.js
|
|
9
|
+
*/
|
|
10
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
12
|
+
import { markCalibrationPending, runSelftest, shutdown } from '../pipeline.js';
|
|
13
|
+
import { QUICKSTART } from './claims.js';
|
|
14
|
+
import { tools } from './tools.js';
|
|
15
|
+
|
|
16
|
+
// §RELAIS §4: instructions-Quickstart als additives 2. Konstruktor-Argument
|
|
17
|
+
// (ServerOptions.instructions, SDK-verifiziert). Inhalt ist eine PROJEKTION
|
|
18
|
+
// des Claims-Registers (claims.js — eine Quelle); Auslieferung an den
|
|
19
|
+
// Konsumenten ist client-abhängig (P5-Ehrlichkeit) — die Tool-Descriptions
|
|
20
|
+
// bleiben selbsttragend, der Quickstart ist Beschleuniger, nicht Voraussetzung.
|
|
21
|
+
const server = new McpServer(
|
|
22
|
+
{
|
|
23
|
+
name: 'vector-mirror',
|
|
24
|
+
version: '1.0.0',
|
|
25
|
+
},
|
|
26
|
+
{ instructions: QUICKSTART },
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// Register all tools from tools.js (BAUPLAN 7.2: registerTool, NOT deprecated tool())
|
|
30
|
+
for (const t of tools) {
|
|
31
|
+
server.registerTool(t.name, t.config, t.handler);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Graceful shutdown: close browser on SIGINT/SIGTERM
|
|
35
|
+
process.on('SIGINT', async () => {
|
|
36
|
+
await shutdown();
|
|
37
|
+
process.exit(0);
|
|
38
|
+
});
|
|
39
|
+
process.on('SIGTERM', async () => {
|
|
40
|
+
await shutdown();
|
|
41
|
+
process.exit(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Connect transport and start serving (BAUPLAN 7.2: StdioServerTransport)
|
|
45
|
+
const transport = new StdioServerTransport();
|
|
46
|
+
await server.connect(transport);
|
|
47
|
+
|
|
48
|
+
// §1.9 Auto-Selftest: FIRE-AND-FORGET nach connect (R-1, KEIN eager-blocking
|
|
49
|
+
// init). Der Server bleibt sofort verfügbar; markCalibrationPending() setzt
|
|
50
|
+
// status.calibration='PENDING' BIS der Selftest fertig ist (dann PASS/FAIL).
|
|
51
|
+
// Selftest-Fehler sind NICHT fatal für den Start (z.B. fehlendes Chromium) —
|
|
52
|
+
// der Server läuft lazy weiter, status zeigt den Stand. Kein await: connect
|
|
53
|
+
// ist nicht geblockt, Startup-Latenz bleibt minimal.
|
|
54
|
+
markCalibrationPending();
|
|
55
|
+
runSelftest(false).catch(() => {
|
|
56
|
+
/* Selftest-Fehler nicht fatal: status.calibration bleibt PENDING/letzter Stand. */
|
|
57
|
+
});
|