@svadmin/mcp 0.4.30 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -2
- package/src/governance.ts +116 -0
- package/src/index.ts +293 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server — expose svadmin DataProvider as tools for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"@svadmin/core": "^0.
|
|
23
|
+
"@svadmin/core": "^0.71.0",
|
|
24
|
+
"@svadmin/devtools": "^0.6.0"
|
|
24
25
|
},
|
|
25
26
|
"license": "MIT",
|
|
26
27
|
"publishConfig": {
|
|
@@ -40,6 +41,9 @@
|
|
|
40
41
|
"peerDependenciesMeta": {
|
|
41
42
|
"@svadmin/core": {
|
|
42
43
|
"optional": true
|
|
44
|
+
},
|
|
45
|
+
"@svadmin/devtools": {
|
|
46
|
+
"optional": true
|
|
43
47
|
}
|
|
44
48
|
}
|
|
45
49
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @svadmin/mcp/governance
|
|
3
|
+
*
|
|
4
|
+
* Pure, filesystem-free rule engine for the SVAdmin Vibe Coding governance
|
|
5
|
+
* checks. The MCP tool and `scripts/check-vibe-governance.ts` both delegate to
|
|
6
|
+
* this module so agents and CI enforce exactly the same rules.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type GovernanceRuleId = 'no-unmanaged-fetch' | 'escape-hatch-discipline';
|
|
10
|
+
|
|
11
|
+
export interface GovernanceSource {
|
|
12
|
+
/** Path used in reports and error messages */
|
|
13
|
+
path: string;
|
|
14
|
+
/** Full source text of the file */
|
|
15
|
+
content: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface GovernanceViolation {
|
|
19
|
+
file: string;
|
|
20
|
+
line: number;
|
|
21
|
+
rule: GovernanceRuleId;
|
|
22
|
+
message: string;
|
|
23
|
+
remediation: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface VibeGovernanceReport {
|
|
27
|
+
passed: boolean;
|
|
28
|
+
totalFilesScanned: number;
|
|
29
|
+
rulesChecked: GovernanceRuleId[];
|
|
30
|
+
violations: GovernanceViolation[];
|
|
31
|
+
summary: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface VibeGovernanceOptions {
|
|
35
|
+
/**
|
|
36
|
+
* Strict mode: a `@vibe-escape` annotation only suppresses a violation when it
|
|
37
|
+
* carries a non-empty reason. Non-strict mode honours any annotation but still
|
|
38
|
+
* reports annotations that are missing a reason.
|
|
39
|
+
*/
|
|
40
|
+
strict?: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const ESCAPE_MARKER = /@vibe-escape\b/u;
|
|
44
|
+
const ESCAPE_REASON = /@vibe-escape\s*:\s*(\S.*)$/u;
|
|
45
|
+
const COMMENT_PREFIX = /^(?:\/\/|\/\*|\*)/u;
|
|
46
|
+
const NETWORK_CALL = [/[^.\w]fetch\s*\(/u, /(?:^|[^.\w])axios\s*[.(]/u];
|
|
47
|
+
|
|
48
|
+
function escapeSuppression(lines: string[], strict: boolean): boolean {
|
|
49
|
+
for (const line of lines) {
|
|
50
|
+
if (!ESCAPE_MARKER.test(line)) continue;
|
|
51
|
+
if (!strict) return true;
|
|
52
|
+
if (ESCAPE_REASON.test(line)) return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Scans in-memory sources for architectural violations.
|
|
59
|
+
*
|
|
60
|
+
* Rules:
|
|
61
|
+
* 1. `no-unmanaged-fetch` — view code must not call `fetch()` / `axios` directly
|
|
62
|
+
* unless the file documents `// @vibe-escape: <reason>`.
|
|
63
|
+
* 2. `escape-hatch-discipline` — every `@vibe-escape` marker must carry a reason.
|
|
64
|
+
*/
|
|
65
|
+
export function scanVibeGovernance(
|
|
66
|
+
sources: GovernanceSource[],
|
|
67
|
+
options: VibeGovernanceOptions = {},
|
|
68
|
+
): VibeGovernanceReport {
|
|
69
|
+
const strict = options.strict ?? false;
|
|
70
|
+
const violations: GovernanceViolation[] = [];
|
|
71
|
+
|
|
72
|
+
for (const source of sources) {
|
|
73
|
+
const lines = source.content.split(/\r?\n/u);
|
|
74
|
+
const suppressed = escapeSuppression(lines, strict);
|
|
75
|
+
|
|
76
|
+
lines.forEach((line, index) => {
|
|
77
|
+
const lineNumber = index + 1;
|
|
78
|
+
const trimmed = line.trim();
|
|
79
|
+
|
|
80
|
+
if (ESCAPE_MARKER.test(line) && !ESCAPE_REASON.test(line)) {
|
|
81
|
+
violations.push({
|
|
82
|
+
file: source.path,
|
|
83
|
+
line: lineNumber,
|
|
84
|
+
rule: 'escape-hatch-discipline',
|
|
85
|
+
message: `Escape hatch annotation is missing a reason: "${trimmed}"`,
|
|
86
|
+
remediation: 'Use "// @vibe-escape: <reason>" so reviewers and agents can justify the bypass.',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (suppressed) return;
|
|
91
|
+
if (COMMENT_PREFIX.test(trimmed)) return;
|
|
92
|
+
if (ESCAPE_MARKER.test(line)) return;
|
|
93
|
+
if (!NETWORK_CALL.some(pattern => pattern.test(line))) return;
|
|
94
|
+
|
|
95
|
+
violations.push({
|
|
96
|
+
file: source.path,
|
|
97
|
+
line: lineNumber,
|
|
98
|
+
rule: 'no-unmanaged-fetch',
|
|
99
|
+
message: `Direct unmanaged network call detected: "${trimmed}"`,
|
|
100
|
+
remediation:
|
|
101
|
+
'Use standard DataProvider hooks (useList, useOne, useCreate, useUpdate) or add "// @vibe-escape: <reason>" if an external API call is intended.',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const passed = violations.length === 0;
|
|
107
|
+
return {
|
|
108
|
+
passed,
|
|
109
|
+
totalFilesScanned: sources.length,
|
|
110
|
+
rulesChecked: ['no-unmanaged-fetch', 'escape-hatch-discipline'],
|
|
111
|
+
violations,
|
|
112
|
+
summary: passed
|
|
113
|
+
? `Vibe Governance passed! Scanned ${sources.length} files with zero architectural violations.`
|
|
114
|
+
: `Vibe Governance failed with ${violations.length} violation(s) across ${sources.length} scanned files.`,
|
|
115
|
+
};
|
|
116
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,18 @@ import { definedOptions } from '@svadmin/core/options';
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import type { DataProvider, ResourceDefinition } from '@svadmin/core';
|
|
28
|
+
import { inferResource } from '@svadmin/core/inferencer';
|
|
29
|
+
import { scanVibeGovernance, type GovernanceSource } from './governance';
|
|
30
|
+
import { inferFromJSONSchema, inferFromDatabaseColumns } from '@svadmin/core/inferencer-schema';
|
|
31
|
+
import {
|
|
32
|
+
buildResourcePatch,
|
|
33
|
+
buildSelfHealingReport,
|
|
34
|
+
type DevtoolsAppDiagnostics,
|
|
35
|
+
type DevtoolsCollector,
|
|
36
|
+
type DevtoolsDiagnostic,
|
|
37
|
+
type DevtoolsSnapshot,
|
|
38
|
+
type ResourcePatchAction,
|
|
39
|
+
} from '@svadmin/devtools';
|
|
28
40
|
|
|
29
41
|
// ─── Types ───────────────────────────────────────────────────
|
|
30
42
|
|
|
@@ -41,6 +53,14 @@ export interface MCPServerOptions {
|
|
|
41
53
|
version?: string;
|
|
42
54
|
/** When true, only expose read operations (getList, getOne, getMany) */
|
|
43
55
|
readOnly?: boolean;
|
|
56
|
+
/** Optional DevTools collector instance to expose runtime diagnostics to Agent */
|
|
57
|
+
collector?: DevtoolsCollector;
|
|
58
|
+
/** Optional custom diagnostics provider function */
|
|
59
|
+
getDiagnostics?: () =>
|
|
60
|
+
| DevtoolsAppDiagnostics
|
|
61
|
+
| DevtoolsSnapshot
|
|
62
|
+
| DevtoolsDiagnostic[]
|
|
63
|
+
| Promise<DevtoolsAppDiagnostics | DevtoolsSnapshot | DevtoolsDiagnostic[]>;
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
/** MCP Tool definition following the Model Context Protocol spec */
|
|
@@ -217,6 +237,118 @@ function generateTools(options: MCPServerOptions): MCPTool[] {
|
|
|
217
237
|
});
|
|
218
238
|
}
|
|
219
239
|
|
|
240
|
+
// Diagnostics and self-healing tools
|
|
241
|
+
tools.push({
|
|
242
|
+
name: 'svadmin_getDiagnostics',
|
|
243
|
+
description:
|
|
244
|
+
'Fetch current SVAdmin runtime diagnostics, including health status, configured providers, active resources, and error events.',
|
|
245
|
+
readOnly: true,
|
|
246
|
+
concurrent: true,
|
|
247
|
+
destructive: false,
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: 'object',
|
|
250
|
+
properties: {},
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
tools.push({
|
|
255
|
+
name: 'svadmin_getSelfHealingPrompt',
|
|
256
|
+
description:
|
|
257
|
+
'Get an AI-ready self-healing diagnosis report from runtime errors captured by SVAdmin DevTools, with structured root causes and suggested code fixes.',
|
|
258
|
+
readOnly: true,
|
|
259
|
+
concurrent: true,
|
|
260
|
+
destructive: false,
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: 'object',
|
|
263
|
+
properties: {},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
tools.push({
|
|
268
|
+
name: 'svadmin_getManifest',
|
|
269
|
+
description:
|
|
270
|
+
'Get the active SVAdmin application manifest including registered resources, fields, operations, and data provider metadata.',
|
|
271
|
+
readOnly: true,
|
|
272
|
+
concurrent: true,
|
|
273
|
+
destructive: false,
|
|
274
|
+
inputSchema: {
|
|
275
|
+
type: 'object',
|
|
276
|
+
properties: {},
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
tools.push({
|
|
281
|
+
name: 'svadmin_generateResource',
|
|
282
|
+
description:
|
|
283
|
+
'Zero-hallucination code generator for SVAdmin. Infers ResourceDefinition, TypeBox validation, and complete Svelte 5 AutoTable/AutoForm component snippets from sample records, JSON Schema, or database column definitions.',
|
|
284
|
+
readOnly: true,
|
|
285
|
+
concurrent: true,
|
|
286
|
+
destructive: false,
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: 'object',
|
|
289
|
+
properties: {
|
|
290
|
+
resource: { type: 'string', description: 'Name of the resource to generate (e.g. "orders", "users")' },
|
|
291
|
+
sourceType: {
|
|
292
|
+
type: 'string',
|
|
293
|
+
enum: ['sampleData', 'jsonSchema', 'databaseColumns'],
|
|
294
|
+
description: 'Type of input payload',
|
|
295
|
+
},
|
|
296
|
+
payload: { type: 'string', description: 'JSON-serialized payload matching the sourceType' },
|
|
297
|
+
idKey: { type: 'string', description: 'Primary key name (defaults to "id")' },
|
|
298
|
+
label: { type: 'string', description: 'Human-readable resource label' },
|
|
299
|
+
},
|
|
300
|
+
required: ['resource', 'sourceType', 'payload'],
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
tools.push({
|
|
305
|
+
name: 'svadmin_applyResourcePatch',
|
|
306
|
+
description:
|
|
307
|
+
'Applies a structured writeback patch derived from the Visual Inspector (Click-to-Edit), generating verified code snippets and agent prompts to update ResourceDefinitions, column visibility, or field properties.',
|
|
308
|
+
readOnly: true,
|
|
309
|
+
concurrent: true,
|
|
310
|
+
destructive: false,
|
|
311
|
+
inputSchema: {
|
|
312
|
+
type: 'object',
|
|
313
|
+
properties: {
|
|
314
|
+
resource: { type: 'string', description: 'Resource name to patch' },
|
|
315
|
+
action: {
|
|
316
|
+
type: 'string',
|
|
317
|
+
enum: ['updateField', 'toggleColumnVisibility', 'updateColumnTitle', 'updatePageSize', 'reorderColumns'],
|
|
318
|
+
description: 'Patch action to perform',
|
|
319
|
+
},
|
|
320
|
+
field: { type: 'string', description: 'Field name targeted' },
|
|
321
|
+
column: { type: 'string', description: 'Column name targeted' },
|
|
322
|
+
value: { type: 'string', description: 'JSON-serialized or string patch value' },
|
|
323
|
+
targetComponent: { type: 'string', description: 'Component context (e.g. AutoTable, AutoForm)' },
|
|
324
|
+
},
|
|
325
|
+
required: ['resource', 'action'],
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
tools.push({
|
|
330
|
+
name: 'svadmin_verifyGovernance',
|
|
331
|
+
description:
|
|
332
|
+
'Runs the SVAdmin Vibe Coding architectural governance check on the supplied sources, verifying data provider isolation (no unmanaged fetch/axios) and escape-hatch annotation discipline. Returns the same verdict as `bun run check:vibe-governance`.',
|
|
333
|
+
readOnly: true,
|
|
334
|
+
concurrent: true,
|
|
335
|
+
destructive: false,
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: 'object',
|
|
338
|
+
properties: {
|
|
339
|
+
files: {
|
|
340
|
+
type: 'string',
|
|
341
|
+
description: 'JSON-serialized array of { path, content } sources to scan (for example the files an agent just edited).',
|
|
342
|
+
},
|
|
343
|
+
strict: {
|
|
344
|
+
type: 'boolean',
|
|
345
|
+
description: 'Require every @vibe-escape annotation to carry a non-empty reason before it suppresses a violation.',
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
required: ['files'],
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
|
|
220
352
|
return tools;
|
|
221
353
|
}
|
|
222
354
|
|
|
@@ -228,7 +360,15 @@ async function handleToolCall(
|
|
|
228
360
|
options: MCPServerOptions,
|
|
229
361
|
): Promise<unknown> {
|
|
230
362
|
const resource = args['resource'] as string;
|
|
231
|
-
|
|
363
|
+
const dataCrudTools = [
|
|
364
|
+
'svadmin_getList',
|
|
365
|
+
'svadmin_getOne',
|
|
366
|
+
'svadmin_getMany',
|
|
367
|
+
'svadmin_create',
|
|
368
|
+
'svadmin_update',
|
|
369
|
+
'svadmin_delete',
|
|
370
|
+
];
|
|
371
|
+
if (dataCrudTools.includes(toolName) && resource && !options.resources.includes(resource)) {
|
|
232
372
|
throw new Error(`Unauthorized resource access: ${resource} is not exposed.`);
|
|
233
373
|
}
|
|
234
374
|
const dp = options.dataProvider;
|
|
@@ -296,6 +436,149 @@ async function handleToolCall(
|
|
|
296
436
|
});
|
|
297
437
|
}
|
|
298
438
|
|
|
439
|
+
case 'svadmin_getDiagnostics': {
|
|
440
|
+
if (options.collector) {
|
|
441
|
+
return options.collector.snapshot();
|
|
442
|
+
}
|
|
443
|
+
if (options.getDiagnostics) {
|
|
444
|
+
return await options.getDiagnostics();
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
status: 'healthy',
|
|
448
|
+
resources: options.resources,
|
|
449
|
+
dataProvider: options.dataProvider.getApiUrl?.() ?? 'configured',
|
|
450
|
+
message: 'No DevTools collector attached. Basic provider info returned.',
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
case 'svadmin_getSelfHealingPrompt': {
|
|
455
|
+
let diags: DevtoolsDiagnostic[] = [];
|
|
456
|
+
if (options.collector) {
|
|
457
|
+
diags = options.collector.snapshot().diagnostics;
|
|
458
|
+
} else if (options.getDiagnostics) {
|
|
459
|
+
const res = await options.getDiagnostics();
|
|
460
|
+
if (Array.isArray(res)) {
|
|
461
|
+
diags = res;
|
|
462
|
+
} else if (res && typeof res === 'object') {
|
|
463
|
+
if ('snapshot' in res && res.snapshot) {
|
|
464
|
+
diags = (res.snapshot as DevtoolsSnapshot).diagnostics;
|
|
465
|
+
} else if ('diagnostics' in res && Array.isArray((res as { diagnostics: unknown }).diagnostics)) {
|
|
466
|
+
diags = (res as { diagnostics: DevtoolsDiagnostic[] }).diagnostics;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return buildSelfHealingReport(diags);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
case 'svadmin_getManifest': {
|
|
474
|
+
return {
|
|
475
|
+
resources: options.resourceDefinitions ?? options.resources.map((name) => ({ name })),
|
|
476
|
+
capabilities: {
|
|
477
|
+
apiUrl: options.dataProvider.getApiUrl?.() ?? undefined,
|
|
478
|
+
readOnly: !!options.readOnly,
|
|
479
|
+
hasCollector: !!options.collector,
|
|
480
|
+
},
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
case 'svadmin_generateResource': {
|
|
485
|
+
const resource = args['resource'] as string;
|
|
486
|
+
const sourceType = args['sourceType'] as 'sampleData' | 'jsonSchema' | 'databaseColumns';
|
|
487
|
+
const rawPayload = args['payload'] as string;
|
|
488
|
+
const idKey = (args['idKey'] as string) ?? 'id';
|
|
489
|
+
const label = args['label'] as string | undefined;
|
|
490
|
+
|
|
491
|
+
const parsedPayload = typeof rawPayload === 'string' ? JSON.parse(rawPayload) : rawPayload;
|
|
492
|
+
let result;
|
|
493
|
+
|
|
494
|
+
if (sourceType === 'sampleData') {
|
|
495
|
+
result = inferResource(resource, Array.isArray(parsedPayload) ? parsedPayload : [parsedPayload], definedOptions({
|
|
496
|
+
primaryKey: idKey,
|
|
497
|
+
label,
|
|
498
|
+
}));
|
|
499
|
+
} else if (sourceType === 'jsonSchema') {
|
|
500
|
+
result = inferFromJSONSchema(resource, parsedPayload, definedOptions({
|
|
501
|
+
primaryKey: idKey,
|
|
502
|
+
label,
|
|
503
|
+
}));
|
|
504
|
+
} else if (sourceType === 'databaseColumns') {
|
|
505
|
+
result = inferFromDatabaseColumns(resource, parsedPayload, definedOptions({
|
|
506
|
+
primaryKey: idKey,
|
|
507
|
+
label,
|
|
508
|
+
}));
|
|
509
|
+
} else {
|
|
510
|
+
throw new Error(`Invalid sourceType: ${sourceType}`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
resource: result.resource,
|
|
515
|
+
code: result.code,
|
|
516
|
+
typeboxCode: result.typeboxCode,
|
|
517
|
+
componentCode: result.componentCode,
|
|
518
|
+
review: result.review,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
case 'svadmin_applyResourcePatch': {
|
|
523
|
+
const resource = args['resource'] as string;
|
|
524
|
+
const action = args['action'] as ResourcePatchAction;
|
|
525
|
+
const field = args['field'] as string | undefined;
|
|
526
|
+
const column = args['column'] as string | undefined;
|
|
527
|
+
const targetComponent = args['targetComponent'] as string | undefined;
|
|
528
|
+
let value = args['value'];
|
|
529
|
+
if (typeof value === 'string') {
|
|
530
|
+
try {
|
|
531
|
+
value = JSON.parse(value);
|
|
532
|
+
} catch {
|
|
533
|
+
// Keep as string
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return buildResourcePatch({
|
|
538
|
+
resource,
|
|
539
|
+
action,
|
|
540
|
+
value,
|
|
541
|
+
...definedOptions({
|
|
542
|
+
field,
|
|
543
|
+
column,
|
|
544
|
+
targetComponent,
|
|
545
|
+
}),
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
case 'svadmin_verifyGovernance': {
|
|
550
|
+
const strict = Boolean(args['strict']);
|
|
551
|
+
const rawFiles = args['files'];
|
|
552
|
+
const parsed: unknown = typeof rawFiles === 'string' ? JSON.parse(rawFiles) : rawFiles;
|
|
553
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
554
|
+
throw new Error('svadmin_verifyGovernance requires a non-empty files array of { path, content } sources.');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const sources: GovernanceSource[] = parsed.map((entry: unknown, index: number) => {
|
|
558
|
+
const candidate = entry as { path?: unknown; content?: unknown } | null;
|
|
559
|
+
if (
|
|
560
|
+
candidate === null ||
|
|
561
|
+
typeof candidate !== 'object' ||
|
|
562
|
+
typeof candidate.path !== 'string' ||
|
|
563
|
+
typeof candidate.content !== 'string'
|
|
564
|
+
) {
|
|
565
|
+
throw new Error(`svadmin_verifyGovernance: files[${index}] must be an object with string path and content.`);
|
|
566
|
+
}
|
|
567
|
+
return { path: candidate.path, content: candidate.content };
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
const report = scanVibeGovernance(sources, { strict });
|
|
571
|
+
return {
|
|
572
|
+
governancePassed: report.passed,
|
|
573
|
+
status: report.passed ? 'compliant' : 'violations',
|
|
574
|
+
strictMode: strict,
|
|
575
|
+
rulesChecked: report.rulesChecked,
|
|
576
|
+
filesScanned: report.totalFilesScanned,
|
|
577
|
+
violations: report.violations,
|
|
578
|
+
summary: report.summary,
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
299
582
|
default:
|
|
300
583
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
301
584
|
}
|
|
@@ -326,6 +609,15 @@ async function handleToolCall(
|
|
|
326
609
|
/**
|
|
327
610
|
* Projects an MCP tool into the standard allowlisted schema while preserving metadata and safety flags.
|
|
328
611
|
*/
|
|
612
|
+
export { scanVibeGovernance } from './governance';
|
|
613
|
+
export type {
|
|
614
|
+
GovernanceRuleId,
|
|
615
|
+
GovernanceSource,
|
|
616
|
+
GovernanceViolation,
|
|
617
|
+
VibeGovernanceOptions,
|
|
618
|
+
VibeGovernanceReport,
|
|
619
|
+
} from './governance';
|
|
620
|
+
|
|
329
621
|
export function projectMCPToolSchema(tool: MCPTool): MCPTool {
|
|
330
622
|
return {
|
|
331
623
|
name: tool.name,
|