acf-mcp 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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +87 -0
  3. package/content/fiches/.gitkeep +0 -0
  4. package/content/fiches/ACF-00.en.md +59 -0
  5. package/content/fiches/ACF-00.fr.md +59 -0
  6. package/content/fiches/ACF-01.en.md +32 -0
  7. package/content/fiches/ACF-01.fr.md +32 -0
  8. package/content/fiches/ACF-02.en.md +32 -0
  9. package/content/fiches/ACF-02.fr.md +32 -0
  10. package/content/fiches/ACF-03.en.md +78 -0
  11. package/content/fiches/ACF-03.fr.md +78 -0
  12. package/content/fiches/ACF-04.en.md +32 -0
  13. package/content/fiches/ACF-04.fr.md +32 -0
  14. package/content/fiches/ACF-05.en.md +32 -0
  15. package/content/fiches/ACF-05.fr.md +32 -0
  16. package/content/fiches/ACF-06.en.md +32 -0
  17. package/content/fiches/ACF-06.fr.md +32 -0
  18. package/content/fiches/ACF-07.en.md +32 -0
  19. package/content/fiches/ACF-07.fr.md +32 -0
  20. package/content/fiches/ACF-08.en.md +32 -0
  21. package/content/fiches/ACF-08.fr.md +32 -0
  22. package/content/fiches/ACF-09.en.md +32 -0
  23. package/content/fiches/ACF-09.fr.md +32 -0
  24. package/content/fiches/ACF-10.en.md +32 -0
  25. package/content/fiches/ACF-10.fr.md +32 -0
  26. package/content/fiches/ACF-11.en.md +32 -0
  27. package/content/fiches/ACF-11.fr.md +32 -0
  28. package/content/fiches/ACF-12.en.md +32 -0
  29. package/content/fiches/ACF-12.fr.md +32 -0
  30. package/content/fiches/ACF-13.en.md +32 -0
  31. package/content/fiches/ACF-13.fr.md +32 -0
  32. package/content/fiches/ACF-14.en.md +32 -0
  33. package/content/fiches/ACF-14.fr.md +32 -0
  34. package/content/fiches/ACF-15.en.md +32 -0
  35. package/content/fiches/ACF-15.fr.md +32 -0
  36. package/content/fiches/ACF-16.en.md +32 -0
  37. package/content/fiches/ACF-16.fr.md +32 -0
  38. package/content/framework/.gitkeep +0 -0
  39. package/content/framework/autonomy-levels.json +73 -0
  40. package/content/framework/ddao.json +16 -0
  41. package/content/framework/dimensions.json +83 -0
  42. package/content/framework/principles.json +77 -0
  43. package/content/glossary/.gitkeep +0 -0
  44. package/content/glossary/en.json +96 -0
  45. package/content/glossary/fr.json +96 -0
  46. package/content/guides/.gitkeep +0 -0
  47. package/content/manual/.gitkeep +0 -0
  48. package/content/meta.json +23 -0
  49. package/content/rules/.gitkeep +0 -0
  50. package/content/rules/ai-act-annex-i.json +54 -0
  51. package/content/rules/ai-act-annex-iii.json +165 -0
  52. package/content/rules/ai-act-roles.json +52 -0
  53. package/content/rules/autonomy-inference.json +38 -0
  54. package/content/rules/criticality-matrix.json +83 -0
  55. package/content/rules/ddao-controls-mapping.json +148 -0
  56. package/content/rules/gdpr-qualification.json +58 -0
  57. package/content/rules/gpai-triggers.json +41 -0
  58. package/content/rules/rules-meta.json +5 -0
  59. package/content/rules/sign-off-matrix.json +84 -0
  60. package/content/whitepaper/.gitkeep +0 -0
  61. package/dist/index.d.ts +30 -0
  62. package/dist/index.js +2898 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/lib/rate-limit.d.ts +24 -0
  65. package/dist/lib/rate-limit.js +89 -0
  66. package/dist/lib/rate-limit.js.map +1 -0
  67. package/dist/search-index.json +1 -0
  68. package/dist/server-DxztThU5.d.ts +931 -0
  69. package/dist/transport/http.d.ts +27 -0
  70. package/dist/transport/http.js +2908 -0
  71. package/dist/transport/http.js.map +1 -0
  72. package/dist/transport/stdio.d.ts +1 -0
  73. package/dist/transport/stdio.js +2907 -0
  74. package/dist/transport/stdio.js.map +1 -0
  75. package/package.json +81 -0
@@ -0,0 +1,2907 @@
1
+ #!/usr/bin/env node
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
+ import { readFile } from 'fs/promises';
7
+ import { existsSync } from 'fs';
8
+ import matter from 'gray-matter';
9
+ import { z } from 'zod';
10
+ import lunr from 'lunr';
11
+ import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
12
+
13
+ var SUPPORTED_LOCALES = [
14
+ "fr",
15
+ "en",
16
+ "es",
17
+ "de",
18
+ "pt",
19
+ "it",
20
+ "nl",
21
+ "ru",
22
+ "ar",
23
+ "tr",
24
+ "ja",
25
+ "zh",
26
+ "ko"
27
+ ];
28
+ var AcfLocaleSchema = z.enum(SUPPORTED_LOCALES);
29
+ var LocalizedStringSchema = z.object({
30
+ fr: z.string(),
31
+ en: z.string()
32
+ }).catchall(z.string());
33
+ var PrincipleSchema = z.object({
34
+ id: z.string().regex(/^P[1-4]$/),
35
+ code: z.string().regex(/^P[1-4]$/),
36
+ title: LocalizedStringSchema,
37
+ summary: LocalizedStringSchema,
38
+ doctrine: LocalizedStringSchema,
39
+ related_fiches: z.array(z.string().regex(/^ACF-(0[0-9]|1[0-6])$/)),
40
+ related_dimensions: z.array(z.string().regex(/^D[1-6]$/))
41
+ });
42
+ var AutonomyLevelSchema = z.object({
43
+ id: z.string().regex(/^N[0-3]$/),
44
+ code: z.string().regex(/^N[0-3]$/),
45
+ title: LocalizedStringSchema,
46
+ description: LocalizedStringSchema,
47
+ controls: LocalizedStringSchema,
48
+ examples: LocalizedStringSchema
49
+ });
50
+ var DimensionSchema = z.object({
51
+ id: z.string().regex(/^D[1-6]$/),
52
+ code: z.string().regex(/^D[1-6]$/),
53
+ title: LocalizedStringSchema,
54
+ description: LocalizedStringSchema,
55
+ practices: LocalizedStringSchema
56
+ });
57
+ var DdaoSchema = z.object({
58
+ title: LocalizedStringSchema,
59
+ expansion: z.literal("Delegated Decision Agent Officer"),
60
+ definition: LocalizedStringSchema,
61
+ responsibilities: LocalizedStringSchema,
62
+ not_to_be_confused_with: LocalizedStringSchema
63
+ });
64
+ var RelatedArticleSchema = z.object({
65
+ regulation: z.enum(["ai-act", "gdpr", "dora", "nis2", "iso-42001"]),
66
+ article: z.string(),
67
+ paragraph: z.string().optional()
68
+ });
69
+ var FicheFrontmatterSchema = z.object({
70
+ code: z.string().regex(/^ACF-(0[0-9]|1[0-6])$/),
71
+ slug: z.string().min(1),
72
+ title: z.string().min(1),
73
+ title_en: z.string().optional(),
74
+ order: z.number().int().min(0).max(16),
75
+ maturity_dimension: z.string().regex(/^D[1-6]$/).optional(),
76
+ related_principles: z.array(z.string().regex(/^P[1-4]$/)).optional(),
77
+ related_articles: z.array(RelatedArticleSchema).optional(),
78
+ related_fiches: z.array(z.string().regex(/^ACF-(0[0-9]|1[0-6])$/)).optional(),
79
+ keywords: z.array(z.string()),
80
+ version: z.string(),
81
+ pdf_url: z.string().optional()
82
+ });
83
+ var ApplicableDateSchema = z.object({
84
+ obligation: z.string(),
85
+ date: z.string()
86
+ });
87
+ var GuideFrontmatterSchema = z.object({
88
+ regulation: z.enum(["ai-act", "gdpr", "dora", "nis2", "iso-42001"]),
89
+ title: z.string().min(1),
90
+ title_en: z.string().optional(),
91
+ version: z.string(),
92
+ last_update: z.string(),
93
+ applicable_dates: z.array(ApplicableDateSchema).optional()
94
+ });
95
+ var GlossaryEntrySchema = z.object({
96
+ term: z.string().min(1),
97
+ expansion: z.string().optional(),
98
+ definition: z.string().min(1),
99
+ related_principles: z.array(z.string().regex(/^P[1-4]$/)).optional(),
100
+ related_fiches: z.array(z.string().regex(/^ACF-(0[0-9]|1[0-6])$/)).optional()
101
+ });
102
+ z.object({
103
+ slug: z.string().min(1),
104
+ title: z.string().min(1),
105
+ level: z.union([z.literal(1), z.literal(2), z.literal(3)]),
106
+ body: z.string()
107
+ });
108
+ var MetaSchema = z.object({
109
+ framework_version: z.string(),
110
+ content_build: z.string(),
111
+ content_hash: z.string().startsWith("sha256:"),
112
+ doctrine_signature: z.string().optional(),
113
+ permanent_archive_url: z.string().url(),
114
+ rules_version: z.string(),
115
+ locales: z.array(AcfLocaleSchema),
116
+ fallback_locale: AcfLocaleSchema
117
+ });
118
+
119
+ // src/core/locale.ts
120
+ var DEFAULT_FALLBACK_LOCALE = "en";
121
+ var AUTHORITY_SOURCE_LOCALE = "fr";
122
+ function tryParseLocale(value) {
123
+ if (!value) return null;
124
+ const lower = value.trim().toLowerCase().slice(0, 2);
125
+ const result = AcfLocaleSchema.safeParse(lower);
126
+ return result.success ? result.data : null;
127
+ }
128
+ function negotiateLocale(input) {
129
+ const fromExplicit = tryParseLocale(input.explicit);
130
+ if (fromExplicit) return fromExplicit;
131
+ const fromEnv = tryParseLocale(input.envLocale);
132
+ if (fromEnv) return fromEnv;
133
+ return DEFAULT_FALLBACK_LOCALE;
134
+ }
135
+ function fallbackChain(requested) {
136
+ const chain = [requested];
137
+ if (!chain.includes(DEFAULT_FALLBACK_LOCALE)) chain.push(DEFAULT_FALLBACK_LOCALE);
138
+ if (!chain.includes(AUTHORITY_SOURCE_LOCALE)) chain.push(AUTHORITY_SOURCE_LOCALE);
139
+ return chain;
140
+ }
141
+
142
+ // src/core/content.ts
143
+ var ContentLoader = class {
144
+ root;
145
+ metaCache = null;
146
+ constructor(opts) {
147
+ this.root = opts.contentRoot;
148
+ }
149
+ /* -------------------- Meta -------------------- */
150
+ async loadMeta() {
151
+ if (this.metaCache) return this.metaCache;
152
+ const file = path.join(this.root, "meta.json");
153
+ const raw = JSON.parse(await readFile(file, "utf8"));
154
+ this.metaCache = MetaSchema.parse(raw);
155
+ return this.metaCache;
156
+ }
157
+ /* -------------------- Framework -------------------- */
158
+ async loadPrinciples() {
159
+ const file = path.join(this.root, "framework", "principles.json");
160
+ const raw = JSON.parse(await readFile(file, "utf8"));
161
+ return (raw.principles ?? []).map((p) => PrincipleSchema.parse(p));
162
+ }
163
+ async loadAutonomyLevels() {
164
+ const file = path.join(this.root, "framework", "autonomy-levels.json");
165
+ const raw = JSON.parse(await readFile(file, "utf8"));
166
+ return (raw.levels ?? []).map((l) => AutonomyLevelSchema.parse(l));
167
+ }
168
+ async loadDimensions() {
169
+ const file = path.join(this.root, "framework", "dimensions.json");
170
+ const raw = JSON.parse(await readFile(file, "utf8"));
171
+ return (raw.dimensions ?? []).map((d) => DimensionSchema.parse(d));
172
+ }
173
+ async loadDdao() {
174
+ const file = path.join(this.root, "framework", "ddao.json");
175
+ const raw = JSON.parse(await readFile(file, "utf8"));
176
+ return DdaoSchema.parse(raw);
177
+ }
178
+ /* -------------------- Fiches -------------------- */
179
+ /**
180
+ * Load a fiche by code with locale fallback chain.
181
+ *
182
+ * **Caller contract:** `code` MUST be pre-validated against `/^ACF-(0[0-9]|1[0-6])$/`
183
+ * before calling this method. ContentLoader does NOT sanitise the code and relies
184
+ * on Zod validation at the tool boundary (Phase 5+). Skipping pre-validation is a
185
+ * path traversal risk.
186
+ */
187
+ async loadFiche(code, locale) {
188
+ for (const candidate of fallbackChain(locale)) {
189
+ const file = path.join(this.root, "fiches", `${code}.${candidate}.md`);
190
+ if (!existsSync(file)) continue;
191
+ const raw = await readFile(file, "utf8");
192
+ const parsed = matter(raw);
193
+ const frontmatter = FicheFrontmatterSchema.parse(parsed.data);
194
+ return {
195
+ frontmatter,
196
+ body: parsed.content,
197
+ served_locale: candidate,
198
+ is_fallback: candidate !== locale
199
+ };
200
+ }
201
+ throw new Error(`Fiche ${code} not found in any locale chain for ${locale}`);
202
+ }
203
+ /* -------------------- Guides -------------------- */
204
+ /**
205
+ * Load a regulatory guide markdown with locale fallback chain.
206
+ *
207
+ * **Caller contract:** `regulation` MUST be pre-validated against the
208
+ * `["ai-act", "gdpr", "dora", "nis2", "iso-42001"]` enum before calling
209
+ * this method. ContentLoader does NOT sanitise the regulation string.
210
+ */
211
+ async loadGuide(regulation, locale) {
212
+ for (const candidate of fallbackChain(locale)) {
213
+ const file = path.join(this.root, "guides", `${regulation}.${candidate}.md`);
214
+ if (!existsSync(file)) continue;
215
+ const raw = await readFile(file, "utf8");
216
+ const parsed = matter(raw);
217
+ const frontmatter = GuideFrontmatterSchema.parse(parsed.data);
218
+ return {
219
+ frontmatter,
220
+ body: parsed.content,
221
+ served_locale: candidate,
222
+ is_fallback: candidate !== locale
223
+ };
224
+ }
225
+ throw new Error(
226
+ `Guide ${regulation} not found in any locale chain for ${locale}`
227
+ );
228
+ }
229
+ /* -------------------- Glossary -------------------- */
230
+ async loadGlossary(locale) {
231
+ for (const candidate of fallbackChain(locale)) {
232
+ const file = path.join(this.root, "glossary", `${candidate}.json`);
233
+ if (!existsSync(file)) continue;
234
+ const raw = JSON.parse(await readFile(file, "utf8"));
235
+ return {
236
+ entries: raw.map((entry) => GlossaryEntrySchema.parse(entry)),
237
+ served_locale: candidate,
238
+ is_fallback: candidate !== locale
239
+ };
240
+ }
241
+ throw new Error(`Glossary not found in any locale chain for ${locale}`);
242
+ }
243
+ /**
244
+ * Lookup a glossary entry by `term` or `expansion` (case-insensitive).
245
+ *
246
+ * **Caller contract:** `term` is matched against entry strings, not used as a
247
+ * file path, so path traversal is not a concern here. But callers should
248
+ * still validate length/format to avoid pathological inputs.
249
+ *
250
+ * Returns null if no entry matches.
251
+ */
252
+ async loadGlossaryEntry(term, locale) {
253
+ const { entries, served_locale, is_fallback } = await this.loadGlossary(locale);
254
+ const normalised = term.trim().toLowerCase();
255
+ const entry = entries.find(
256
+ (e) => e.term.toLowerCase() === normalised || e.expansion?.toLowerCase() === normalised
257
+ ) ?? null;
258
+ if (!entry) return null;
259
+ return { entry, served_locale, is_fallback };
260
+ }
261
+ /* -------------------- Whitepaper -------------------- */
262
+ /**
263
+ * Load the whitepaper raw markdown body with locale fallback chain.
264
+ *
265
+ * Returns the file contents AS-IS. If the file contains a YAML frontmatter
266
+ * block, that block is included verbatim in `body` (this method does NOT
267
+ * strip it). The Phase 5 whitepaper section parser is responsible for
268
+ * walking H1/H2 and discarding any frontmatter prefix.
269
+ */
270
+ async loadWhitepaperRaw(locale) {
271
+ for (const candidate of fallbackChain(locale)) {
272
+ const file = path.join(this.root, "whitepaper", `${candidate}.md`);
273
+ if (!existsSync(file)) continue;
274
+ const body = await readFile(file, "utf8");
275
+ return {
276
+ body,
277
+ served_locale: candidate,
278
+ is_fallback: candidate !== locale
279
+ };
280
+ }
281
+ throw new Error(`Whitepaper not found in any locale chain for ${locale}`);
282
+ }
283
+ };
284
+ var ConfidenceSchema = z.enum(["low", "medium", "high"]);
285
+ var CriticalitySchema = z.enum(["low", "medium", "high", "critical"]);
286
+ var AcfLevelSchema = z.enum(["N0", "N1", "N2", "N3"]);
287
+ var AiActRoleSchema = z.enum([
288
+ "provider",
289
+ "deployer",
290
+ "importer",
291
+ "distributor",
292
+ "not_applicable"
293
+ ]);
294
+ var GdprRoleSchema = z.enum([
295
+ "controller",
296
+ "processor",
297
+ "joint_controller",
298
+ "not_applicable"
299
+ ]);
300
+ var TriggerSchema = z.object({
301
+ structured_flags: z.array(z.string()).optional(),
302
+ keyword_patterns: z.array(z.string()).optional(),
303
+ negation_aware: z.boolean().default(true),
304
+ enum_match: z.record(z.string(), z.array(z.string())).optional()
305
+ });
306
+ var versionedHeader = {
307
+ version: z.string(),
308
+ last_update: z.string()
309
+ };
310
+ var RulesMetaSchema = z.object({
311
+ rules_version: z.string(),
312
+ last_update: z.string(),
313
+ applicable_jurisdictions: z.array(z.string())
314
+ });
315
+ var RuleCategorySchema = z.object({
316
+ id: z.string(),
317
+ title: z.string(),
318
+ triggers: TriggerSchema,
319
+ obligations: z.array(z.string()),
320
+ fiches: z.array(z.string()),
321
+ confidence_base: ConfidenceSchema,
322
+ requires_human_review: z.literal(true)
323
+ });
324
+ var AiActAnnexIIISchema = z.object({
325
+ ...versionedHeader,
326
+ applicable_dates: z.array(
327
+ z.object({
328
+ category: z.string(),
329
+ applicable_from: z.string(),
330
+ deferred: z.boolean()
331
+ })
332
+ ),
333
+ categories: z.array(RuleCategorySchema)
334
+ });
335
+ var AiActAnnexISchema = z.object({
336
+ ...versionedHeader,
337
+ applicable_dates: z.array(
338
+ z.object({
339
+ category: z.string(),
340
+ applicable_from: z.string(),
341
+ deferred: z.boolean()
342
+ })
343
+ ),
344
+ categories: z.array(RuleCategorySchema)
345
+ });
346
+ var GpaiTriggersSchema = z.object({
347
+ ...versionedHeader,
348
+ triggers: TriggerSchema,
349
+ obligations: z.array(
350
+ z.object({
351
+ article: z.string(),
352
+ requirement: z.string(),
353
+ applicable_date: z.string(),
354
+ systemic_risk_only: z.boolean().optional()
355
+ })
356
+ )
357
+ });
358
+ var CriticalityMatrixSchema = z.object({
359
+ ...versionedHeader,
360
+ cells: z.array(
361
+ z.object({
362
+ personal_data_level: z.enum(["none", "standard", "sensitive_special"]),
363
+ financial_exposure: z.enum([
364
+ "none",
365
+ "low_operation",
366
+ "medium_contract",
367
+ "high_corporate"
368
+ ]),
369
+ sector_modifier: z.number(),
370
+ score: CriticalitySchema,
371
+ rationale_template: z.string()
372
+ })
373
+ )
374
+ });
375
+ var AutonomyInferenceSchema = z.object({
376
+ ...versionedHeader,
377
+ thresholds: z.array(
378
+ z.object({
379
+ level: AcfLevelSchema,
380
+ conditions: z.record(z.string(), z.array(z.string())),
381
+ rationale_template: z.string()
382
+ })
383
+ )
384
+ });
385
+ var DdaoControlsMappingSchema = z.object({
386
+ ...versionedHeader,
387
+ mappings: z.array(
388
+ z.object({
389
+ level: AcfLevelSchema,
390
+ risk: CriticalitySchema,
391
+ recommended_controls: z.array(
392
+ z.object({
393
+ title: z.string(),
394
+ description: z.string(),
395
+ owner_role: z.enum([
396
+ "ddao",
397
+ "dpo",
398
+ "ciso",
399
+ "business_owner",
400
+ "auditor"
401
+ ]),
402
+ frequency: z.enum([
403
+ "one_time",
404
+ "monthly",
405
+ "quarterly",
406
+ "annual",
407
+ "on_event"
408
+ ]),
409
+ evidence_artifact: z.string()
410
+ })
411
+ ),
412
+ ddao_controls: z.array(
413
+ z.object({
414
+ control_id: z.string(),
415
+ control_type: z.enum([
416
+ "preventive",
417
+ "detective",
418
+ "corrective",
419
+ "governance"
420
+ ]),
421
+ fiche_reference: z.string(),
422
+ implementation_note: z.string()
423
+ })
424
+ )
425
+ })
426
+ )
427
+ });
428
+ var SignOffMatrixSchema = z.object({
429
+ ...versionedHeader,
430
+ rules: z.array(
431
+ z.object({
432
+ criticality: CriticalitySchema,
433
+ personal_data_level: z.enum(["none", "standard", "sensitive_special"]),
434
+ financial_exposure: z.enum([
435
+ "none",
436
+ "low_operation",
437
+ "medium_contract",
438
+ "high_corporate"
439
+ ]),
440
+ required: z.object({
441
+ security: z.boolean(),
442
+ privacy: z.boolean(),
443
+ compliance: z.boolean(),
444
+ legal: z.boolean(),
445
+ business_sponsor: z.boolean(),
446
+ board: z.boolean()
447
+ })
448
+ })
449
+ )
450
+ });
451
+ var GdprQualificationSchema = z.object({
452
+ ...versionedHeader,
453
+ cases: z.array(
454
+ z.object({
455
+ id: z.string(),
456
+ triggers: TriggerSchema,
457
+ role: GdprRoleSchema,
458
+ confidence_base: ConfidenceSchema,
459
+ rationale_template: z.string()
460
+ })
461
+ )
462
+ });
463
+ var AiActRolesSchema = z.object({
464
+ ...versionedHeader,
465
+ rules: z.array(
466
+ z.object({
467
+ id: z.string(),
468
+ triggers: TriggerSchema,
469
+ role: AiActRoleSchema,
470
+ confidence_base: ConfidenceSchema,
471
+ rationale_template: z.string()
472
+ })
473
+ )
474
+ });
475
+
476
+ // src/core/rules-loader.ts
477
+ var RulesLoader = class {
478
+ root;
479
+ constructor(opts) {
480
+ this.root = opts.rulesRoot;
481
+ }
482
+ async loadJson(file) {
483
+ const raw = await readFile(path.join(this.root, file), "utf8");
484
+ return JSON.parse(raw);
485
+ }
486
+ async loadRulesMeta() {
487
+ return RulesMetaSchema.parse(await this.loadJson("rules-meta.json"));
488
+ }
489
+ async loadAiActAnnexIII() {
490
+ return AiActAnnexIIISchema.parse(await this.loadJson("ai-act-annex-iii.json"));
491
+ }
492
+ async loadAiActAnnexI() {
493
+ return AiActAnnexISchema.parse(await this.loadJson("ai-act-annex-i.json"));
494
+ }
495
+ async loadGpaiTriggers() {
496
+ return GpaiTriggersSchema.parse(await this.loadJson("gpai-triggers.json"));
497
+ }
498
+ async loadAutonomyInference() {
499
+ return AutonomyInferenceSchema.parse(await this.loadJson("autonomy-inference.json"));
500
+ }
501
+ async loadCriticalityMatrix() {
502
+ return CriticalityMatrixSchema.parse(await this.loadJson("criticality-matrix.json"));
503
+ }
504
+ async loadDdaoControlsMapping() {
505
+ return DdaoControlsMappingSchema.parse(await this.loadJson("ddao-controls-mapping.json"));
506
+ }
507
+ async loadSignOffMatrix() {
508
+ return SignOffMatrixSchema.parse(await this.loadJson("sign-off-matrix.json"));
509
+ }
510
+ async loadGdprQualification() {
511
+ return GdprQualificationSchema.parse(await this.loadJson("gdpr-qualification.json"));
512
+ }
513
+ async loadAiActRoles() {
514
+ return AiActRolesSchema.parse(await this.loadJson("ai-act-roles.json"));
515
+ }
516
+ };
517
+ var FRAMEWORK_CATEGORIES = /* @__PURE__ */ new Set([
518
+ "principle",
519
+ "autonomy_level",
520
+ "dimension",
521
+ "ddao"
522
+ ]);
523
+ var CATEGORY_SCORE_MULTIPLIER = {
524
+ principle: 1.15,
525
+ autonomy_level: 1.1,
526
+ dimension: 1.1,
527
+ ddao: 1.1,
528
+ fiche: 1.05,
529
+ guide: 1.02,
530
+ whitepaper: 1,
531
+ glossary: 0.9,
532
+ meta: 0.8
533
+ };
534
+ var SearchEngine = class _SearchEngine {
535
+ constructor(idx, docs) {
536
+ this.idx = idx;
537
+ this.docs = docs;
538
+ }
539
+ idx;
540
+ docs;
541
+ static async fromFile(file) {
542
+ const raw = JSON.parse(await readFile(file, "utf8"));
543
+ const idx = lunr.Index.load(raw.index);
544
+ const docs = new Map(Object.entries(raw.docs));
545
+ return new _SearchEngine(idx, docs);
546
+ }
547
+ static fromMemory(serialised) {
548
+ const idx = lunr.Index.load(serialised.index);
549
+ const docs = new Map(Object.entries(serialised.docs));
550
+ return new _SearchEngine(idx, docs);
551
+ }
552
+ /**
553
+ * Normalise a query string the same way lunr normalises indexed tokens:
554
+ * - strip combining diacritics (é → e, ü → u, ç → c, etc.)
555
+ * - lower-case
556
+ * This ensures "souveraineté" matches the stored token "souverainete".
557
+ */
558
+ static normaliseQuery(q) {
559
+ return q.normalize("NFD").replace(new RegExp("\\p{Mn}", "gu"), "").toLowerCase();
560
+ }
561
+ search(input) {
562
+ const normalisedQuery = _SearchEngine.normaliseQuery(input.query);
563
+ let lunrResults;
564
+ try {
565
+ lunrResults = this.idx.search(normalisedQuery);
566
+ } catch {
567
+ const sanitised = normalisedQuery.replace(/[^\p{L}\p{N}\s]/gu, " ").trim();
568
+ lunrResults = sanitised ? this.idx.search(sanitised) : [];
569
+ }
570
+ const candidates = [];
571
+ for (const r of lunrResults) {
572
+ const doc = this.docs.get(r.ref);
573
+ if (!doc) continue;
574
+ if (doc.locale !== input.locale) continue;
575
+ if (!this.matchesScope(doc.category, input.scope)) continue;
576
+ const multiplier = CATEGORY_SCORE_MULTIPLIER[doc.category] ?? 1;
577
+ candidates.push({
578
+ uri: doc.uri,
579
+ title: doc.title,
580
+ snippet: doc.snippet,
581
+ score: r.score * multiplier,
582
+ category: doc.category,
583
+ locale: doc.locale
584
+ });
585
+ }
586
+ candidates.sort((a, b) => b.score - a.score);
587
+ return candidates.slice(0, input.limit);
588
+ }
589
+ matchesScope(category, scope) {
590
+ if (scope === "all") return true;
591
+ if (scope === "framework") return FRAMEWORK_CATEGORIES.has(category);
592
+ if (scope === "fiche") return category === "fiche";
593
+ if (scope === "guide") return category === "guide";
594
+ if (scope === "whitepaper") return category === "whitepaper";
595
+ if (scope === "glossary") return category === "glossary";
596
+ return false;
597
+ }
598
+ };
599
+
600
+ // src/core/uri.ts
601
+ var PREFIX = "acf://";
602
+ function parseAcfUri(uri) {
603
+ if (!uri.startsWith(PREFIX)) {
604
+ throw new Error(`Invalid scheme \u2014 expected acf://, got: ${uri}`);
605
+ }
606
+ const rest = uri.slice(PREFIX.length);
607
+ const [pathPart, queryPart] = rest.split("?", 2);
608
+ if (pathPart === void 0) {
609
+ throw new Error(`Empty URI: ${uri}`);
610
+ }
611
+ const segments = pathPart.split("/").filter(Boolean);
612
+ let locale;
613
+ if (queryPart) {
614
+ const params = new URLSearchParams(queryPart);
615
+ const lang = params.get("lang");
616
+ if (lang) {
617
+ const parsed = AcfLocaleSchema.safeParse(lang);
618
+ if (parsed.success) locale = parsed.data;
619
+ }
620
+ }
621
+ if (segments[0] === "framework") {
622
+ if (segments[1] === "principle" && segments[2]) {
623
+ return { category: "framework_principle", id: segments[2], locale };
624
+ }
625
+ if (segments[1] === "autonomy-level" && segments[2]) {
626
+ return { category: "framework_autonomy_level", id: segments[2], locale };
627
+ }
628
+ if (segments[1] === "dimension" && segments[2]) {
629
+ return { category: "framework_dimension", id: segments[2], locale };
630
+ }
631
+ if (segments[1] === "ddao") {
632
+ return { category: "framework_ddao", id: void 0, locale };
633
+ }
634
+ }
635
+ if (segments[0] === "fiche" && segments[1]) {
636
+ return { category: "fiche", id: segments[1], locale };
637
+ }
638
+ if (segments[0] === "guide" && segments[1]) {
639
+ return { category: "guide", id: segments[1], locale };
640
+ }
641
+ if (segments[0] === "whitepaper") {
642
+ if (!segments[1]) return { category: "whitepaper", id: void 0, locale };
643
+ if (segments[1] === "toc")
644
+ return { category: "whitepaper_toc", id: void 0, locale };
645
+ if (segments[1] === "section" && segments[2]) {
646
+ return { category: "whitepaper_section", id: segments[2], locale };
647
+ }
648
+ }
649
+ if (segments[0] === "manual") {
650
+ if (!segments[1]) return { category: "manual", id: void 0, locale };
651
+ if (segments[1] === "toc")
652
+ return { category: "manual_toc", id: void 0, locale };
653
+ if (segments[1] === "section" && segments[2]) {
654
+ return { category: "manual_section", id: segments[2], locale };
655
+ }
656
+ }
657
+ if (segments[0] === "deck") {
658
+ return { category: "deck", id: void 0, locale };
659
+ }
660
+ if (segments[0] === "glossary") {
661
+ if (!segments[1]) return { category: "glossary", id: void 0, locale };
662
+ return {
663
+ category: "glossary_entry",
664
+ id: decodeURIComponent(segments[1]),
665
+ locale
666
+ };
667
+ }
668
+ if (segments[0] === "meta") {
669
+ return { category: "meta", id: void 0, locale };
670
+ }
671
+ throw new Error(`Unknown ACF URI: ${uri}`);
672
+ }
673
+
674
+ // src/core/resources.ts
675
+ async function listResources(registry) {
676
+ const items = [];
677
+ const principles = await registry.content.loadPrinciples();
678
+ for (const p of principles) {
679
+ items.push({
680
+ uri: `acf://framework/principle/${p.code}`,
681
+ name: `Principle ${p.code} \u2014 ${p.title.en}`,
682
+ description: p.summary.en,
683
+ mimeType: "application/json"
684
+ });
685
+ }
686
+ const levels = await registry.content.loadAutonomyLevels();
687
+ for (const l of levels) {
688
+ items.push({
689
+ uri: `acf://framework/autonomy-level/${l.code}`,
690
+ name: `Autonomy level ${l.code} \u2014 ${l.title.en}`,
691
+ description: l.description.en,
692
+ mimeType: "application/json"
693
+ });
694
+ }
695
+ const dimensions = await registry.content.loadDimensions();
696
+ for (const d of dimensions) {
697
+ items.push({
698
+ uri: `acf://framework/dimension/${d.code}`,
699
+ name: `Maturity dimension ${d.code} \u2014 ${d.title.en}`,
700
+ description: d.description.en,
701
+ mimeType: "application/json"
702
+ });
703
+ }
704
+ items.push({
705
+ uri: "acf://framework/ddao",
706
+ name: "DDAO \u2014 Delegated Decision Agent Officer",
707
+ description: "Canonical ACF\xAE role definition for autonomous-agent guardianship.",
708
+ mimeType: "application/json"
709
+ });
710
+ for (let i = 0; i <= 16; i++) {
711
+ const code = `ACF-${String(i).padStart(2, "0")}`;
712
+ items.push({
713
+ uri: `acf://fiche/${code}`,
714
+ name: `ACF\xAE card ${code}`,
715
+ description: `One of the 17 ACF\xAE methodological cards (V1.0).`,
716
+ mimeType: "text/markdown"
717
+ });
718
+ }
719
+ items.push({
720
+ uri: "acf://glossary",
721
+ name: "ACF\xAE Glossary index",
722
+ description: "List of canonical ACF\xAE terms with cross-refs.",
723
+ mimeType: "application/json"
724
+ });
725
+ items.push({
726
+ uri: "acf://meta",
727
+ name: "ACF\xAE doctrine metadata",
728
+ description: "Framework version, content hash, archive URL, supported locales.",
729
+ mimeType: "application/json"
730
+ });
731
+ return items;
732
+ }
733
+ async function readResource(registry, uri, acceptLanguage) {
734
+ const parsed = parseAcfUri(uri);
735
+ const locale = negotiateLocale({
736
+ explicit: parsed.locale,
737
+ envLocale: process.env["ACF_MCP_LOCALE"]
738
+ });
739
+ switch (parsed.category) {
740
+ case "framework_principle": {
741
+ const list = await registry.content.loadPrinciples();
742
+ const found = list.find((p) => p.code === parsed.id);
743
+ if (!found) throw new Error(`Principle not found: ${parsed.id}`);
744
+ return jsonResource(uri, found);
745
+ }
746
+ case "framework_autonomy_level": {
747
+ const list = await registry.content.loadAutonomyLevels();
748
+ const found = list.find((l) => l.code === parsed.id);
749
+ if (!found) throw new Error(`Autonomy level not found: ${parsed.id}`);
750
+ return jsonResource(uri, found);
751
+ }
752
+ case "framework_dimension": {
753
+ const list = await registry.content.loadDimensions();
754
+ const found = list.find((d) => d.code === parsed.id);
755
+ if (!found) throw new Error(`Dimension not found: ${parsed.id}`);
756
+ return jsonResource(uri, found);
757
+ }
758
+ case "framework_ddao": {
759
+ return jsonResource(uri, await registry.content.loadDdao());
760
+ }
761
+ case "fiche": {
762
+ const fiche = await registry.content.loadFiche(parsed.id ?? "", locale);
763
+ return {
764
+ contents: [
765
+ {
766
+ uri,
767
+ mimeType: "text/markdown",
768
+ text: renderFicheMarkdown(fiche)
769
+ }
770
+ ]
771
+ };
772
+ }
773
+ case "glossary": {
774
+ const { entries } = await registry.content.loadGlossary(locale);
775
+ return jsonResource(uri, entries);
776
+ }
777
+ case "glossary_entry": {
778
+ const result = await registry.content.loadGlossaryEntry(parsed.id ?? "", locale);
779
+ if (!result) throw new Error(`Glossary entry not found: ${parsed.id}`);
780
+ return jsonResource(uri, result.entry);
781
+ }
782
+ case "meta": {
783
+ return jsonResource(uri, await registry.content.loadMeta());
784
+ }
785
+ default:
786
+ throw new Error(`Resource not implemented in V1.0: ${parsed.category}`);
787
+ }
788
+ }
789
+ function jsonResource(uri, data) {
790
+ return {
791
+ contents: [
792
+ { uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }
793
+ ]
794
+ };
795
+ }
796
+ function renderFicheMarkdown(fiche) {
797
+ const head = [
798
+ "---",
799
+ `served_locale: ${fiche.served_locale}`,
800
+ `is_fallback: ${fiche.is_fallback}`,
801
+ "---",
802
+ ""
803
+ ].join("\n");
804
+ return head + fiche.body;
805
+ }
806
+ async function registerResources(server, registry) {
807
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
808
+ resources: await listResources(registry)
809
+ }));
810
+ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
811
+ return readResource(registry, req.params.uri);
812
+ });
813
+ }
814
+
815
+ // src/constants/tool-descriptions.ts
816
+ var TOOL_DESCRIPTIONS = {
817
+ "acf.search": "When you don't know which ACF\xAE resource is most relevant to the situation at hand (a question, a regulator request, a board challenge\u2026), search the full ACF\xAE corpus and rank candidate resources by relevance.",
818
+ "acf.fiche.lookup": "When facing a specific governance question (defining an agent's mandate, designing a kill switch, building a decision register\u2026), retrieve the canonical ACF\xAE methodological card that applies \u2014 with its example, its mapping to principles and its related cards.",
819
+ "acf.regulation.article": "When a regulator, an auditor or a board references a specific article (AI Act Art. 9, GDPR Art. 35, DORA Art. 28, NIS2 Art. 21, ISO 42001 \xA76\u2026), get the verified text and its operational translation into the ACF\xAE framework \u2014 which principles it activates, which maturity dimensions it stresses, which fiches operationalise it.",
820
+ "acf.glossary.define": "When governance vocabulary becomes ambiguous (what is a DDAO? what does Decision Sovereignty exactly mean? when does an agent transition from N1 to N2?), get the canonical ACF\xAE definition and its connection to the rest of the doctrine.",
821
+ "acf.cite": "When writing a thesis, a regulatory filing, a board memo or an academic paper that needs to reference ACF\xAE, produce a properly formatted citation in the requested style (APA, MLA, Chicago, ISO 690, BibTeX).",
822
+ "acf.advisor": "When a user describes a real AI use case and needs a structured ACF\xAE governance assessment in return \u2014 which principles fire, which maturity dimensions are critical, which autonomy level fits, which regulatory obligations apply, which fiches to mobilise in what order, what are the first operational actions, what are the operational risks. This is the conversion tool: from documentation library to governance advisor.",
823
+ "acf.classify-agent": "When a system owner needs a first-pass governance qualification of an AI agent before go-live \u2014 what autonomy level it likely operates at, what risk class it likely carries, what regulations apply, what controls to put in place, who should sign off \u2014 get one structured preliminary assessment covering all five questions in a single call. Input kept deliberately simple (10 qualified fields); output is structured for human review, not for autonomous regulatory decision-making.",
824
+ "acf.assess-autonomy": "When deciding how much autonomy to grant an agent \u2014 should it propose, decide or execute? what triggers human review? what is the kill switch design? \u2014 get a first-pass assessment grounded in the 4 ACF\xAE autonomy levels with explicit go/no-go criteria, gating thresholds, and uncertainty signalling for human review.",
825
+ "acf.identify-governance-gaps": "When auditing your current AI governance setup \u2014 what does ACF\xAE suggest you should have that you don't? \u2014 get a first-pass gap analysis across the 6 maturity dimensions, prioritised by criticality, with remediation actions, quick wins, and explicit uncertainty signalling on which gaps were inferred vs declared.",
826
+ "acf.map-ai-act-obligations": "When a system has been preliminarily qualified as high-risk and you need the exhaustive list of AI Act obligations that apply \u2014 Art. 9 risk management, Art. 10 data, Art. 11 docs, Art. 12 logs, Art. 13 transparency, Art. 14 human oversight, Art. 15 robustness, GPAI Art. 51-55 \u2014 get the full obligation set structured by lifecycle phase (pre-go-live / continuous / on-incident) with deadlines (incl. Digital Omnibus deferrals) and ACF\xAE operational translation.",
827
+ "acf.assign-ddao-controls": "When a DDAO needs concrete controls assigned to an autonomous agent \u2014 what kill switches, what escalation thresholds, what audit trails, what documentation, who owns each control \u2014 get the ACF\xAE control set scoped to the agent's autonomy level and risk class, with double vocabulary (plain-English recommended_controls + ACF-canonical ddao_controls) for external clarity.",
828
+ "acf.evaluate-agent-mandate": "When an existing agent mandate already exists (drafted or proposed for DDAO sign-off) and you need a first-pass audit before go-live or regulatory inspection \u2014 does it cover the decision perimeter, the escalation thresholds, the kill switch, the audit trail, the documentation duty? \u2014 get a structured preliminary verdict with strengths, gaps, required additions, and explicit gaps_to_validate for the human reviewer."
829
+ };
830
+ var SearchInputSchema = z.object({
831
+ query: z.string().min(2).max(200),
832
+ scope: z.enum(["all", "framework", "fiche", "guide", "whitepaper", "glossary"]).default("all"),
833
+ locale: AcfLocaleSchema.optional(),
834
+ limit: z.number().int().min(1).max(20).default(5)
835
+ });
836
+ async function handleSearchTool(registry, rawInput) {
837
+ const input = SearchInputSchema.parse(rawInput);
838
+ const locale = input.locale ?? "en";
839
+ const hits = registry.search.search({
840
+ query: input.query,
841
+ scope: input.scope,
842
+ locale,
843
+ limit: input.limit
844
+ });
845
+ return { hits };
846
+ }
847
+ var FicheLookupInputSchema = z.object({
848
+ code: z.string().regex(/^ACF-(0[0-9]|1[0-6])$/),
849
+ locale: AcfLocaleSchema.optional(),
850
+ include_frontmatter: z.boolean().default(true)
851
+ });
852
+ async function handleFicheLookupTool(registry, rawInput) {
853
+ const input = FicheLookupInputSchema.parse(rawInput);
854
+ const locale = input.locale ?? "en";
855
+ const fiche = await registry.content.loadFiche(input.code, locale);
856
+ return {
857
+ code: fiche.frontmatter.code,
858
+ title: fiche.frontmatter.title,
859
+ markdown: fiche.body,
860
+ metadata: input.include_frontmatter ? fiche.frontmatter : {},
861
+ pdf_url: fiche.frontmatter.pdf_url ?? "",
862
+ uri: `acf://fiche/${fiche.frontmatter.code}`,
863
+ served_locale: fiche.served_locale,
864
+ is_fallback: fiche.is_fallback
865
+ };
866
+ }
867
+ var RegulationArticleInputSchema = z.object({
868
+ regulation: z.enum(["ai-act", "gdpr", "dora", "nis2", "iso-42001"]),
869
+ article: z.string().min(1).max(20),
870
+ locale: AcfLocaleSchema.optional()
871
+ });
872
+ var ARTICLE_MAPPINGS = {
873
+ "ai-act": {
874
+ "9": {
875
+ regulation: "ai-act",
876
+ article: "9",
877
+ title: "Risk management system (high-risk AI systems)",
878
+ text: "Providers of high-risk AI systems shall establish, implement, document and maintain a risk management system. See Regulation (EU) 2024/1689, Article 9.",
879
+ mapping: {
880
+ principles: ["P2", "P4"],
881
+ dimensions: ["D5", "D6"],
882
+ fiches: ["ACF-02", "ACF-11"],
883
+ operational_note: "ACF\xAE operationalises Art. 9 via ACF-02 (criticality matrix) + ACF-11 (risk assessment), bound by P4 (proportional governance)."
884
+ },
885
+ applicable_date: "2027-12-02"
886
+ },
887
+ "10": {
888
+ regulation: "ai-act",
889
+ article: "10",
890
+ title: "Data and data governance (high-risk AI systems)",
891
+ text: "Training, validation and testing data sets shall be subject to data governance and management practices appropriate for the intended purpose. See Regulation (EU) 2024/1689, Article 10.",
892
+ mapping: {
893
+ principles: ["P2"],
894
+ dimensions: ["D5"],
895
+ fiches: ["ACF-05", "ACF-13"],
896
+ operational_note: "ACF\xAE operationalises Art. 10 via ACF-05 (decision register) + ACF-13 (retention policy)."
897
+ },
898
+ applicable_date: "2027-12-02"
899
+ },
900
+ "14": {
901
+ regulation: "ai-act",
902
+ article: "14",
903
+ title: "Human oversight (high-risk AI systems)",
904
+ text: "High-risk AI systems shall be designed and developed so that they can be effectively overseen by natural persons. See Regulation (EU) 2024/1689, Article 14.",
905
+ mapping: {
906
+ principles: ["P3"],
907
+ dimensions: ["D3", "D4"],
908
+ fiches: ["ACF-07", "ACF-09", "ACF-14"],
909
+ operational_note: "ACF\xAE operationalises Art. 14 via P3 (ultimate human control), ACF-07 (kill switch), ACF-09 (escalation thresholds), ACF-14 (takeover drills)."
910
+ },
911
+ applicable_date: "2027-12-02"
912
+ }
913
+ },
914
+ "gdpr": {
915
+ "25": {
916
+ regulation: "gdpr",
917
+ article: "25",
918
+ title: "Data protection by design and by default",
919
+ text: "The controller shall implement appropriate technical and organisational measures to ensure data protection by design and by default. See Regulation (EU) 2016/679, Article 25.",
920
+ mapping: {
921
+ principles: ["P2"],
922
+ dimensions: ["D3", "D5"],
923
+ fiches: ["ACF-03", "ACF-13"],
924
+ operational_note: "ACF\xAE operationalises Art. 25 via ACF-03 (constitution forbidding non-purposeful PII access) + ACF-13 (retention policy)."
925
+ }
926
+ },
927
+ "35": {
928
+ regulation: "gdpr",
929
+ article: "35",
930
+ title: "Data protection impact assessment (DPIA)",
931
+ text: "Where processing is likely to result in a high risk to the rights and freedoms of natural persons, the controller shall carry out a DPIA. See Regulation (EU) 2016/679, Article 35.",
932
+ mapping: {
933
+ principles: ["P2"],
934
+ dimensions: ["D5"],
935
+ fiches: ["ACF-11"],
936
+ operational_note: "ACF\xAE operationalises Art. 35 via ACF-11 (risk assessment) feeding the DPIA template."
937
+ }
938
+ }
939
+ }
940
+ };
941
+ async function handleRegulationArticleTool(_registry, rawInput) {
942
+ const input = RegulationArticleInputSchema.parse(rawInput);
943
+ const reg = ARTICLE_MAPPINGS[input.regulation];
944
+ if (!reg || !reg[input.article]) {
945
+ throw new Error(
946
+ `Article ${input.regulation} Art. ${input.article} not yet seeded in V1.0 \u2014 full expert guides arrive in content-authoring sub-plan.`
947
+ );
948
+ }
949
+ return reg[input.article];
950
+ }
951
+ var GlossaryDefineInputSchema = z.object({
952
+ term: z.string().min(1).max(80),
953
+ locale: AcfLocaleSchema.optional()
954
+ });
955
+ async function handleGlossaryDefineTool(registry, rawInput) {
956
+ const input = GlossaryDefineInputSchema.parse(rawInput);
957
+ const locale = input.locale ?? "en";
958
+ const result = await registry.content.loadGlossaryEntry(input.term, locale);
959
+ if (!result) {
960
+ throw new Error(`Glossary entry not found: ${input.term}`);
961
+ }
962
+ return {
963
+ term: result.entry.term,
964
+ expansion: result.entry.expansion,
965
+ definition: result.entry.definition,
966
+ related: {
967
+ principles: result.entry.related_principles ?? [],
968
+ fiches: result.entry.related_fiches ?? []
969
+ },
970
+ // Use the ACTUAL served locale (which may be a fallback), not the requested one.
971
+ served_locale: result.served_locale
972
+ };
973
+ }
974
+ var CiteInputSchema = z.object({
975
+ uri: z.string().regex(/^acf:\/\//),
976
+ style: z.enum(["apa", "mla", "chicago", "iso-690", "bibtex"]).default("apa"),
977
+ locale: AcfLocaleSchema.optional()
978
+ });
979
+ var CITATION_AUTHOR = "Dorange, V.";
980
+ var CITATION_YEAR = "2026";
981
+ var CITATION_BASE_URL = "https://acfstandard.com";
982
+ function uriToUrlSlug(uri) {
983
+ return uri.replace(/^acf:\/\//, "").replace(/[^a-z0-9]+/gi, "-").replace(/^-|-$/g, "");
984
+ }
985
+ async function handleCiteTool(registry, rawInput) {
986
+ const input = CiteInputSchema.parse(rawInput);
987
+ const meta = registry.meta;
988
+ const slug = uriToUrlSlug(input.uri);
989
+ const url = `${CITATION_BASE_URL}/doctrine/v${meta.framework_version}/${slug}`;
990
+ const title = `Agentic Commerce Framework\xAE (ACF\xAE) \u2014 ${input.uri}`;
991
+ const structured = {
992
+ author: CITATION_AUTHOR,
993
+ year: CITATION_YEAR,
994
+ title,
995
+ url
996
+ };
997
+ let citation;
998
+ switch (input.style) {
999
+ case "apa":
1000
+ citation = `${CITATION_AUTHOR} (${CITATION_YEAR}). ${title}. ACF Standard. ${url}`;
1001
+ break;
1002
+ case "mla":
1003
+ citation = `${CITATION_AUTHOR} "${title}." ACF Standard, ${CITATION_YEAR}, ${url}.`;
1004
+ break;
1005
+ case "chicago":
1006
+ citation = `${CITATION_AUTHOR} "${title}." ACF Standard. ${CITATION_YEAR}. ${url}.`;
1007
+ break;
1008
+ case "iso-690":
1009
+ citation = `${CITATION_AUTHOR} ${title}. ACF Standard, ${CITATION_YEAR}. Available at: ${url}`;
1010
+ break;
1011
+ case "bibtex":
1012
+ citation = [
1013
+ `@misc{acf-${slug},`,
1014
+ ` author = {${CITATION_AUTHOR}},`,
1015
+ ` title = {${title}},`,
1016
+ ` year = {${CITATION_YEAR}},`,
1017
+ ` url = {${url}}`,
1018
+ "}"
1019
+ ].join("\n");
1020
+ break;
1021
+ }
1022
+ return { citation, structured };
1023
+ }
1024
+
1025
+ // src/lib/zod-to-json-schema.ts
1026
+ function zodToJsonSchema(schema) {
1027
+ return toSchema(schema);
1028
+ }
1029
+ function toSchema(s2) {
1030
+ const def = s2._def;
1031
+ switch (def.typeName) {
1032
+ case "ZodObject": {
1033
+ const obj = s2;
1034
+ const properties = {};
1035
+ const required = [];
1036
+ const shape = obj.shape;
1037
+ for (const key of Object.keys(shape)) {
1038
+ const child = shape[key];
1039
+ properties[key] = toSchema(child);
1040
+ if (!isOptional(child)) required.push(key);
1041
+ }
1042
+ return {
1043
+ type: "object",
1044
+ properties,
1045
+ required,
1046
+ additionalProperties: false
1047
+ };
1048
+ }
1049
+ case "ZodString": {
1050
+ const s22 = s2;
1051
+ const out = { type: "string" };
1052
+ const checks = s22._def.checks ?? [];
1053
+ for (const c of checks) {
1054
+ if (c.kind === "min" && c.value !== void 0) out["minLength"] = c.value;
1055
+ if (c.kind === "max" && c.value !== void 0) out["maxLength"] = c.value;
1056
+ }
1057
+ return out;
1058
+ }
1059
+ case "ZodNumber": {
1060
+ const s22 = s2;
1061
+ const out = { type: "number" };
1062
+ const checks = s22._def.checks ?? [];
1063
+ for (const c of checks) {
1064
+ if (c.kind === "min" && c.value !== void 0) out["minimum"] = c.value;
1065
+ if (c.kind === "max" && c.value !== void 0) out["maximum"] = c.value;
1066
+ if (c.kind === "int") out["type"] = "integer";
1067
+ }
1068
+ return out;
1069
+ }
1070
+ case "ZodBoolean":
1071
+ return { type: "boolean" };
1072
+ case "ZodEnum": {
1073
+ const e = s2;
1074
+ return { type: "string", enum: [...e.options] };
1075
+ }
1076
+ case "ZodArray": {
1077
+ const inner = s2._def.type;
1078
+ return { type: "array", items: toSchema(inner) };
1079
+ }
1080
+ case "ZodOptional": {
1081
+ const inner = s2._def.innerType;
1082
+ return toSchema(inner);
1083
+ }
1084
+ case "ZodDefault": {
1085
+ const inner = s2._def.innerType;
1086
+ const out = toSchema(inner);
1087
+ const defaultValue = s2._def.defaultValue();
1088
+ return { ...out, default: defaultValue };
1089
+ }
1090
+ case "ZodLiteral": {
1091
+ const v = s2._def.value;
1092
+ return { const: v };
1093
+ }
1094
+ case "ZodRecord":
1095
+ return { type: "object", additionalProperties: true };
1096
+ default:
1097
+ return {};
1098
+ }
1099
+ }
1100
+ function isOptional(s2) {
1101
+ const t = s2._def.typeName;
1102
+ return t === "ZodOptional" || t === "ZodDefault";
1103
+ }
1104
+ var HumanApprovalEnum = z.enum(["always", "sometimes", "never"]);
1105
+ var PersonalDataEnum = z.enum([
1106
+ "none",
1107
+ "standard",
1108
+ "sensitive_special"
1109
+ ]);
1110
+ var FinancialExposureEnum = z.enum([
1111
+ "none",
1112
+ "low_operation",
1113
+ "medium_contract",
1114
+ "high_corporate"
1115
+ ]);
1116
+ var ExternalActionsEnum = z.enum([
1117
+ "none",
1118
+ "read_only",
1119
+ "limited_write",
1120
+ "full_write"
1121
+ ]);
1122
+ var UsageAudienceEnum = z.enum([
1123
+ "internal",
1124
+ "third_party_b2b",
1125
+ "public_consumer"
1126
+ ]);
1127
+ var JurisdictionEnum = z.enum([
1128
+ "eu",
1129
+ "uk",
1130
+ "us",
1131
+ "ca",
1132
+ "ch",
1133
+ "br",
1134
+ "jp",
1135
+ "other"
1136
+ ]);
1137
+ var AiActTriggerEnum = z.enum([
1138
+ "biometric_identity",
1139
+ "critical_infrastructure",
1140
+ "educational_assessment",
1141
+ "employment_recruitment",
1142
+ "credit_scoring",
1143
+ "law_enforcement",
1144
+ "migration_asylum",
1145
+ "justice_democracy",
1146
+ "none"
1147
+ ]);
1148
+ var ProcessingPurposeEnum = z.enum([
1149
+ "hr",
1150
+ "marketing",
1151
+ "core_financial",
1152
+ "tech_support",
1153
+ "healthcare",
1154
+ "education",
1155
+ "public_service",
1156
+ "compliance_monitoring",
1157
+ "other"
1158
+ ]);
1159
+ var ClassifyAgentInputSchema = z.object({
1160
+ name: z.string().min(2).max(200),
1161
+ description: z.string().min(20).max(1e3),
1162
+ decisions_taken: z.array(z.string()).min(1).max(20),
1163
+ human_approval_required: HumanApprovalEnum,
1164
+ personal_data_level: PersonalDataEnum,
1165
+ financial_exposure: FinancialExposureEnum,
1166
+ external_actions: ExternalActionsEnum,
1167
+ gpai_used: z.boolean(),
1168
+ usage_audience: UsageAudienceEnum,
1169
+ sector: z.string().max(80).optional(),
1170
+ jurisdiction: z.array(JurisdictionEnum).optional(),
1171
+ ai_act_triggers: z.array(AiActTriggerEnum).optional(),
1172
+ processing_purposes: z.array(ProcessingPurposeEnum).optional(),
1173
+ locale: AcfLocaleSchema.optional()
1174
+ });
1175
+ function extractFlags(input) {
1176
+ return [
1177
+ `human_approval_required:${input.human_approval_required}`,
1178
+ `personal_data_level:${input.personal_data_level}`,
1179
+ `financial_exposure:${input.financial_exposure}`,
1180
+ `external_actions:${input.external_actions}`,
1181
+ `usage_audience:${input.usage_audience}`,
1182
+ `gpai_used:${input.gpai_used}`
1183
+ ];
1184
+ }
1185
+ function extractEnumHints(input) {
1186
+ const hints = {};
1187
+ if (input.ai_act_triggers) hints["ai_act_triggers"] = input.ai_act_triggers;
1188
+ if (input.processing_purposes)
1189
+ hints["processing_purposes"] = input.processing_purposes;
1190
+ hints["usage_audience"] = [input.usage_audience];
1191
+ hints["personal_data_level"] = [input.personal_data_level];
1192
+ hints["gpai_used"] = [String(input.gpai_used)];
1193
+ return hints;
1194
+ }
1195
+
1196
+ // src/core/infer-autonomy.ts
1197
+ function inferAutonomy(rules, input) {
1198
+ for (const t of rules.thresholds) {
1199
+ const happroved = t.conditions["human_approval_required"] ?? [];
1200
+ const ext = t.conditions["external_actions"] ?? [];
1201
+ if (happroved.includes(input.human_approval_required) && ext.includes(input.external_actions)) {
1202
+ return {
1203
+ level: t.level,
1204
+ rationale: t.rationale_template,
1205
+ rule_id: `autonomy-inference.${t.level}`
1206
+ };
1207
+ }
1208
+ }
1209
+ return {
1210
+ level: "N1",
1211
+ rationale: "No clear autonomy threshold matched the declared inputs; defaulting to N1 (supervised). Confirm with the team.",
1212
+ rule_id: "autonomy-inference.default-N1"
1213
+ };
1214
+ }
1215
+
1216
+ // src/core/infer-criticality.ts
1217
+ function inferCriticality(matrix, input) {
1218
+ for (const cell of matrix.cells) {
1219
+ if (cell.personal_data_level === input.personal_data_level && cell.financial_exposure === input.financial_exposure) {
1220
+ return {
1221
+ score: cell.score,
1222
+ rationale: cell.rationale_template,
1223
+ matrix_ref: `cell(${cell.personal_data_level}, ${cell.financial_exposure})`,
1224
+ rule_id: `criticality-matrix.${cell.personal_data_level}-${cell.financial_exposure}`
1225
+ };
1226
+ }
1227
+ }
1228
+ return {
1229
+ score: "medium",
1230
+ rationale: "Combination of personal_data_level + financial_exposure not yet calibrated; defaulting to medium for safety.",
1231
+ matrix_ref: "default-medium",
1232
+ rule_id: "criticality-matrix.default-medium"
1233
+ };
1234
+ }
1235
+
1236
+ // src/core/negation-aware.ts
1237
+ var NEGATION_TOKENS = [
1238
+ // FR
1239
+ "n'a pas",
1240
+ "n'est pas",
1241
+ "ne pas",
1242
+ "ne sont pas",
1243
+ "n'analyse pas",
1244
+ "pas de",
1245
+ "aucun",
1246
+ "aucune",
1247
+ "jamais",
1248
+ "n'... jamais",
1249
+ "ne... jamais",
1250
+ // EN
1251
+ "not ",
1252
+ "no ",
1253
+ "never",
1254
+ "without",
1255
+ "absent"
1256
+ ];
1257
+ var NEGATION_WINDOW = 40;
1258
+ var NEGATION_REGEX = new RegExp(
1259
+ `(${NEGATION_TOKENS.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`,
1260
+ "i"
1261
+ );
1262
+ function negationAwareMatch(text, patterns) {
1263
+ const lower = text.toLowerCase();
1264
+ const hits = [];
1265
+ for (const pattern of patterns) {
1266
+ const p = pattern.toLowerCase();
1267
+ let from = 0;
1268
+ while (from <= lower.length) {
1269
+ const i = lower.indexOf(p, from);
1270
+ if (i === -1) break;
1271
+ const windowStart = Math.max(0, i - NEGATION_WINDOW);
1272
+ const before = lower.slice(windowStart, i);
1273
+ const negated = NEGATION_REGEX.test(before);
1274
+ hits.push({ pattern, index: i, negated });
1275
+ from = i + p.length;
1276
+ }
1277
+ }
1278
+ return {
1279
+ matched: hits.some((h) => !h.negated),
1280
+ hits
1281
+ };
1282
+ }
1283
+
1284
+ // src/core/trigger-eval.ts
1285
+ function evaluateTrigger(trigger, input) {
1286
+ const evidence = [];
1287
+ let fired = false;
1288
+ if (trigger.keyword_patterns && trigger.keyword_patterns.length > 0) {
1289
+ const r = negationAwareMatch(input.text, trigger.keyword_patterns);
1290
+ if (r.matched) {
1291
+ fired = true;
1292
+ for (const h of r.hits) {
1293
+ if (!h.negated) evidence.push(`keyword:${h.pattern}`);
1294
+ }
1295
+ }
1296
+ }
1297
+ if (trigger.enum_match) {
1298
+ for (const [enumKey, allowed] of Object.entries(trigger.enum_match)) {
1299
+ const provided = input.enums[enumKey] ?? [];
1300
+ for (const v of provided) {
1301
+ if (allowed.includes(v)) {
1302
+ fired = true;
1303
+ evidence.push(`enum:${enumKey}=${v}`);
1304
+ }
1305
+ }
1306
+ }
1307
+ }
1308
+ if (trigger.structured_flags) {
1309
+ for (const flag of trigger.structured_flags) {
1310
+ if (input.flags.includes(flag)) {
1311
+ fired = true;
1312
+ evidence.push(`flag:${flag}`);
1313
+ }
1314
+ }
1315
+ }
1316
+ return { fired, evidence };
1317
+ }
1318
+
1319
+ // src/core/infer-obligations.ts
1320
+ function inferAiActObligations(bundle, input) {
1321
+ const out = {
1322
+ firedCategories: [],
1323
+ gpaiObligations: [],
1324
+ rationale_per_rule: []
1325
+ };
1326
+ for (const cat of bundle.annexIII.categories) {
1327
+ const r = evaluateTrigger(cat.triggers, input);
1328
+ out.rationale_per_rule.push({
1329
+ rule_id: `ai-act-annex-iii.${cat.id}`,
1330
+ rule_version: bundle.annexIII.version,
1331
+ fired: r.fired,
1332
+ evidence: r.evidence.join("; ")
1333
+ });
1334
+ if (r.fired) {
1335
+ out.firedCategories.push({
1336
+ id: cat.id,
1337
+ title: cat.title,
1338
+ obligations: cat.obligations,
1339
+ fiches: cat.fiches,
1340
+ confidence_base: cat.confidence_base,
1341
+ evidence: r.evidence,
1342
+ source: "annex-iii"
1343
+ });
1344
+ }
1345
+ }
1346
+ for (const cat of bundle.annexI.categories) {
1347
+ const r = evaluateTrigger(cat.triggers, input);
1348
+ out.rationale_per_rule.push({
1349
+ rule_id: `ai-act-annex-i.${cat.id}`,
1350
+ rule_version: bundle.annexI.version,
1351
+ fired: r.fired,
1352
+ evidence: r.evidence.join("; ")
1353
+ });
1354
+ if (r.fired) {
1355
+ out.firedCategories.push({
1356
+ id: cat.id,
1357
+ title: cat.title,
1358
+ obligations: cat.obligations,
1359
+ fiches: cat.fiches,
1360
+ confidence_base: cat.confidence_base,
1361
+ evidence: r.evidence,
1362
+ source: "annex-i"
1363
+ });
1364
+ }
1365
+ }
1366
+ const gpaiR = evaluateTrigger(bundle.gpai.triggers, input);
1367
+ out.rationale_per_rule.push({
1368
+ rule_id: "gpai-triggers",
1369
+ rule_version: bundle.gpai.version,
1370
+ fired: gpaiR.fired,
1371
+ evidence: gpaiR.evidence.join("; ")
1372
+ });
1373
+ if (gpaiR.fired) {
1374
+ out.gpaiObligations = bundle.gpai.obligations.filter((o) => !o.systemic_risk_only).map((o) => ({
1375
+ article: o.article,
1376
+ requirement: o.requirement,
1377
+ applicable_date: o.applicable_date
1378
+ }));
1379
+ }
1380
+ return out;
1381
+ }
1382
+
1383
+ // src/core/infer-roles.ts
1384
+ function inferAiActRole(rules, input) {
1385
+ for (const r of rules.rules) {
1386
+ const fired = evaluateTrigger(r.triggers, input);
1387
+ if (fired.fired) {
1388
+ return {
1389
+ role: r.role,
1390
+ confidence: r.confidence_base,
1391
+ rationale: r.rationale_template,
1392
+ rule_id: `ai-act-roles.${r.id}`,
1393
+ evidence: fired.evidence
1394
+ };
1395
+ }
1396
+ }
1397
+ return {
1398
+ role: "not_applicable",
1399
+ confidence: "low",
1400
+ rationale: "No AI Act role rule fired with the declared inputs. Defaulting to not_applicable \u2014 confirm manually.",
1401
+ rule_id: "ai-act-roles.default-na",
1402
+ evidence: []
1403
+ };
1404
+ }
1405
+ function inferGdprRole(rules, input) {
1406
+ for (const c of rules.cases) {
1407
+ const fired = evaluateTrigger(c.triggers, input);
1408
+ if (fired.fired) {
1409
+ return {
1410
+ role: c.role,
1411
+ confidence: c.confidence_base,
1412
+ rationale: c.rationale_template,
1413
+ rule_id: `gdpr-qualification.${c.id}`,
1414
+ evidence: fired.evidence
1415
+ };
1416
+ }
1417
+ }
1418
+ return {
1419
+ role: "not_applicable",
1420
+ confidence: "low",
1421
+ rationale: "No GDPR qualification rule fired; defaulting to not_applicable \u2014 confirm manually whether any PII transits the agent.",
1422
+ rule_id: "gdpr-qualification.default-na",
1423
+ evidence: []
1424
+ };
1425
+ }
1426
+
1427
+ // src/core/infer-sign-off.ts
1428
+ var DEFAULT = {
1429
+ security: false,
1430
+ privacy: false,
1431
+ compliance: false,
1432
+ legal: false,
1433
+ business_sponsor: true,
1434
+ board: false
1435
+ };
1436
+ function inferSignOff(matrix, input) {
1437
+ for (const rule of matrix.rules) {
1438
+ if (rule.criticality === input.criticality && rule.personal_data_level === input.personal_data_level && rule.financial_exposure === input.financial_exposure) {
1439
+ return {
1440
+ required: rule.required,
1441
+ rule_id: `sign-off-matrix.${rule.criticality}-${rule.personal_data_level}-${rule.financial_exposure}`
1442
+ };
1443
+ }
1444
+ }
1445
+ for (const rule of matrix.rules) {
1446
+ if (rule.criticality === input.criticality) {
1447
+ return {
1448
+ required: rule.required,
1449
+ rule_id: `sign-off-matrix.${rule.criticality}-fallback`
1450
+ };
1451
+ }
1452
+ }
1453
+ return { required: DEFAULT, rule_id: "sign-off-matrix.default" };
1454
+ }
1455
+
1456
+ // src/core/infer-controls.ts
1457
+ var LEVEL_ORDER = ["N0", "N1", "N2", "N3"];
1458
+ var RISK_ORDER = ["low", "medium", "high", "critical"];
1459
+ function clampNext(order, value, delta) {
1460
+ const i = order.indexOf(value);
1461
+ const j = Math.min(order.length - 1, Math.max(0, i + delta));
1462
+ return order[j];
1463
+ }
1464
+ function inferDdaoControls(mapping, input) {
1465
+ const candidates = [
1466
+ { level: input.level, risk: input.risk },
1467
+ { level: input.level, risk: clampNext(RISK_ORDER, input.risk, 1) },
1468
+ { level: clampNext(LEVEL_ORDER, input.level, 1), risk: input.risk },
1469
+ { level: input.level, risk: clampNext(RISK_ORDER, input.risk, -1) },
1470
+ { level: clampNext(LEVEL_ORDER, input.level, -1), risk: input.risk }
1471
+ ];
1472
+ for (const c of candidates) {
1473
+ const found = mapping.mappings.find(
1474
+ (m) => m.level === c.level && m.risk === c.risk
1475
+ );
1476
+ if (found) {
1477
+ return {
1478
+ recommended_controls: found.recommended_controls,
1479
+ ddao_controls: found.ddao_controls,
1480
+ rule_id: `ddao-controls-mapping.${c.level}-${c.risk}`
1481
+ };
1482
+ }
1483
+ }
1484
+ return {
1485
+ recommended_controls: [],
1486
+ ddao_controls: [],
1487
+ rule_id: "ddao-controls-mapping.fallback-empty"
1488
+ };
1489
+ }
1490
+
1491
+ // src/core/confidence.ts
1492
+ var ORDER = ["low", "medium", "high"];
1493
+ function step(c, delta) {
1494
+ const i = ORDER.indexOf(c);
1495
+ const j = Math.min(ORDER.length - 1, Math.max(0, i + delta));
1496
+ return ORDER[j];
1497
+ }
1498
+ function aggregateConfidence(input) {
1499
+ let current = input.ruleBaseConfidence;
1500
+ if (input.enumProvided) current = step(current, 0);
1501
+ else current = step(current, -1);
1502
+ if (input.contextFieldsProvided >= 3) current = step(current, 0);
1503
+ else if (input.contextFieldsProvided === 0) current = step(current, -1);
1504
+ if (input.contradictions === 1) current = step(current, -1);
1505
+ else if (input.contradictions >= 2) current = step(current, -2);
1506
+ return current;
1507
+ }
1508
+
1509
+ // src/constants/disclaimer.ts
1510
+ var ACF_REASON_DISCLAIMER = "Preliminary qualification produced by the ACF\xAE deterministic engine. Not legal advice. Human review required. Do not use for autonomous regulatory decision-making. See doctrine_archive_url for the version of the doctrine used.";
1511
+
1512
+ // src/core/doctrine-footer.ts
1513
+ var REGULATORY_SNAPSHOT = "EU AI Act (Reg. 2024/1689, incl. Digital Omnibus deferral) + GDPR (Reg. 2016/679) + DORA (Reg. 2022/2554) + NIS2 (Dir. 2022/2555) + ISO 42001:2023 \u2014 as of 2026-06-07";
1514
+ function buildDoctrineFooter(input) {
1515
+ return {
1516
+ doctrine_version: `ACF framework v${input.frameworkVersion} / rules ${input.rulesVersion}`,
1517
+ doctrine_hash: input.contentHash,
1518
+ doctrine_archive_url: input.archiveUrl,
1519
+ regulatory_snapshot: REGULATORY_SNAPSHOT,
1520
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
1521
+ requires_human_review: true,
1522
+ disclaimer: ACF_REASON_DISCLAIMER
1523
+ };
1524
+ }
1525
+
1526
+ // src/core/tool-classify-agent.ts
1527
+ var CLASSIFY_AGENT_CONVERSION_CTA = "Generate the full auditable PDF report on https://acfstandard.com/compliance?ref=mcp";
1528
+ var PRE_GO_LIVE_ARTICLES = /* @__PURE__ */ new Set([
1529
+ "art-9",
1530
+ "art-10",
1531
+ "art-11",
1532
+ "art-13",
1533
+ "art-43",
1534
+ "art-50"
1535
+ ]);
1536
+ var ON_INCIDENT_ARTICLES = /* @__PURE__ */ new Set([
1537
+ "art-15",
1538
+ "art-72",
1539
+ "art-79"
1540
+ ]);
1541
+ async function handleClassifyAgentTool(registry, rawInput) {
1542
+ const input = ClassifyAgentInputSchema.parse(rawInput);
1543
+ const [
1544
+ autonomyRules,
1545
+ criticalityRules,
1546
+ annexIII,
1547
+ annexI,
1548
+ gpai,
1549
+ aiActRoles,
1550
+ gdprRules,
1551
+ signOffMatrix,
1552
+ controlsMapping,
1553
+ rulesMeta
1554
+ ] = await Promise.all([
1555
+ registry.rules.loadAutonomyInference(),
1556
+ registry.rules.loadCriticalityMatrix(),
1557
+ registry.rules.loadAiActAnnexIII(),
1558
+ registry.rules.loadAiActAnnexI(),
1559
+ registry.rules.loadGpaiTriggers(),
1560
+ registry.rules.loadAiActRoles(),
1561
+ registry.rules.loadGdprQualification(),
1562
+ registry.rules.loadSignOffMatrix(),
1563
+ registry.rules.loadDdaoControlsMapping(),
1564
+ registry.rules.loadRulesMeta()
1565
+ ]);
1566
+ const flags = extractFlags(input);
1567
+ const enums = extractEnumHints(input);
1568
+ const triggerInput = { text: input.description, enums, flags };
1569
+ const autonomy = inferAutonomy(autonomyRules, input);
1570
+ const criticality = inferCriticality(criticalityRules, input);
1571
+ const obligationsOut = inferAiActObligations(
1572
+ { annexIII, annexI, gpai },
1573
+ triggerInput
1574
+ );
1575
+ const aiActRole = inferAiActRole(aiActRoles, triggerInput);
1576
+ const gdprRole = inferGdprRole(gdprRules, triggerInput);
1577
+ const signOff = inferSignOff(signOffMatrix, {
1578
+ criticality: criticality.score,
1579
+ personal_data_level: input.personal_data_level,
1580
+ financial_exposure: input.financial_exposure
1581
+ });
1582
+ const controls = inferDdaoControls(controlsMapping, {
1583
+ level: autonomy.level,
1584
+ risk: criticality.score
1585
+ });
1586
+ const enumProvided = (input.ai_act_triggers?.length ?? 0) > 0 || (input.processing_purposes?.length ?? 0) > 0;
1587
+ const contextFieldsProvided = (input.sector ? 1 : 0) + (input.jurisdiction?.length ? 1 : 0) + (input.gpai_used !== void 0 ? 1 : 0) + 1;
1588
+ const contradictions = computeContradictions(input, obligationsOut);
1589
+ const confidence = aggregateConfidence({
1590
+ ruleBaseConfidence: averageConfidence(obligationsOut.firedCategories.map((c) => c.confidence_base)),
1591
+ enumProvided,
1592
+ contextFieldsProvided,
1593
+ contradictions
1594
+ });
1595
+ const assumptions = [];
1596
+ const gaps = [];
1597
+ if (!input.sector) {
1598
+ assumptions.push("sector not provided; the criticality calibration uses the neutral default.");
1599
+ gaps.push("confirm sector \u2014 banking/health/critical-infra modifiers may shift criticality.");
1600
+ }
1601
+ if (!input.jurisdiction || input.jurisdiction.length === 0) {
1602
+ assumptions.push("jurisdiction not provided; AI Act + GDPR reasoning assumes EU.");
1603
+ gaps.push("confirm jurisdiction(s) \u2014 non-EU jurisdictions may require entirely different qualifications.");
1604
+ }
1605
+ if (!enumProvided) {
1606
+ assumptions.push("ai_act_triggers and processing_purposes inferred from description (free-text matching).");
1607
+ gaps.push("validate inferred AI Act triggers with a human reviewer before relying on the qualification.");
1608
+ }
1609
+ const fiches = collectFiches(autonomy.level, criticality.score, obligationsOut.firedCategories);
1610
+ const footer = buildDoctrineFooter({
1611
+ frameworkVersion: registry.meta.framework_version,
1612
+ rulesVersion: rulesMeta.rules_version,
1613
+ contentHash: registry.meta.content_hash,
1614
+ archiveUrl: registry.meta.permanent_archive_url
1615
+ });
1616
+ const rationale = [
1617
+ ...obligationsOut.rationale_per_rule,
1618
+ {
1619
+ rule_id: aiActRole.rule_id,
1620
+ rule_version: rulesMeta.rules_version,
1621
+ fired: aiActRole.role !== "not_applicable",
1622
+ evidence: aiActRole.evidence.join("; ")
1623
+ },
1624
+ {
1625
+ rule_id: gdprRole.rule_id,
1626
+ rule_version: rulesMeta.rules_version,
1627
+ fired: gdprRole.role !== "not_applicable",
1628
+ evidence: gdprRole.evidence.join("; ")
1629
+ },
1630
+ {
1631
+ rule_id: signOff.rule_id,
1632
+ rule_version: rulesMeta.rules_version,
1633
+ fired: true,
1634
+ evidence: `criticality=${criticality.score}, pii=${input.personal_data_level}, fin=${input.financial_exposure}`
1635
+ },
1636
+ {
1637
+ rule_id: controls.rule_id,
1638
+ rule_version: rulesMeta.rules_version,
1639
+ fired: controls.recommended_controls.length > 0,
1640
+ evidence: `level=${autonomy.level}, risk=${criticality.score}`
1641
+ },
1642
+ {
1643
+ rule_id: autonomy.rule_id,
1644
+ rule_version: rulesMeta.rules_version,
1645
+ fired: true,
1646
+ evidence: `human_approval=${input.human_approval_required}, ext=${input.external_actions}`
1647
+ },
1648
+ {
1649
+ rule_id: criticality.rule_id,
1650
+ rule_version: rulesMeta.rules_version,
1651
+ fired: true,
1652
+ evidence: `pii=${input.personal_data_level}, fin=${input.financial_exposure}`
1653
+ }
1654
+ ];
1655
+ return {
1656
+ acf_level: { level: autonomy.level, rationale: autonomy.rationale },
1657
+ criticality: {
1658
+ score: criticality.score,
1659
+ rationale: criticality.rationale,
1660
+ matrix_ref: criticality.matrix_ref
1661
+ },
1662
+ regulatory_qualifications: {
1663
+ likely_ai_act_role: aiActRole.role,
1664
+ likely_gdpr_status: gdprRole.role
1665
+ },
1666
+ regulatory_qualifications_confidence: {
1667
+ likely_ai_act_role: aiActRole.confidence,
1668
+ likely_gdpr_status: gdprRole.confidence
1669
+ },
1670
+ ai_act_obligations: groupObligationsByPhase(obligationsOut, gpai.obligations),
1671
+ applicable_fiches: fiches,
1672
+ recommended_controls: controls.recommended_controls.map((c) => c.title),
1673
+ ddao_controls: controls.ddao_controls.map((c) => `${c.control_id} (${c.control_type}, ${c.fiche_reference}): ${c.implementation_note}`),
1674
+ ddao_escalation: {
1675
+ required: criticality.score === "high" || criticality.score === "critical",
1676
+ trigger_thresholds: deriveEscalationThresholds(autonomy.level, criticality.score)
1677
+ },
1678
+ sign_off_required: signOff.required,
1679
+ confidence,
1680
+ assumptions,
1681
+ gaps_to_validate: gaps,
1682
+ requires_human_review: true,
1683
+ rationale_per_rule: rationale,
1684
+ ...footer,
1685
+ conversion_cta: CLASSIFY_AGENT_CONVERSION_CTA,
1686
+ disclaimer: ACF_REASON_DISCLAIMER
1687
+ };
1688
+ }
1689
+ function averageConfidence(confs) {
1690
+ if (confs.length === 0) return "medium";
1691
+ const score = confs.reduce((acc, c) => acc + (c === "low" ? 0 : c === "medium" ? 1 : 2), 0) / confs.length;
1692
+ return score < 0.66 ? "low" : score < 1.34 ? "medium" : "high";
1693
+ }
1694
+ function computeContradictions(input, obligations) {
1695
+ let n = 0;
1696
+ if ((input.ai_act_triggers?.includes("none") ?? false) && obligations.firedCategories.length > 0) {
1697
+ n += 1;
1698
+ }
1699
+ return n;
1700
+ }
1701
+ function groupObligationsByPhase(obligationsOut, gpaiAll) {
1702
+ const pre = [];
1703
+ const continuous = [];
1704
+ const incident = [];
1705
+ const flatObligations = /* @__PURE__ */ new Set();
1706
+ for (const cat of obligationsOut.firedCategories) {
1707
+ for (const o of cat.obligations) flatObligations.add(o);
1708
+ }
1709
+ for (const article of flatObligations) {
1710
+ const item = {
1711
+ article,
1712
+ requirement: ARTICLE_REQUIREMENT_BY_ID[article] ?? `See AI Act ${article}`,
1713
+ applicable_date: "2027-12-02"
1714
+ };
1715
+ if (PRE_GO_LIVE_ARTICLES.has(article)) pre.push(item);
1716
+ else if (ON_INCIDENT_ARTICLES.has(article)) incident.push(item);
1717
+ else continuous.push(item);
1718
+ }
1719
+ for (const o of obligationsOut.gpaiObligations) {
1720
+ if (PRE_GO_LIVE_ARTICLES.has(o.article)) pre.push(o);
1721
+ else continuous.push(o);
1722
+ }
1723
+ return { pre_go_live: pre, continuous, on_incident: incident };
1724
+ }
1725
+ var ARTICLE_REQUIREMENT_BY_ID = {
1726
+ "art-5": "Prohibited practices screening.",
1727
+ "art-9": "Establish, implement and document a risk management system.",
1728
+ "art-10": "Data governance, training/validation/testing data sets quality.",
1729
+ "art-11": "Technical documentation.",
1730
+ "art-12": "Record-keeping / automated logging.",
1731
+ "art-13": "Transparency and provision of information to deployers / users.",
1732
+ "art-14": "Effective human oversight design and operation.",
1733
+ "art-15": "Accuracy, robustness, cybersecurity.",
1734
+ "art-26": "Deployer obligations: instructions, monitoring, fundamental rights impact assessment.",
1735
+ "art-27": "Fundamental rights impact assessment for high-risk AI deployers.",
1736
+ "art-43": "Conformity assessment for Annex I product-safety scope.",
1737
+ "art-50": "Transparency notice when interacting with AI / GPAI.",
1738
+ "art-72": "Post-market monitoring system.",
1739
+ "art-79": "Serious incident reporting to authorities.",
1740
+ "art-86": "Right to explanation of individual decision-making."
1741
+ };
1742
+ function deriveEscalationThresholds(level, risk) {
1743
+ const out = [];
1744
+ if (level === "N0" || level === "N1") {
1745
+ out.push("Any rejection of human-validated action escalates to DDAO.");
1746
+ }
1747
+ if (level === "N2") {
1748
+ out.push("Action above bounded perimeter escalates to DDAO before execution.");
1749
+ out.push("Drift > 10% on key metric vs baseline escalates within 24h.");
1750
+ }
1751
+ if (level === "N3") {
1752
+ out.push("Any out-of-perimeter action escalates immediately (block + human review).");
1753
+ out.push("Drift > 5% on any key metric triggers a kill-switch evaluation.");
1754
+ out.push("3 consecutive incidents within 24h auto-suspends the agent.");
1755
+ }
1756
+ if (risk === "critical") {
1757
+ out.push("Any incident with regulatory exposure escalates to board + legal within 4h.");
1758
+ }
1759
+ return out;
1760
+ }
1761
+ function collectFiches(level, risk, firedCategories) {
1762
+ const fiches = /* @__PURE__ */ new Map();
1763
+ if (level === "N2" || level === "N3") {
1764
+ fiches.set("ACF-12", "Formal mandate required for N2+ autonomy.");
1765
+ fiches.set("ACF-07", "Kill switch required for N2+ autonomy.");
1766
+ fiches.set("ACF-05", "Decision register required for N2+ autonomy.");
1767
+ }
1768
+ if (level === "N3") {
1769
+ fiches.set("ACF-14", "Monthly human-takeover drill required for N3.");
1770
+ fiches.set("ACF-08", "Real-time observability required for N3.");
1771
+ }
1772
+ if (risk === "high" || risk === "critical") {
1773
+ fiches.set("ACF-02", "Criticality matrix grounds the high-risk scoring.");
1774
+ fiches.set("ACF-09", "Escalation thresholds required for high+criticality.");
1775
+ fiches.set("ACF-11", "Risk assessment required for high+criticality.");
1776
+ }
1777
+ for (const cat of firedCategories) {
1778
+ for (const f of cat.fiches) {
1779
+ if (!fiches.has(f)) fiches.set(f, "Mobilised by an AI Act category that fired on the agent description.");
1780
+ }
1781
+ }
1782
+ return [...fiches.entries()].map(([code, why]) => ({ code, why }));
1783
+ }
1784
+ var AdvisorInputSchema = z.object({
1785
+ case_description: z.string().min(20).max(2e3),
1786
+ sector: z.string().max(80).optional(),
1787
+ jurisdiction: z.enum(["eu", "uk", "us", "ca", "ch", "br", "jp", "other"]).optional(),
1788
+ deployment_scale: z.enum(["pilot", "department", "enterprise", "public"]).optional(),
1789
+ locale: AcfLocaleSchema.optional()
1790
+ });
1791
+ var ADVISOR_CONVERSION_CTA = "Continue this assessment with the auditable ACF\xAE Compliance workspace at https://acfstandard.com/compliance?ref=mcp";
1792
+ async function handleAdvisorTool(registry, rawInput) {
1793
+ const input = AdvisorInputSchema.parse(rawInput);
1794
+ const [autonomyRules, criticalityRules, annexIII, annexI, gpai, rulesMeta] = await Promise.all([
1795
+ registry.rules.loadAutonomyInference(),
1796
+ registry.rules.loadCriticalityMatrix(),
1797
+ registry.rules.loadAiActAnnexIII(),
1798
+ registry.rules.loadAiActAnnexI(),
1799
+ registry.rules.loadGpaiTriggers(),
1800
+ registry.rules.loadRulesMeta()
1801
+ ]);
1802
+ const lower = input.case_description.toLowerCase();
1803
+ const inferredApproval = /(human validate|valide|sign-off)/i.test(lower) ? "always" : /(without approval|sans validation|never validate)/i.test(lower) ? "never" : "sometimes";
1804
+ const inferredExternal = /(automated send|exécute|execute|place order|envoyer)/i.test(lower) ? "limited_write" : "read_only";
1805
+ const inferredPII = /(health|sensitive|medical|biometric|santé|biométrique)/i.test(lower) ? "sensitive_special" : /(customer|client|email|user|utilisateur|name|nom)/i.test(lower) ? "standard" : "none";
1806
+ const inferredFinancial = /(eur 50k|10k|million|millions|board exposure)/i.test(lower) ? "high_corporate" : /(contract|contrat|deal)/i.test(lower) ? "medium_contract" : /(operating cost|opex|operational)/i.test(lower) ? "low_operation" : "none";
1807
+ const autonomy = inferAutonomy(autonomyRules, {
1808
+ human_approval_required: inferredApproval,
1809
+ external_actions: inferredExternal
1810
+ });
1811
+ const criticality = inferCriticality(criticalityRules, {
1812
+ personal_data_level: inferredPII,
1813
+ financial_exposure: inferredFinancial
1814
+ });
1815
+ const obligationsOut = inferAiActObligations(
1816
+ { annexIII, annexI, gpai },
1817
+ { text: input.case_description, enums: {}, flags: [] }
1818
+ );
1819
+ const principles = collectActivatedPrinciples(autonomy.level, criticality.score, obligationsOut.firedCategories.length > 0);
1820
+ const dimensions = collectCriticalDimensions(autonomy.level, criticality.score);
1821
+ const fiches = collectPriorityFiches(autonomy.level, criticality.score, obligationsOut.firedCategories);
1822
+ const articles = collectApplicableArticles(obligationsOut);
1823
+ const firstActions = deriveFirstActions(autonomy.level, criticality.score);
1824
+ const risks = deriveOperationalRisks(autonomy.level, criticality.score, obligationsOut);
1825
+ const confidence = aggregateConfidence({
1826
+ ruleBaseConfidence: "medium",
1827
+ enumProvided: false,
1828
+ contextFieldsProvided: (input.sector ? 1 : 0) + (input.jurisdiction ? 1 : 0) + (input.deployment_scale ? 1 : 0),
1829
+ contradictions: 0
1830
+ });
1831
+ const assumptions = [];
1832
+ const gaps = [];
1833
+ if (!input.sector) {
1834
+ assumptions.push("sector not provided; criticality uses neutral default.");
1835
+ gaps.push("provide sector to refine the criticality calibration.");
1836
+ }
1837
+ if (!input.jurisdiction) {
1838
+ assumptions.push("jurisdiction not provided; reasoning assumes EU.");
1839
+ gaps.push("confirm jurisdiction to keep the regulatory snapshot valid.");
1840
+ }
1841
+ assumptions.push("human_approval, external_actions and personal_data_level were inferred from the free-text description; pass classify-agent with qualified enums for higher confidence.");
1842
+ gaps.push("re-run via acf.classify-agent with structured enums for a more defensible qualification.");
1843
+ const footer = buildDoctrineFooter({
1844
+ frameworkVersion: registry.meta.framework_version,
1845
+ rulesVersion: rulesMeta.rules_version,
1846
+ contentHash: registry.meta.content_hash,
1847
+ archiveUrl: registry.meta.permanent_archive_url
1848
+ });
1849
+ const rationale = [
1850
+ ...obligationsOut.rationale_per_rule,
1851
+ {
1852
+ rule_id: autonomy.rule_id,
1853
+ rule_version: rulesMeta.rules_version,
1854
+ fired: true,
1855
+ evidence: `inferred human_approval=${inferredApproval}, external=${inferredExternal} from description`
1856
+ },
1857
+ {
1858
+ rule_id: criticality.rule_id,
1859
+ rule_version: rulesMeta.rules_version,
1860
+ fired: true,
1861
+ evidence: `inferred pii=${inferredPII}, financial=${inferredFinancial} from description`
1862
+ }
1863
+ ];
1864
+ return {
1865
+ autonomy_level: { level: autonomy.level, rationale: autonomy.rationale },
1866
+ risk_level: {
1867
+ level: criticality.score === "critical" ? "unacceptable" : criticality.score === "high" ? "high" : criticality.score === "medium" ? "medium" : "low",
1868
+ rationale: criticality.rationale
1869
+ },
1870
+ activated_principles: principles,
1871
+ critical_dimensions: dimensions,
1872
+ priority_fiches: fiches,
1873
+ applicable_articles: articles,
1874
+ first_actions: firstActions,
1875
+ operational_risks: risks,
1876
+ confidence,
1877
+ assumptions,
1878
+ gaps_to_validate: gaps,
1879
+ requires_human_review: true,
1880
+ rationale_per_rule: rationale,
1881
+ ...footer,
1882
+ conversion_cta: ADVISOR_CONVERSION_CTA,
1883
+ disclaimer: ACF_REASON_DISCLAIMER
1884
+ };
1885
+ }
1886
+ function collectActivatedPrinciples(level, risk, regFired) {
1887
+ const out = [
1888
+ { code: "P1", why: "Decision sovereignty applies to every agentic deployment, regardless of autonomy." }
1889
+ ];
1890
+ if (level === "N2" || level === "N3") {
1891
+ out.push({ code: "P3", why: "Conditional or autonomous execution requires a documented kill switch." });
1892
+ }
1893
+ if (risk === "high" || risk === "critical") {
1894
+ out.push({ code: "P2", why: "High-criticality decisions must be reconstructible after the fact." });
1895
+ out.push({ code: "P4", why: "Reinforced governance must match the criticality." });
1896
+ }
1897
+ if (regFired) out.push({ code: "P2", why: "Regulatory exposure makes traceability non-negotiable." });
1898
+ return dedup(out);
1899
+ }
1900
+ function collectCriticalDimensions(level, risk) {
1901
+ const out = [];
1902
+ if (level !== "N0") out.push({ code: "D4", why: "Role allocation (DDAO + sign-off) is required for N1+ autonomy." });
1903
+ if (risk === "high" || risk === "critical") {
1904
+ out.push({ code: "D5", why: "Regulatory compliance dimension is on the critical path." });
1905
+ out.push({ code: "D3", why: "Technical control (kill switch, observability) is non-negotiable." });
1906
+ }
1907
+ out.push({ code: "D2", why: "Doctrine adoption is the entry gate before any agentic deployment." });
1908
+ return dedup(out);
1909
+ }
1910
+ function collectPriorityFiches(level, risk, firedCategories) {
1911
+ const fiches = /* @__PURE__ */ new Map();
1912
+ let order = 1;
1913
+ fiches.set("ACF-00", { order: order++, why: "Read first: framework introduction." });
1914
+ if (level === "N2" || level === "N3") {
1915
+ fiches.set("ACF-12", { order: order++, why: "Mandate is required before go-live." });
1916
+ fiches.set("ACF-07", { order: order++, why: "Kill switch is required before go-live." });
1917
+ }
1918
+ if (risk === "high" || risk === "critical") {
1919
+ fiches.set("ACF-02", { order: order++, why: "Calibrate criticality with the matrix." });
1920
+ fiches.set("ACF-09", { order: order++, why: "Define escalation thresholds." });
1921
+ fiches.set("ACF-11", { order: order++, why: "Run a formal risk assessment." });
1922
+ }
1923
+ for (const cat of firedCategories) {
1924
+ for (const f of cat.fiches) {
1925
+ if (!fiches.has(f)) fiches.set(f, { order: order++, why: "Mobilised by a fired regulatory category." });
1926
+ }
1927
+ }
1928
+ return [...fiches.entries()].map(([code, v]) => ({ code, order: v.order, why: v.why }));
1929
+ }
1930
+ function collectApplicableArticles(obligations) {
1931
+ const out = [];
1932
+ const seen = /* @__PURE__ */ new Set();
1933
+ for (const cat of obligations.firedCategories) {
1934
+ for (const article of cat.obligations) {
1935
+ const key = `ai-act:${article}`;
1936
+ if (!seen.has(key)) {
1937
+ seen.add(key);
1938
+ out.push({ regulation: "ai-act", article, why: `Mobilised by category ${cat.title}.` });
1939
+ }
1940
+ }
1941
+ }
1942
+ for (const g of obligations.gpaiObligations) {
1943
+ const key = `ai-act:${g.article}`;
1944
+ if (!seen.has(key)) {
1945
+ seen.add(key);
1946
+ out.push({ regulation: "ai-act", article: g.article, why: "GPAI obligation." });
1947
+ }
1948
+ }
1949
+ return out;
1950
+ }
1951
+ function deriveFirstActions(level, risk) {
1952
+ const out = [
1953
+ "Identify or appoint the DDAO accountable for the agent (cf. ACF-12).",
1954
+ "Document the agent constitution (decision perimeter, allowed/forbidden actions \u2014 cf. ACF-03).",
1955
+ "Open the decision register and define the retention policy (cf. ACF-05 + ACF-13)."
1956
+ ];
1957
+ if (level === "N2" || level === "N3") {
1958
+ out.push("Design, implement and test the kill switch (cf. ACF-07).");
1959
+ }
1960
+ if (risk === "high" || risk === "critical") {
1961
+ out.push("Run the formal risk assessment + DPIA if PII (cf. ACF-11).");
1962
+ }
1963
+ return out.slice(0, 5);
1964
+ }
1965
+ function deriveOperationalRisks(level, risk, obligations) {
1966
+ const out = [];
1967
+ if (level === "N3") out.push("Drift goes undetected if observability is not real-time.");
1968
+ if (risk === "high" || risk === "critical") {
1969
+ out.push("Audit chain breaks if the decision register is not immutable.");
1970
+ out.push("Sign-off bottleneck if DDAO is not available in escalation SLA.");
1971
+ }
1972
+ if (obligations.firedCategories.length > 0) {
1973
+ out.push("Regulatory exposure escalates if Article 49 register is not maintained in sync with internal register.");
1974
+ }
1975
+ return out;
1976
+ }
1977
+ function dedup(arr) {
1978
+ const seen = /* @__PURE__ */ new Set();
1979
+ const out = [];
1980
+ for (const e of arr) {
1981
+ if (!seen.has(e.code)) {
1982
+ seen.add(e.code);
1983
+ out.push(e);
1984
+ }
1985
+ }
1986
+ return out;
1987
+ }
1988
+ var AssessAutonomyInputSchema = z.object({
1989
+ agent_description: z.string().min(10).max(1500),
1990
+ intended_actions: z.array(z.string()).min(1).max(20),
1991
+ reversibility: z.enum(["fully", "partially", "irreversible"]),
1992
+ audit_requirements: z.enum(["none", "internal", "regulatory", "forensic"]),
1993
+ human_in_loop_cost: z.enum(["low", "medium", "high"]).optional(),
1994
+ locale: AcfLocaleSchema.optional()
1995
+ });
1996
+ async function handleAssessAutonomyTool(registry, rawInput) {
1997
+ const input = AssessAutonomyInputSchema.parse(rawInput);
1998
+ const rulesMeta = await registry.rules.loadRulesMeta();
1999
+ let level = "N1";
2000
+ let rationale = "Default baseline for AI agents with bounded responsibility.";
2001
+ if (input.audit_requirements === "regulatory" || input.audit_requirements === "forensic") {
2002
+ level = "N1";
2003
+ rationale = "Regulatory-audit obligations push the recommended baseline to N1 (supervised) at a minimum. Consider N0 if traceability is uncertain.";
2004
+ }
2005
+ if (input.reversibility === "irreversible" && (input.audit_requirements === "regulatory" || input.audit_requirements === "forensic")) {
2006
+ level = "N2";
2007
+ rationale = "Irreversible actions combined with regulatory-audit obligations require at minimum N2 (bounded autonomy) with strong human oversight checkpoints.";
2008
+ }
2009
+ if (input.audit_requirements === "internal" && input.reversibility === "fully") {
2010
+ if (input.intended_actions.every((a) => /search|summari|propose|suggest/i.test(a))) {
2011
+ level = "N0";
2012
+ rationale = "All intended actions are reversible suggestions or read-only; N0 (assistance) is the right baseline.";
2013
+ }
2014
+ }
2015
+ if (input.reversibility === "partially" && input.audit_requirements === "internal") {
2016
+ level = "N2";
2017
+ rationale = "Partial reversibility with internal audit allows bounded N2 execution within a documented mandate.";
2018
+ }
2019
+ if (input.reversibility !== "irreversible" && input.audit_requirements === "none" && input.human_in_loop_cost === "high") {
2020
+ level = "N3";
2021
+ rationale = "High human-in-loop cost and reversible actions allow N3 autonomy with strong observability + kill switch.";
2022
+ }
2023
+ const go_no_go = [
2024
+ { criterion: "Mandate signed by the named DDAO", status: level === "N0" ? "pass" : "conditional" },
2025
+ { criterion: "Kill switch documented and tested", status: level === "N0" ? "pass" : "fail" },
2026
+ { criterion: "Decision register format defined", status: level === "N0" ? "pass" : "conditional" },
2027
+ { criterion: "Escalation thresholds named in numeric terms", status: level === "N0" ? "pass" : "fail" },
2028
+ { criterion: "Sign-off from DPO if PII transits the agent", status: "conditional" }
2029
+ ];
2030
+ const gating_thresholds = [
2031
+ { condition: "Any action above bounded perimeter", escalation: "Block + DDAO ack before execution" },
2032
+ { condition: "Drift > 10% on key metric vs baseline", escalation: "Auto-suspend within 24h + post-mortem" },
2033
+ { condition: "3 consecutive incidents in 24h", escalation: "Auto-suspend + immediate DDAO review" }
2034
+ ];
2035
+ if (level === "N3") {
2036
+ gating_thresholds.push({
2037
+ condition: "Any out-of-perimeter action",
2038
+ escalation: "Block + kill switch evaluation within 5 minutes"
2039
+ });
2040
+ }
2041
+ const kill_switch_design = {
2042
+ levels: ["freeze (instant)", "redirect (\u22645 min)", "revoke (\u22641 h)"],
2043
+ response_time_s: [5, 300, 3600]
2044
+ };
2045
+ const referenced_fiches = level === "N0" ? ["ACF-00", "ACF-05"] : level === "N1" ? ["ACF-05", "ACF-09", "ACF-12"] : level === "N2" ? ["ACF-07", "ACF-09", "ACF-12", "ACF-05"] : ["ACF-07", "ACF-08", "ACF-12", "ACF-14"];
2046
+ const footer = buildDoctrineFooter({
2047
+ frameworkVersion: registry.meta.framework_version,
2048
+ rulesVersion: rulesMeta.rules_version,
2049
+ contentHash: registry.meta.content_hash,
2050
+ archiveUrl: registry.meta.permanent_archive_url
2051
+ });
2052
+ return {
2053
+ recommended_level: { level, rationale },
2054
+ go_no_go_criteria: go_no_go,
2055
+ gating_thresholds,
2056
+ kill_switch_design,
2057
+ referenced_fiches,
2058
+ confidence: aggregateConfidence({
2059
+ ruleBaseConfidence: "medium",
2060
+ enumProvided: true,
2061
+ contextFieldsProvided: input.human_in_loop_cost ? 1 : 0,
2062
+ contradictions: 0
2063
+ }),
2064
+ assumptions: ["Inference is deterministic over the 4 supplied dimensions; sector calibration not included."],
2065
+ gaps_to_validate: [
2066
+ "Confirm whether any intended action handles PII (Article 35 GDPR DPIA may apply).",
2067
+ "Confirm whether the kill switch has been tested in the last quarter."
2068
+ ],
2069
+ requires_human_review: true,
2070
+ rationale_per_rule: [
2071
+ {
2072
+ rule_id: "assess-autonomy.decision-tree",
2073
+ rule_version: rulesMeta.rules_version,
2074
+ fired: true,
2075
+ evidence: `reversibility=${input.reversibility}, audit=${input.audit_requirements}, hil_cost=${input.human_in_loop_cost ?? "n/a"}`
2076
+ }
2077
+ ],
2078
+ ...footer,
2079
+ disclaimer: ACF_REASON_DISCLAIMER
2080
+ };
2081
+ }
2082
+ var IdentifyGapsInputSchema = z.object({
2083
+ current_inventory: z.object({
2084
+ ai_systems_count: z.number().int().min(0),
2085
+ high_risk_count: z.number().int().min(0).optional(),
2086
+ gpai_used: z.boolean().optional(),
2087
+ shadow_ai_known: z.boolean().optional()
2088
+ }),
2089
+ current_processes: z.array(
2090
+ z.object({
2091
+ process: z.string(),
2092
+ exists: z.boolean(),
2093
+ documented: z.boolean().optional()
2094
+ })
2095
+ ),
2096
+ sector: z.string().max(80).optional(),
2097
+ locale: AcfLocaleSchema.optional()
2098
+ });
2099
+ var REQUIRED_PROCESSES_BY_DIM = {
2100
+ D1: ["ai_committee", "executive_sponsor"],
2101
+ D2: ["ai_inventory", "doctrine_published"],
2102
+ D3: ["kill_switch_drill", "observability"],
2103
+ D4: ["ddao_appointed", "raci"],
2104
+ D5: ["dpia", "article_49_register", "ai_act_qualification"],
2105
+ D6: ["annual_audit", "incident_review"]
2106
+ };
2107
+ async function handleIdentifyGapsTool(registry, rawInput) {
2108
+ const input = IdentifyGapsInputSchema.parse(rawInput);
2109
+ const rulesMeta = await registry.rules.loadRulesMeta();
2110
+ const processMap = /* @__PURE__ */ new Map();
2111
+ for (const p of input.current_processes) processMap.set(p.process, p);
2112
+ const dims = {};
2113
+ const gaps = [];
2114
+ for (const [dim, required] of Object.entries(REQUIRED_PROCESSES_BY_DIM)) {
2115
+ let score = 100;
2116
+ for (const proc of required) {
2117
+ const found = processMap.get(proc);
2118
+ if (found && !found.exists) {
2119
+ score -= Math.floor(100 / required.length);
2120
+ const severity = dim === "D5" ? "critical" : dim === "D3" || dim === "D4" ? "high" : "medium";
2121
+ gaps.push({
2122
+ dimension: dim,
2123
+ severity,
2124
+ description: `Process '${proc}' is missing for ${dim}.`,
2125
+ remediation: remediateProcess(proc),
2126
+ fiches: ficheFor(proc),
2127
+ estimated_effort_days: effortFor(proc)
2128
+ });
2129
+ } else if (!found) {
2130
+ score -= Math.floor(50 / required.length);
2131
+ } else if (found.documented === false) {
2132
+ score -= Math.floor(50 / required.length);
2133
+ gaps.push({
2134
+ dimension: dim,
2135
+ severity: "medium",
2136
+ description: `Process '${proc}' exists but is undocumented.`,
2137
+ remediation: `Document '${proc}' with the relevant ACF\xAE card.`,
2138
+ fiches: ficheFor(proc),
2139
+ estimated_effort_days: 2
2140
+ });
2141
+ }
2142
+ }
2143
+ dims[dim] = Math.max(0, score);
2144
+ }
2145
+ if (input.current_inventory.shadow_ai_known) {
2146
+ gaps.push({
2147
+ dimension: "D1",
2148
+ severity: "high",
2149
+ description: "Shadow AI exists in the organisation.",
2150
+ remediation: "Run a discovery campaign + classify each shadow agent via acf.classify-agent.",
2151
+ fiches: ["ACF-01"],
2152
+ estimated_effort_days: 10
2153
+ });
2154
+ }
2155
+ if (input.current_inventory.high_risk_count !== void 0 && input.current_inventory.high_risk_count > 0 && !processMap.get("dpia")?.exists) {
2156
+ gaps.push({
2157
+ dimension: "D5",
2158
+ severity: "critical",
2159
+ description: "High-risk systems present but no DPIA process.",
2160
+ remediation: "Stand up the DPIA workflow before any new high-risk go-live.",
2161
+ fiches: ["ACF-11"],
2162
+ estimated_effort_days: 5
2163
+ });
2164
+ }
2165
+ const overall = Math.round(
2166
+ Object.values(dims).reduce((acc, v) => acc + v, 0) / Object.keys(dims).length
2167
+ );
2168
+ const order = ["critical", "high", "medium", "low"];
2169
+ const priority = [...gaps].sort((a, b) => order.indexOf(a.severity) - order.indexOf(b.severity)).map((g) => `${g.dimension}: ${g.description}`);
2170
+ const quickWins = gaps.filter((g) => (g.estimated_effort_days ?? 0) <= 3).map((g) => g.description);
2171
+ const footer = buildDoctrineFooter({
2172
+ frameworkVersion: registry.meta.framework_version,
2173
+ rulesVersion: rulesMeta.rules_version,
2174
+ contentHash: registry.meta.content_hash,
2175
+ archiveUrl: registry.meta.permanent_archive_url
2176
+ });
2177
+ return {
2178
+ maturity_score: { overall, by_dimension: dims },
2179
+ gaps,
2180
+ priority_order: priority,
2181
+ quick_wins: quickWins,
2182
+ confidence: aggregateConfidence({
2183
+ ruleBaseConfidence: "medium",
2184
+ enumProvided: false,
2185
+ contextFieldsProvided: input.sector ? 1 : 0,
2186
+ contradictions: 0
2187
+ }),
2188
+ assumptions: ["Maturity baseline is unweighted across dimensions; sector-specific weights not yet calibrated."],
2189
+ gaps_to_validate: [
2190
+ "Confirm which gaps were inferred vs explicitly declared by your inventory.",
2191
+ "Run acf.classify-agent on the high-risk subset to consolidate the qualification."
2192
+ ],
2193
+ requires_human_review: true,
2194
+ rationale_per_rule: [
2195
+ {
2196
+ rule_id: "identify-gaps.dimension-checklist",
2197
+ rule_version: rulesMeta.rules_version,
2198
+ fired: true,
2199
+ evidence: `${input.current_processes.length} processes evaluated`
2200
+ }
2201
+ ],
2202
+ ...footer,
2203
+ disclaimer: ACF_REASON_DISCLAIMER
2204
+ };
2205
+ }
2206
+ function remediateProcess(p) {
2207
+ const map = {
2208
+ ai_committee: "Establish a recurring AI committee with executive sponsorship.",
2209
+ executive_sponsor: "Identify a sponsoring executive at COMEX level.",
2210
+ ai_inventory: "Stand up the AI inventory using the ACF-01 mapping template.",
2211
+ doctrine_published: "Publish a doctrine note grounded in ACF\xAE v1.0 to the relevant teams.",
2212
+ kill_switch_drill: "Run a quarterly kill-switch drill per ACF-14.",
2213
+ observability: "Wire end-to-end observability on every N2+ agent.",
2214
+ ddao_appointed: "Appoint a DDAO per N2+ agent with documented mandate.",
2215
+ raci: "Publish a RACI for agentic decisions including DDAO + DPO + CISO + sponsor.",
2216
+ dpia: "Stand up the DPIA workflow with ACF-11 as the template.",
2217
+ article_49_register: "Prepare the Article 49 register for any high-risk system before go-live.",
2218
+ ai_act_qualification: "Run acf.classify-agent on each system to qualify under the AI Act.",
2219
+ annual_audit: "Schedule an annual internal audit of agents in production.",
2220
+ incident_review: "Open a quarterly incident review forum with the AI committee."
2221
+ };
2222
+ return map[p] ?? `Document and implement ${p}.`;
2223
+ }
2224
+ function ficheFor(p) {
2225
+ const map = {
2226
+ ai_inventory: ["ACF-01"],
2227
+ decision_register: ["ACF-05"],
2228
+ kill_switch_drill: ["ACF-07", "ACF-14"],
2229
+ ddao_appointed: ["ACF-12"],
2230
+ dpia: ["ACF-11"],
2231
+ article_49_register: ["ACF-05", "ACF-11"],
2232
+ observability: ["ACF-08"]
2233
+ };
2234
+ return map[p] ?? [];
2235
+ }
2236
+ function effortFor(p) {
2237
+ const map = {
2238
+ ai_committee: 5,
2239
+ decision_register: 8,
2240
+ kill_switch_drill: 3,
2241
+ dpia: 10,
2242
+ ai_inventory: 8,
2243
+ observability: 15
2244
+ };
2245
+ return map[p] ?? 5;
2246
+ }
2247
+ var MapObligationsInputSchema = z.object({
2248
+ annex: z.enum(["iii", "i", "none"]),
2249
+ use_case: z.string().min(10).max(500),
2250
+ provider_or_deployer: z.enum(["provider", "deployer", "both"]),
2251
+ gpai_used: z.boolean().optional(),
2252
+ locale: AcfLocaleSchema.optional()
2253
+ });
2254
+ var PRE_GO_LIVE = /* @__PURE__ */ new Set(["art-9", "art-10", "art-11", "art-13", "art-43", "art-50"]);
2255
+ var ON_INCIDENT = /* @__PURE__ */ new Set(["art-15", "art-72", "art-79"]);
2256
+ var ARTICLE_DETAIL = {
2257
+ "art-9": { title: "Risk management system", requirement: "Establish, implement, document, maintain a risk management system across the lifecycle.", deadline: "2027-12-02", operational_actions: ["Stand up risk register", "Define risk methodology", "Review quarterly"], evidence_required: ["Risk register", "Methodology doc"], digital_omnibus_deferred: true },
2258
+ "art-10": { title: "Data and data governance", requirement: "Training/validation/testing data sets governance + quality requirements.", deadline: "2027-12-02", operational_actions: ["Document data sources", "Run bias evaluation", "Document data drift monitoring"], evidence_required: ["Data inventory", "Bias evaluation report"], digital_omnibus_deferred: true },
2259
+ "art-11": { title: "Technical documentation", requirement: "Maintain technical documentation per Annex IV.", deadline: "2027-12-02", operational_actions: ["Author Annex IV-aligned tech doc"], evidence_required: ["Technical documentation"], digital_omnibus_deferred: true },
2260
+ "art-12": { title: "Record-keeping", requirement: "Automated logs over the lifecycle of the system.", deadline: "2027-12-02", operational_actions: ["Wire structured logging", "Define retention policy"], evidence_required: ["Logs"], digital_omnibus_deferred: true },
2261
+ "art-13": { title: "Transparency to deployers", requirement: "Provide instructions for use that allow the deployer to interpret outputs.", deadline: "2027-12-02", operational_actions: ["Draft instructions for use", "Translate to operating languages"], evidence_required: ["Instructions for use"], digital_omnibus_deferred: true },
2262
+ "art-14": { title: "Human oversight", requirement: "Effective human oversight design + operation.", deadline: "2027-12-02", operational_actions: ["Design kill switch (ACF-07)", "Document escalation thresholds (ACF-09)"], evidence_required: ["Kill switch design", "Drill reports"], digital_omnibus_deferred: true },
2263
+ "art-15": { title: "Accuracy, robustness, cybersecurity", requirement: "Maintain accuracy, robustness, cybersecurity levels appropriate to the use.", deadline: "continuous", operational_actions: ["Define accuracy KPIs", "Run robustness tests", "Define cybersecurity controls"], evidence_required: ["KPI dashboards", "Pen test reports"], digital_omnibus_deferred: false },
2264
+ "art-26": { title: "Deployer obligations", requirement: "Instructions, monitoring, fundamental rights impact assessment.", deadline: "2027-12-02", operational_actions: ["Run fundamental rights IA", "Document monitoring plan"], evidence_required: ["FRIA report", "Monitoring plan"], digital_omnibus_deferred: true },
2265
+ "art-27": { title: "Fundamental rights impact assessment", requirement: "Mandatory FRIA for high-risk deployers in some sectors.", deadline: "2027-12-02", operational_actions: ["Author FRIA"], evidence_required: ["FRIA"], digital_omnibus_deferred: true },
2266
+ "art-43": { title: "Conformity assessment", requirement: "Conformity assessment procedure for Annex I scope.", deadline: "2028-08-02", operational_actions: ["Run conformity assessment via notified body"], evidence_required: ["CE marking"], digital_omnibus_deferred: false },
2267
+ "art-50": { title: "Transparency on AI/GPAI", requirement: "Disclose AI usage to natural persons interacting with the system.", deadline: "2026-08-02", operational_actions: ["Add disclosure on every user surface"], evidence_required: ["Disclosure copy"], digital_omnibus_deferred: false },
2268
+ "art-72": { title: "Post-market monitoring", requirement: "Establish a post-market monitoring system.", deadline: "continuous", operational_actions: ["Define post-market monitoring plan"], evidence_required: ["Monitoring reports"], digital_omnibus_deferred: false },
2269
+ "art-79": { title: "Serious incident reporting", requirement: "Report serious incidents to authorities within 15 days.", deadline: "on-incident", operational_actions: ["Define incident classification + reporting playbook"], evidence_required: ["Incident reports"], digital_omnibus_deferred: false },
2270
+ "art-86": { title: "Right to explanation", requirement: "Affected persons can request an explanation of an individual decision.", deadline: "2027-12-02", operational_actions: ["Build explanation generation flow"], evidence_required: ["Explanation templates"], digital_omnibus_deferred: true }
2271
+ };
2272
+ async function handleMapObligationsTool(registry, rawInput) {
2273
+ const input = MapObligationsInputSchema.parse(rawInput);
2274
+ const [annexIII, annexI, gpai, rulesMeta] = await Promise.all([
2275
+ registry.rules.loadAiActAnnexIII(),
2276
+ registry.rules.loadAiActAnnexI(),
2277
+ registry.rules.loadGpaiTriggers(),
2278
+ registry.rules.loadRulesMeta()
2279
+ ]);
2280
+ const obligationsOut = inferAiActObligations(
2281
+ { annexIII, annexI, gpai },
2282
+ {
2283
+ text: input.use_case,
2284
+ enums: input.gpai_used ? { gpai_used: ["true"] } : {},
2285
+ flags: []
2286
+ }
2287
+ );
2288
+ const articles = /* @__PURE__ */ new Set();
2289
+ for (const cat of obligationsOut.firedCategories) for (const a of cat.obligations) articles.add(a);
2290
+ if (input.provider_or_deployer === "deployer" || input.provider_or_deployer === "both") {
2291
+ articles.add("art-26");
2292
+ articles.add("art-27");
2293
+ }
2294
+ articles.add("art-50");
2295
+ const pre = [];
2296
+ const continuous = [];
2297
+ const incident = [];
2298
+ const ficheFor2 = (a) => {
2299
+ for (const cat of obligationsOut.firedCategories) {
2300
+ if (cat.obligations.includes(a)) return cat.fiches;
2301
+ }
2302
+ return [];
2303
+ };
2304
+ for (const a of articles) {
2305
+ const detail = ARTICLE_DETAIL[a];
2306
+ if (!detail) continue;
2307
+ const item = {
2308
+ article: a,
2309
+ title: detail.title,
2310
+ requirement: detail.requirement,
2311
+ deadline: detail.deadline,
2312
+ fiches: ficheFor2(a),
2313
+ operational_actions: detail.operational_actions,
2314
+ evidence_required: detail.evidence_required,
2315
+ digital_omnibus_deferred: detail.digital_omnibus_deferred
2316
+ };
2317
+ if (PRE_GO_LIVE.has(a)) pre.push(item);
2318
+ else if (ON_INCIDENT.has(a)) incident.push(item);
2319
+ else continuous.push(item);
2320
+ }
2321
+ for (const o of obligationsOut.gpaiObligations) {
2322
+ const item = {
2323
+ article: o.article,
2324
+ title: ARTICLE_DETAIL[o.article]?.title ?? "GPAI obligation",
2325
+ requirement: o.requirement,
2326
+ deadline: o.applicable_date,
2327
+ fiches: [],
2328
+ operational_actions: ARTICLE_DETAIL[o.article]?.operational_actions ?? [],
2329
+ evidence_required: ARTICLE_DETAIL[o.article]?.evidence_required ?? [],
2330
+ digital_omnibus_deferred: false
2331
+ };
2332
+ if (PRE_GO_LIVE.has(o.article)) pre.push(item);
2333
+ else continuous.push(item);
2334
+ }
2335
+ const total_count = pre.length + continuous.length + incident.length;
2336
+ const critical_path = pre.filter((p) => p.digital_omnibus_deferred === false).slice(0, 5).map((p) => `${p.article}: ${p.title}`);
2337
+ const footer = buildDoctrineFooter({
2338
+ frameworkVersion: registry.meta.framework_version,
2339
+ rulesVersion: rulesMeta.rules_version,
2340
+ contentHash: registry.meta.content_hash,
2341
+ archiveUrl: registry.meta.permanent_archive_url
2342
+ });
2343
+ return {
2344
+ obligations: { pre_go_live: pre, continuous, on_incident: incident },
2345
+ total_count,
2346
+ critical_path,
2347
+ confidence: aggregateConfidence({
2348
+ ruleBaseConfidence: "medium",
2349
+ enumProvided: input.annex === "iii" || input.annex === "i",
2350
+ contextFieldsProvided: input.gpai_used !== void 0 ? 1 : 0,
2351
+ contradictions: input.annex === "none" && obligationsOut.firedCategories.length > 0 ? 1 : 0
2352
+ }),
2353
+ assumptions: input.annex === "none" ? ["Caller declared no annex but at least one rule fired on the use case description; flagged below."] : [],
2354
+ gaps_to_validate: [
2355
+ "Confirm provider_or_deployer qualification \u2014 it changes the obligation set.",
2356
+ "Confirm GPAI usage \u2014 Article 50 transparency obligation depends on it."
2357
+ ],
2358
+ requires_human_review: true,
2359
+ rationale_per_rule: obligationsOut.rationale_per_rule,
2360
+ ...footer,
2361
+ disclaimer: ACF_REASON_DISCLAIMER
2362
+ };
2363
+ }
2364
+ var AssignControlsInputSchema = z.object({
2365
+ agent_description: z.string().min(10).max(500),
2366
+ acf_level: z.enum(["N0", "N1", "N2", "N3"]),
2367
+ risk_level: z.enum(["low", "medium", "high", "critical"]),
2368
+ budget_constraint: z.enum(["minimal", "standard", "comprehensive"]).optional(),
2369
+ locale: AcfLocaleSchema.optional()
2370
+ });
2371
+ var EFFORT_DAYS_BY_FREQUENCY = {
2372
+ one_time: 5,
2373
+ monthly: 1,
2374
+ quarterly: 2,
2375
+ annual: 4,
2376
+ on_event: 1
2377
+ };
2378
+ async function handleAssignControlsTool(registry, rawInput) {
2379
+ const input = AssignControlsInputSchema.parse(rawInput);
2380
+ const [mapping, rulesMeta] = await Promise.all([
2381
+ registry.rules.loadDdaoControlsMapping(),
2382
+ registry.rules.loadRulesMeta()
2383
+ ]);
2384
+ const controls = inferDdaoControls(mapping, {
2385
+ level: input.acf_level,
2386
+ risk: input.risk_level
2387
+ });
2388
+ let recommended = controls.recommended_controls;
2389
+ if (input.budget_constraint === "minimal") recommended = recommended.slice(0, 2);
2390
+ if (input.budget_constraint === "comprehensive") recommended = [...controls.recommended_controls];
2391
+ const estimatedEffort = recommended.reduce(
2392
+ (acc, c) => acc + (EFFORT_DAYS_BY_FREQUENCY[c.frequency] ?? 1),
2393
+ 0
2394
+ );
2395
+ const footer = buildDoctrineFooter({
2396
+ frameworkVersion: registry.meta.framework_version,
2397
+ rulesVersion: rulesMeta.rules_version,
2398
+ contentHash: registry.meta.content_hash,
2399
+ archiveUrl: registry.meta.permanent_archive_url
2400
+ });
2401
+ const summary = `Control set scoped to ACF\xAE ${input.acf_level} / risk=${input.risk_level} (${recommended.length} recommended controls, ${controls.ddao_controls.length} ACF-canonical controls).`;
2402
+ return {
2403
+ recommended_controls: recommended,
2404
+ ddao_controls: controls.ddao_controls,
2405
+ total_count: recommended.length + controls.ddao_controls.length,
2406
+ estimated_total_effort_days: estimatedEffort,
2407
+ ddao_summary: summary,
2408
+ confidence: aggregateConfidence({
2409
+ ruleBaseConfidence: "medium",
2410
+ enumProvided: true,
2411
+ contextFieldsProvided: input.budget_constraint ? 1 : 0,
2412
+ contradictions: 0
2413
+ }),
2414
+ assumptions: [
2415
+ "Controls are derived from the canonical level \xD7 risk mapping; sector-specific overrides not applied in V1.0."
2416
+ ],
2417
+ gaps_to_validate: [
2418
+ "Confirm DDAO availability for the proposed escalation cadence.",
2419
+ "Confirm evidence-storage location for each control before go-live."
2420
+ ],
2421
+ requires_human_review: true,
2422
+ rationale_per_rule: [
2423
+ {
2424
+ rule_id: controls.rule_id,
2425
+ rule_version: rulesMeta.rules_version,
2426
+ fired: true,
2427
+ evidence: `level=${input.acf_level}, risk=${input.risk_level}`
2428
+ }
2429
+ ],
2430
+ ...footer,
2431
+ disclaimer: ACF_REASON_DISCLAIMER
2432
+ };
2433
+ }
2434
+ var EvaluateMandateInputSchema = z.object({
2435
+ mandate_text: z.string().min(50).max(1e4),
2436
+ agent_purpose: z.string().min(10).max(500),
2437
+ deployment_context: z.string().max(500).optional(),
2438
+ locale: AcfLocaleSchema.optional()
2439
+ });
2440
+ var CHECKS = [
2441
+ {
2442
+ key: "ddao_appointed",
2443
+ area: "Identification du DDAO",
2444
+ weight: 15,
2445
+ matchers: [/\bDDAO\b[^.]*:\s*\S{3,}/i, /\bDelegated Decision Agent Officer\b[^.]*:\s*\S{3,}/i],
2446
+ remediation: "Name an actual DDAO (person or role) \u2014 placeholders like 'TBD' are not acceptable.",
2447
+ fiche_reference: "ACF-12"
2448
+ },
2449
+ {
2450
+ key: "decision_perimeter",
2451
+ area: "P\xE9rim\xE8tre de d\xE9cision",
2452
+ weight: 15,
2453
+ matchers: [/\b(decision perimeter|perimeter|périmètre)/i, /\b(allowed|autorisées?)\b/i],
2454
+ remediation: "Document the positive (allowed) and negative (forbidden) action lists.",
2455
+ fiche_reference: "ACF-03"
2456
+ },
2457
+ {
2458
+ key: "forbidden_actions",
2459
+ area: "Actions interdites",
2460
+ weight: 10,
2461
+ matchers: [/\b(forbidden|interdites?|not allowed|never)\b/i],
2462
+ remediation: "Add an explicit forbidden-actions list \u2014 without it, the agent drifts at the first uncovered case.",
2463
+ fiche_reference: "ACF-03"
2464
+ },
2465
+ {
2466
+ key: "escalation_thresholds",
2467
+ area: "Seuils d'escalade",
2468
+ weight: 15,
2469
+ matchers: [/\b(escalation|escalade)\b[^.]*\d/i, /\bthreshold|seuil/i],
2470
+ remediation: "Specify numeric and qualitative escalation thresholds.",
2471
+ fiche_reference: "ACF-09"
2472
+ },
2473
+ {
2474
+ key: "kill_switch",
2475
+ area: "Kill switch",
2476
+ weight: 15,
2477
+ matchers: [/\b(kill switch|suspension|revoke|révoquer|drill)\b/i],
2478
+ remediation: "Document the kill switch (levels, response times, drill frequency).",
2479
+ fiche_reference: "ACF-07"
2480
+ },
2481
+ {
2482
+ key: "audit_log",
2483
+ area: "Audit log / decision register",
2484
+ weight: 15,
2485
+ matchers: [/\b(audit log|decision register|registre|retention)\b/i],
2486
+ remediation: "Document the decision register format and retention.",
2487
+ fiche_reference: "ACF-05"
2488
+ },
2489
+ {
2490
+ key: "doctrine_version",
2491
+ area: "Doctrine version",
2492
+ weight: 10,
2493
+ matchers: [/\bACF®?[^.]*v\d/i, /\bdoctrine version\b/i],
2494
+ remediation: "Cite the ACF\xAE doctrine version applied (e.g. 'ACF\xAE v1.0').",
2495
+ fiche_reference: "ACF-00"
2496
+ },
2497
+ {
2498
+ key: "sign_off",
2499
+ area: "Sign-off",
2500
+ weight: 5,
2501
+ matchers: [/\b(sign-off|sign off|signature|approuvé)\b/i],
2502
+ remediation: "List the sign-off roles (DDAO + DPO if PII + CISO if attack surface + Legal if exposure).",
2503
+ fiche_reference: "ACF-12"
2504
+ }
2505
+ ];
2506
+ async function handleEvaluateMandateTool(registry, rawInput) {
2507
+ const input = EvaluateMandateInputSchema.parse(rawInput);
2508
+ const rulesMeta = await registry.rules.loadRulesMeta();
2509
+ const strengths = [];
2510
+ const gaps = [];
2511
+ const additions = [];
2512
+ let score = 0;
2513
+ for (const check of CHECKS) {
2514
+ const matched = check.matchers.every((m) => m.test(input.mandate_text));
2515
+ if (matched) {
2516
+ score += check.weight;
2517
+ strengths.push(`${check.area}: present.`);
2518
+ } else {
2519
+ gaps.push({
2520
+ area: check.area,
2521
+ severity: check.weight >= 15 ? "high" : check.weight >= 10 ? "medium" : "low",
2522
+ description: `${check.area} missing or insufficient.`
2523
+ });
2524
+ additions.push({
2525
+ section: check.area,
2526
+ suggested_text: check.remediation,
2527
+ fiche_reference: check.fiche_reference
2528
+ });
2529
+ }
2530
+ }
2531
+ const tbdHits = /\bTBD\b|\bà compléter\b|\?\?\?/i.test(input.mandate_text);
2532
+ if (tbdHits) {
2533
+ gaps.push({
2534
+ area: "Placeholders",
2535
+ severity: "critical",
2536
+ description: "Mandate contains 'TBD' / placeholders \u2014 it is not signable in this state."
2537
+ });
2538
+ score = Math.max(0, score - 20);
2539
+ }
2540
+ let verdict;
2541
+ if (score >= 80) verdict = "approve";
2542
+ else if (score >= 50) verdict = "approve_with_changes";
2543
+ else verdict = "reject";
2544
+ const footer = buildDoctrineFooter({
2545
+ frameworkVersion: registry.meta.framework_version,
2546
+ rulesVersion: rulesMeta.rules_version,
2547
+ contentHash: registry.meta.content_hash,
2548
+ archiveUrl: registry.meta.permanent_archive_url
2549
+ });
2550
+ return {
2551
+ verdict,
2552
+ rationale: verdict === "approve" ? "All canonical mandate sections are present; no critical placeholders detected." : verdict === "approve_with_changes" ? "The mandate covers the core but misses one or more sections. Address the additions below before DDAO sign-off." : "The mandate misses too many canonical sections (or contains placeholders) to be signable.",
2553
+ strengths,
2554
+ identified_gaps: gaps,
2555
+ required_additions: additions,
2556
+ reference_fiches: ["ACF-03", "ACF-07", "ACF-09", "ACF-12"],
2557
+ acf_compliance_score: score,
2558
+ confidence: aggregateConfidence({
2559
+ ruleBaseConfidence: "medium",
2560
+ enumProvided: false,
2561
+ contextFieldsProvided: input.deployment_context ? 1 : 0,
2562
+ contradictions: 0
2563
+ }),
2564
+ assumptions: ["Mandate evaluation uses an 8-check canonical baseline \u2014 sector-specific extras not yet implemented."],
2565
+ gaps_to_validate: [
2566
+ "Confirm with the DDAO that the named escalation thresholds reflect real organisational SLAs.",
2567
+ "Confirm that the kill switch drill has been run in the last quarter."
2568
+ ],
2569
+ requires_human_review: true,
2570
+ rationale_per_rule: CHECKS.map((c) => ({
2571
+ rule_id: `evaluate-mandate.${c.key}`,
2572
+ rule_version: rulesMeta.rules_version,
2573
+ fired: c.matchers.every((m) => m.test(input.mandate_text)),
2574
+ evidence: `${c.area} check`
2575
+ })),
2576
+ ...footer,
2577
+ disclaimer: ACF_REASON_DISCLAIMER
2578
+ };
2579
+ }
2580
+
2581
+ // src/core/tools.ts
2582
+ var HANDLERS = [
2583
+ {
2584
+ name: "acf.search",
2585
+ description: TOOL_DESCRIPTIONS["acf.search"],
2586
+ inputSchema: zodToJsonSchema(SearchInputSchema),
2587
+ handle: handleSearchTool
2588
+ },
2589
+ {
2590
+ name: "acf.fiche.lookup",
2591
+ description: TOOL_DESCRIPTIONS["acf.fiche.lookup"],
2592
+ inputSchema: zodToJsonSchema(FicheLookupInputSchema),
2593
+ handle: handleFicheLookupTool
2594
+ },
2595
+ {
2596
+ name: "acf.regulation.article",
2597
+ description: TOOL_DESCRIPTIONS["acf.regulation.article"],
2598
+ inputSchema: zodToJsonSchema(RegulationArticleInputSchema),
2599
+ handle: handleRegulationArticleTool
2600
+ },
2601
+ {
2602
+ name: "acf.glossary.define",
2603
+ description: TOOL_DESCRIPTIONS["acf.glossary.define"],
2604
+ inputSchema: zodToJsonSchema(GlossaryDefineInputSchema),
2605
+ handle: handleGlossaryDefineTool
2606
+ },
2607
+ {
2608
+ name: "acf.cite",
2609
+ description: TOOL_DESCRIPTIONS["acf.cite"],
2610
+ inputSchema: zodToJsonSchema(CiteInputSchema),
2611
+ handle: handleCiteTool
2612
+ },
2613
+ {
2614
+ name: "acf.classify-agent",
2615
+ description: TOOL_DESCRIPTIONS["acf.classify-agent"],
2616
+ inputSchema: zodToJsonSchema(ClassifyAgentInputSchema),
2617
+ handle: handleClassifyAgentTool
2618
+ },
2619
+ {
2620
+ name: "acf.advisor",
2621
+ description: TOOL_DESCRIPTIONS["acf.advisor"],
2622
+ inputSchema: zodToJsonSchema(AdvisorInputSchema),
2623
+ handle: handleAdvisorTool
2624
+ },
2625
+ {
2626
+ name: "acf.assess-autonomy",
2627
+ description: TOOL_DESCRIPTIONS["acf.assess-autonomy"],
2628
+ inputSchema: zodToJsonSchema(AssessAutonomyInputSchema),
2629
+ handle: handleAssessAutonomyTool
2630
+ },
2631
+ {
2632
+ name: "acf.identify-governance-gaps",
2633
+ description: TOOL_DESCRIPTIONS["acf.identify-governance-gaps"],
2634
+ inputSchema: zodToJsonSchema(IdentifyGapsInputSchema),
2635
+ handle: handleIdentifyGapsTool
2636
+ },
2637
+ {
2638
+ name: "acf.map-ai-act-obligations",
2639
+ description: TOOL_DESCRIPTIONS["acf.map-ai-act-obligations"],
2640
+ inputSchema: zodToJsonSchema(MapObligationsInputSchema),
2641
+ handle: handleMapObligationsTool
2642
+ },
2643
+ {
2644
+ name: "acf.assign-ddao-controls",
2645
+ description: TOOL_DESCRIPTIONS["acf.assign-ddao-controls"],
2646
+ inputSchema: zodToJsonSchema(AssignControlsInputSchema),
2647
+ handle: handleAssignControlsTool
2648
+ },
2649
+ {
2650
+ name: "acf.evaluate-agent-mandate",
2651
+ description: TOOL_DESCRIPTIONS["acf.evaluate-agent-mandate"],
2652
+ inputSchema: zodToJsonSchema(EvaluateMandateInputSchema),
2653
+ handle: handleEvaluateMandateTool
2654
+ }
2655
+ ];
2656
+ async function registerTools(server, registry) {
2657
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2658
+ tools: HANDLERS.map((h) => ({
2659
+ name: h.name,
2660
+ description: h.description,
2661
+ inputSchema: h.inputSchema
2662
+ }))
2663
+ }));
2664
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
2665
+ const handler = HANDLERS.find((h) => h.name === req.params.name);
2666
+ if (!handler) {
2667
+ throw new Error(`Unknown tool: ${req.params.name}`);
2668
+ }
2669
+ const result = await handler.handle(registry, req.params.arguments ?? {});
2670
+ return {
2671
+ content: [
2672
+ { type: "text", text: JSON.stringify(result, null, 2) }
2673
+ ]
2674
+ };
2675
+ });
2676
+ }
2677
+ function s(args, key) {
2678
+ const v = args[key];
2679
+ return v === void 0 || v === null ? "" : String(v);
2680
+ }
2681
+ var ACF_PROMPTS = [
2682
+ {
2683
+ name: "acf.brief-dpia",
2684
+ description: "When you need to draft a defensible DPIA \u2014 one that will hold against a CNIL inspection or a board challenge \u2014 get a structured first draft organised by ACF\xAE governance dimensions with the right legal references already in place.",
2685
+ arguments: [
2686
+ { name: "system_name", description: "Name of the AI system.", required: true },
2687
+ { name: "system_description", description: "What the system does.", required: true },
2688
+ { name: "deployment_context", description: "Geography, scale, audience.", required: true },
2689
+ { name: "high_risk_qualified", description: "Whether the system is already qualified as high-risk.", required: false }
2690
+ ],
2691
+ render: (args) => [
2692
+ "I need to draft a defensible DPIA for the following AI system:",
2693
+ "",
2694
+ `- Name: ${s(args, "system_name")}`,
2695
+ `- Description: ${s(args, "system_description")}`,
2696
+ `- Deployment context: ${s(args, "deployment_context")}`,
2697
+ `- High-risk qualified: ${s(args, "high_risk_qualified") || "to confirm"}`,
2698
+ "",
2699
+ "Load the canonical ACF\xAE doctrine first:",
2700
+ "- acf://guide/gdpr (Article 35 + DPIA methodology)",
2701
+ "- acf://fiche/ACF-11 (risk assessment template)",
2702
+ "- acf://fiche/ACF-02 (criticality matrix)",
2703
+ "- acf://fiche/ACF-05 (decision register requirement)",
2704
+ "",
2705
+ "Then produce a DPIA structured by ACF\xAE governance dimensions (D5 first), with explicit legal references (GDPR Art. 35, AI Act Art. 9/10/14 if applicable). Cite the doctrine_archive_url in the conclusion.",
2706
+ "",
2707
+ "Mark every assumption explicitly and signal what the human reviewer must confirm before submission."
2708
+ ].join("\n")
2709
+ },
2710
+ {
2711
+ name: "acf.brief-art49-register",
2712
+ description: "When you need to register a high-risk AI system in the EU AI Act Article 49 public register and you want the entry to be both compliant and consistent with your internal ACF\xAE decisions register.",
2713
+ arguments: [
2714
+ { name: "system_name", description: "Name of the AI system.", required: true },
2715
+ { name: "provider_or_deployer", description: "Role of the entity ('provider' / 'deployer').", required: true },
2716
+ { name: "annex_iii_use_case", description: "Which Annex III item applies.", required: true }
2717
+ ],
2718
+ render: (args) => [
2719
+ "I need to prepare an entry for the EU AI Act Article 49 public register.",
2720
+ "",
2721
+ `- System: ${s(args, "system_name")}`,
2722
+ `- Role: ${s(args, "provider_or_deployer")}`,
2723
+ `- Annex III use case: ${s(args, "annex_iii_use_case")}`,
2724
+ "",
2725
+ "Load:",
2726
+ "- acf://guide/ai-act (Article 49 + annex III canonical mapping)",
2727
+ "- acf://fiche/ACF-05 (internal decision register format)",
2728
+ "- acf://fiche/ACF-11 (risk assessment)",
2729
+ "",
2730
+ "Then produce the Article 49 entry in JSON, ensuring it is consistent with the internal ACF\xAE decision register (same identifiers, same scope). Flag any inconsistency that must be resolved before submission."
2731
+ ].join("\n")
2732
+ },
2733
+ {
2734
+ name: "acf.brief-board-report",
2735
+ description: "When the Board asks for a quarterly AI governance report and you want a presentable draft \u2014 executive style, hard numbers, risk heatmap, remediation status \u2014 that you can hand over without rewriting.",
2736
+ arguments: [
2737
+ { name: "quarter", description: "Reporting quarter (e.g. 'Q3 2026').", required: true },
2738
+ { name: "ai_systems_count", description: "Number of AI systems in production.", required: true },
2739
+ { name: "high_risk_count", description: "Number of high-risk systems.", required: false },
2740
+ { name: "open_remediations", description: "Number of open remediation items.", required: false },
2741
+ { name: "context_notes", description: "Free text \u2014 major events of the quarter.", required: false }
2742
+ ],
2743
+ render: (args) => [
2744
+ "I need a presentable AI governance report for the Board.",
2745
+ "",
2746
+ `- Quarter: ${s(args, "quarter")}`,
2747
+ `- AI systems in production: ${s(args, "ai_systems_count")}`,
2748
+ `- High-risk systems: ${s(args, "high_risk_count") || "TBD"}`,
2749
+ `- Open remediations: ${s(args, "open_remediations") || "TBD"}`,
2750
+ `- Context: ${s(args, "context_notes") || ""}`,
2751
+ "",
2752
+ "Load:",
2753
+ "- acf://framework/principle/P2 (traceability)",
2754
+ "- acf://framework/principle/P4 (proportional governance)",
2755
+ "- acf://framework/dimension/D5 (regulatory compliance)",
2756
+ "- acf://framework/dimension/D6 (audit & continuous improvement)",
2757
+ "",
2758
+ "Then produce a 1-page executive draft: portfolio health, criticality distribution (refer to acf://fiche/ACF-02), incidents + remediation status, regulatory delta (AI Act Annex III countdown), recommendations."
2759
+ ].join("\n")
2760
+ },
2761
+ {
2762
+ name: "acf.brief-ddao-mandate",
2763
+ description: "When you are about to deploy an autonomous AI agent and your CISO / DPO / Legal needs a signed mandate that documents the decision perimeter, the escalation thresholds, the kill switch and the audit trail \u2014 before go-live.",
2764
+ arguments: [
2765
+ { name: "agent_name", description: "Name of the agent.", required: true },
2766
+ { name: "agent_purpose", description: "What the agent does.", required: true },
2767
+ { name: "decision_perimeter", description: "Allowed and forbidden actions.", required: true },
2768
+ { name: "escalation_thresholds", description: "Escalation thresholds.", required: true }
2769
+ ],
2770
+ render: (args) => [
2771
+ "I need a signable DDAO mandate.",
2772
+ "",
2773
+ `- Agent name: ${s(args, "agent_name")}`,
2774
+ `- Purpose: ${s(args, "agent_purpose")}`,
2775
+ `- Decision perimeter: ${s(args, "decision_perimeter")}`,
2776
+ `- Escalation thresholds: ${s(args, "escalation_thresholds")}`,
2777
+ "",
2778
+ "Load:",
2779
+ "- acf://framework/ddao",
2780
+ "- acf://fiche/ACF-12 (mandate template)",
2781
+ "- acf://fiche/ACF-03 (constitution)",
2782
+ "- acf://fiche/ACF-07 (kill switch)",
2783
+ "- acf://fiche/ACF-09 (escalation thresholds)",
2784
+ "",
2785
+ "Then produce a mandate document in 7 sections (Identification, Decision perimeter, Forbidden actions, Escalation thresholds, Kill switch, Audit log, Doctrine version) ready for DDAO + DPO + CISO sign-off."
2786
+ ].join("\n")
2787
+ },
2788
+ {
2789
+ name: "acf.brief-classify-system",
2790
+ description: "When you receive an AI system to classify against the EU AI Act and you need a defensible qualification \u2014 Article 5 screening, Annex III check, Annex I check, GPAI test, conclusion with a justification that holds up to a regulator's challenge.",
2791
+ arguments: [
2792
+ { name: "system_description", description: "What the system does.", required: true },
2793
+ { name: "sector", description: "Sector of operation.", required: true },
2794
+ { name: "personal_data", description: "Does it process personal data?", required: false }
2795
+ ],
2796
+ render: (args) => [
2797
+ "I need a defensible AI Act qualification.",
2798
+ "",
2799
+ `- System: ${s(args, "system_description")}`,
2800
+ `- Sector: ${s(args, "sector")}`,
2801
+ `- Personal data: ${s(args, "personal_data") || "to confirm"}`,
2802
+ "",
2803
+ "Load:",
2804
+ "- acf://guide/ai-act",
2805
+ "- acf://fiche/ACF-02 (criticality)",
2806
+ "- acf://fiche/ACF-11 (risk assessment)",
2807
+ "",
2808
+ "Then run the canonical screening (Art. 5 prohibitions \u2192 Annex III \u2192 Annex I \u2192 GPAI test \u2192 transparency-only Art. 50) and conclude with a paragraph that lists the obligations and the operational consequences. If a tool would help, call acf.classify-agent with the structured input."
2809
+ ].join("\n")
2810
+ },
2811
+ {
2812
+ name: "acf.brief-teacher-case",
2813
+ description: "When you need to prepare a teaching case for a master's class, a bar school session or a CPD seminar \u2014 one calibrated to the ACF\xAE V1.0 quality bar (named characters, precise legal refs, bold-answer bonus, 1-3 useful pages max) \u2014 get a ready-to-edit case generated from your angle.",
2814
+ arguments: [
2815
+ { name: "topic", description: "Pedagogical angle (e.g. 'mandate drafting').", required: true },
2816
+ { name: "audience", description: "Audience (e.g. 'L3 droit', 'EMBA').", required: true },
2817
+ { name: "duration_minutes", description: "Duration in minutes (15-180).", required: false }
2818
+ ],
2819
+ render: (args) => [
2820
+ "I need a calibrated ACF\xAE V1.0 teaching case.",
2821
+ "",
2822
+ `- Topic: ${s(args, "topic")}`,
2823
+ `- Audience: ${s(args, "audience")}`,
2824
+ `- Duration: ${s(args, "duration_minutes") || "60"} minutes`,
2825
+ "",
2826
+ "Load:",
2827
+ "- acf://fiche/ACF-00 (introduction)",
2828
+ "- acf://framework/principle/P1",
2829
+ "- acf://framework/principle/P3",
2830
+ "",
2831
+ "Then produce a case study calibrated to the ACF\xAE toolkit quality bar: named characters (avoid Marc/Marie), precise legal references (AI Act / GDPR / DORA articles), 1-3 page brief, 5 explicit deliverables, a teacher's correction guide. End with the ACF\xAE doctrine citation."
2832
+ ].join("\n")
2833
+ }
2834
+ ];
2835
+ var PROMPT_INDEX = new Map(ACF_PROMPTS.map((p) => [p.name, p]));
2836
+ function renderPrompt(name, args) {
2837
+ const prompt = PROMPT_INDEX.get(name);
2838
+ if (!prompt) throw new Error(`Unknown prompt: ${name}`);
2839
+ return {
2840
+ description: prompt.description,
2841
+ messages: [
2842
+ {
2843
+ role: "user",
2844
+ content: { type: "text", text: prompt.render(args) }
2845
+ }
2846
+ ]
2847
+ };
2848
+ }
2849
+ async function registerPrompts(server, _registry) {
2850
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
2851
+ prompts: ACF_PROMPTS.map((p) => ({
2852
+ name: p.name,
2853
+ description: p.description,
2854
+ arguments: p.arguments
2855
+ }))
2856
+ }));
2857
+ server.setRequestHandler(GetPromptRequestSchema, async (req) => {
2858
+ return renderPrompt(req.params.name, req.params.arguments ?? {});
2859
+ });
2860
+ }
2861
+
2862
+ // src/core/server.ts
2863
+ var ACF_MCP_NAME = "acf-mcp";
2864
+ var ACF_MCP_VERSION = "1.0.0";
2865
+ async function createAcfServer(opts) {
2866
+ const content = new ContentLoader({ contentRoot: opts.contentRoot });
2867
+ const rules = new RulesLoader({ rulesRoot: opts.rulesRoot });
2868
+ const search = await SearchEngine.fromFile(opts.indexPath);
2869
+ const meta = await content.loadMeta();
2870
+ const registry = { content, rules, search, meta };
2871
+ const server = new Server(
2872
+ { name: ACF_MCP_NAME, version: ACF_MCP_VERSION },
2873
+ {
2874
+ capabilities: {
2875
+ resources: { listChanged: false, subscribe: false },
2876
+ tools: { listChanged: false },
2877
+ prompts: { listChanged: false }
2878
+ }
2879
+ }
2880
+ );
2881
+ await registerResources(server, registry);
2882
+ await registerTools(server, registry);
2883
+ await registerPrompts(server);
2884
+ return { server, registry };
2885
+ }
2886
+
2887
+ // src/transport/stdio.ts
2888
+ var HERE = path.dirname(fileURLToPath(import.meta.url));
2889
+ var PKG_ROOT = path.resolve(HERE, "..", "..");
2890
+ async function main() {
2891
+ const { server } = await createAcfServer({
2892
+ contentRoot: path.join(PKG_ROOT, "content"),
2893
+ rulesRoot: path.join(PKG_ROOT, "content", "rules"),
2894
+ indexPath: path.join(PKG_ROOT, "dist", "search-index.json")
2895
+ });
2896
+ const transport = new StdioServerTransport();
2897
+ await server.connect(transport);
2898
+ process.stderr.write(`acf-mcp ready (stdio).
2899
+ `);
2900
+ }
2901
+ main().catch((err) => {
2902
+ process.stderr.write(`${err.stack ?? String(err)}
2903
+ `);
2904
+ process.exit(1);
2905
+ });
2906
+ //# sourceMappingURL=stdio.js.map
2907
+ //# sourceMappingURL=stdio.js.map