@jigyasudham/veto 0.8.3 → 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 (111) hide show
  1. package/README.md +209 -52
  2. package/dist/agents/executor.js +36 -3
  3. package/dist/cli.js +246 -7
  4. package/dist/context/reader.js +113 -0
  5. package/dist/council/index.js +3 -1
  6. package/dist/plugins/loader.js +49 -0
  7. package/dist/router/index.js +2 -2
  8. package/dist/router/learning-updater.js +45 -1
  9. package/dist/server.js +478 -14
  10. package/dist/watcher/index.js +77 -0
  11. package/dist/workflow/pipeline.js +64 -0
  12. package/package.json +12 -3
  13. package/.claude/settings.local.json +0 -9
  14. package/src/adapters/claude.ts +0 -70
  15. package/src/adapters/codex.ts +0 -71
  16. package/src/adapters/gemini.ts +0 -71
  17. package/src/adapters/index.ts +0 -217
  18. package/src/agents/development/api.ts +0 -120
  19. package/src/agents/development/backend.ts +0 -85
  20. package/src/agents/development/coder.ts +0 -213
  21. package/src/agents/development/database.ts +0 -83
  22. package/src/agents/development/debugger.ts +0 -238
  23. package/src/agents/development/devops.ts +0 -86
  24. package/src/agents/development/frontend.ts +0 -85
  25. package/src/agents/development/migration.ts +0 -144
  26. package/src/agents/development/performance.ts +0 -144
  27. package/src/agents/development/refactor.ts +0 -86
  28. package/src/agents/development/reviewer.ts +0 -268
  29. package/src/agents/development/tester.ts +0 -151
  30. package/src/agents/executor.ts +0 -158
  31. package/src/agents/memory/context-manager.ts +0 -171
  32. package/src/agents/memory/decision-logger.ts +0 -160
  33. package/src/agents/memory/knowledge-base.ts +0 -124
  34. package/src/agents/memory/pattern-learner.ts +0 -143
  35. package/src/agents/memory/project-mapper.ts +0 -118
  36. package/src/agents/quality/accessibility.ts +0 -99
  37. package/src/agents/quality/code-quality.ts +0 -115
  38. package/src/agents/quality/compatibility.ts +0 -58
  39. package/src/agents/quality/documentation.ts +0 -105
  40. package/src/agents/quality/error-handling.ts +0 -96
  41. package/src/agents/research/competitor-analyzer.ts +0 -45
  42. package/src/agents/research/cost-analyzer.ts +0 -54
  43. package/src/agents/research/estimator.ts +0 -60
  44. package/src/agents/research/ethics-bias.ts +0 -113
  45. package/src/agents/research/researcher.ts +0 -114
  46. package/src/agents/research/risk-assessor.ts +0 -63
  47. package/src/agents/research/tech-advisor.ts +0 -55
  48. package/src/agents/security/auth.ts +0 -287
  49. package/src/agents/security/dependency-audit.ts +0 -337
  50. package/src/agents/security/penetration.ts +0 -262
  51. package/src/agents/security/privacy.ts +0 -285
  52. package/src/agents/security/scanner.ts +0 -322
  53. package/src/agents/security/secrets.ts +0 -249
  54. package/src/agents/types.ts +0 -66
  55. package/src/agents/workflow/automation.ts +0 -59
  56. package/src/agents/workflow/file-manager.ts +0 -52
  57. package/src/agents/workflow/git-agent.ts +0 -55
  58. package/src/agents/workflow/reporter.ts +0 -51
  59. package/src/agents/workflow/search-agent.ts +0 -40
  60. package/src/agents/workflow/task-coordinator.ts +0 -41
  61. package/src/agents/workflow/task-planner.ts +0 -47
  62. package/src/cli.ts +0 -204
  63. package/src/council/decision-engine.ts +0 -171
  64. package/src/council/devil-advocate.ts +0 -116
  65. package/src/council/index.ts +0 -44
  66. package/src/council/lead-developer.ts +0 -118
  67. package/src/council/legal-compliance.ts +0 -152
  68. package/src/council/product-manager.ts +0 -102
  69. package/src/council/security.ts +0 -172
  70. package/src/council/system-architect.ts +0 -132
  71. package/src/council/types.ts +0 -33
  72. package/src/council/ux-designer.ts +0 -121
  73. package/src/memory/local.ts +0 -305
  74. package/src/memory/schema.ts +0 -174
  75. package/src/memory/sync.ts +0 -274
  76. package/src/router/complexity-scorer.ts +0 -96
  77. package/src/router/context-compressor.ts +0 -74
  78. package/src/router/index.ts +0 -60
  79. package/src/router/learning-updater.ts +0 -271
  80. package/src/router/model-selector.ts +0 -83
  81. package/src/router/rate-monitor.ts +0 -103
  82. package/src/server.ts +0 -1038
  83. package/src/skills/development/skill-api-design.ts +0 -329
  84. package/src/skills/development/skill-auth.ts +0 -271
  85. package/src/skills/development/skill-ci-cd.ts +0 -0
  86. package/src/skills/development/skill-crud.ts +0 -209
  87. package/src/skills/development/skill-db-schema.ts +0 -0
  88. package/src/skills/development/skill-docker.ts +0 -0
  89. package/src/skills/development/skill-env-setup.ts +0 -0
  90. package/src/skills/development/skill-scaffold.ts +0 -323
  91. package/src/skills/intelligence/skill-complexity-score.ts +0 -69
  92. package/src/skills/intelligence/skill-cost-track.ts +0 -39
  93. package/src/skills/intelligence/skill-learning-loop.ts +0 -69
  94. package/src/skills/intelligence/skill-pattern-detect.ts +0 -38
  95. package/src/skills/intelligence/skill-rate-watch.ts +0 -61
  96. package/src/skills/memory/skill-context-compress.ts +0 -98
  97. package/src/skills/memory/skill-cross-sync.ts +0 -104
  98. package/src/skills/memory/skill-decision-log.ts +0 -119
  99. package/src/skills/memory/skill-session-restore.ts +0 -59
  100. package/src/skills/memory/skill-session-save.ts +0 -94
  101. package/src/skills/quality/skill-accessibility.ts +0 -0
  102. package/src/skills/quality/skill-code-review.ts +0 -84
  103. package/src/skills/quality/skill-docs-gen.ts +0 -0
  104. package/src/skills/quality/skill-perf-audit.ts +0 -0
  105. package/src/skills/quality/skill-security-scan.ts +0 -91
  106. package/src/skills/quality/skill-test-suite.ts +0 -290
  107. package/src/skills/workflow/skill-deploy.ts +0 -0
  108. package/src/skills/workflow/skill-git-workflow.ts +0 -0
  109. package/src/skills/workflow/skill-rollback.ts +0 -0
  110. package/src/skills/workflow/skill-task-breakdown.ts +0 -0
  111. package/tsconfig.json +0 -20
package/src/server.ts DELETED
@@ -1,1038 +0,0 @@
1
- #!/usr/bin/env node
2
- // Veto MCP Server — Phase 1 skeleton
3
- // Exposes: veto_status, veto_session_save, veto_session_restore, veto_sessions_list
4
-
5
- // Suppress node:sqlite experimental warning — it would corrupt the MCP stdio protocol
6
- process.removeAllListeners('warning');
7
-
8
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
9
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
- import {
11
- CallToolRequestSchema,
12
- ListToolsRequestSchema,
13
- } from '@modelcontextprotocol/sdk/types.js';
14
- import {
15
- saveSession, restoreSession, listSessions, getDbPath, saveCouncilOutcome,
16
- storeKnowledge, searchKnowledge, deleteKnowledge,
17
- updateProjectMap, getProjectMap,
18
- upsertPattern, getPatterns,
19
- } from './memory/local.js';
20
- import { exportMemory, importMemory, getLocalDbSize } from './memory/sync.js';
21
- import { runDebate } from './council/index.js';
22
- import { routeTask, getRateStatus, recordOutcome, getLearningStats, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights } from './router/index.js';
23
- import type { AgentType, Platform } from './router/index.js';
24
- import { executeParallel, executeOne } from './agents/executor.js';
25
- import type { AgentTask, WorkerAgentType } from './agents/types.js';
26
- import { handoff, continueSession, getPlatformSetup } from './adapters/index.js';
27
-
28
- const VERSION = '0.8.0';
29
-
30
- const server = new Server(
31
- { name: 'veto', version: VERSION },
32
- {
33
- capabilities: {
34
- tools: {},
35
- },
36
- }
37
- );
38
-
39
- // ─── Tool Definitions ─────────────────────────────────────────────────────────
40
-
41
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
42
- tools: [
43
- {
44
- name: 'veto_status',
45
- description: 'Returns Veto server status, version, and database info.',
46
- inputSchema: {
47
- type: 'object',
48
- properties: {},
49
- required: [],
50
- },
51
- },
52
- {
53
- name: 'veto_session_save',
54
- description:
55
- 'Saves the current session context to SQLite for later restoration across AI platforms.',
56
- inputSchema: {
57
- type: 'object',
58
- properties: {
59
- summary: {
60
- type: 'string',
61
- description: 'A brief summary of what was accomplished this session.',
62
- },
63
- context: {
64
- type: 'string',
65
- description: 'Key context to restore (decisions, current task, file list, etc.).',
66
- },
67
- task_state: {
68
- type: 'string',
69
- description: 'Current task state — what is done and what is next.',
70
- },
71
- platform: {
72
- type: 'string',
73
- description: 'AI platform used (claude, gemini, codex). Defaults to "claude".',
74
- enum: ['claude', 'gemini', 'codex'],
75
- },
76
- project_dir: {
77
- type: 'string',
78
- description: 'Absolute path to the current project directory.',
79
- },
80
- token_count: {
81
- type: 'number',
82
- description: 'Approximate tokens used this session.',
83
- },
84
- },
85
- required: ['summary', 'context'],
86
- },
87
- },
88
- {
89
- name: 'veto_session_restore',
90
- description:
91
- 'Restores a previously saved session by ID. Use veto_sessions_list to find IDs.',
92
- inputSchema: {
93
- type: 'object',
94
- properties: {
95
- session_id: {
96
- type: 'string',
97
- description: 'UUID of the session to restore.',
98
- },
99
- },
100
- required: ['session_id'],
101
- },
102
- },
103
- {
104
- name: 'veto_sessions_list',
105
- description: 'Lists the most recent saved sessions (up to 10).',
106
- inputSchema: {
107
- type: 'object',
108
- properties: {
109
- limit: {
110
- type: 'number',
111
- description: 'Number of sessions to return (default 10, max 50).',
112
- },
113
- },
114
- required: [],
115
- },
116
- },
117
- {
118
- name: 'veto_route_task',
119
- description:
120
- 'Scores a task for complexity (0-100) and returns the optimal tier, model recommendation, and rate status. Use before any substantial task to let the router decide which model to use.',
121
- inputSchema: {
122
- type: 'object',
123
- properties: {
124
- task: {
125
- type: 'string',
126
- description: 'The task description to score and route.',
127
- },
128
- agent_type: {
129
- type: 'string',
130
- description: 'Optional agent type — some agents are tier-locked regardless of score.',
131
- enum: [
132
- 'lead-developer', 'system-architect', 'security-scanner',
133
- 'devil-advocate', 'decision-engine', 'risk-assessor',
134
- 'coder', 'tester', 'reviewer', 'database', 'documentation',
135
- 'file-manager', 'git-agent', 'search-agent', 'secrets', 'reporter',
136
- 'dynamic',
137
- ],
138
- },
139
- files_affected: {
140
- type: 'number',
141
- description: 'Number of files the task will touch (influences complexity score).',
142
- },
143
- force_council: {
144
- type: 'boolean',
145
- description: 'Set true to force a Tier 3 / council-required routing.',
146
- },
147
- context: {
148
- type: 'string',
149
- description: 'Current context text — router will return a compression plan.',
150
- },
151
- preferred_platform: {
152
- type: 'string',
153
- description: 'Preferred AI platform. Router may override if rate-limited.',
154
- enum: ['claude', 'gemini', 'codex'],
155
- },
156
- },
157
- required: ['task'],
158
- },
159
- },
160
- {
161
- name: 'veto_rate_status',
162
- description: 'Returns current request counts and rate limit status for all AI platforms tracked by Veto.',
163
- inputSchema: {
164
- type: 'object',
165
- properties: {},
166
- required: [],
167
- },
168
- },
169
- {
170
- name: 'veto_council_debate',
171
- description:
172
- 'Runs the Veto Council — 5 specialist agents (Lead Dev, PM, Architect, UX, Devil\'s Advocate) debate your task in parallel and return a GREEN / YELLOW / RED / DEADLOCK verdict before any code is written. Call this before architecture decisions, security-sensitive work, database migrations, or any task the router scores above 71.',
173
- inputSchema: {
174
- type: 'object',
175
- properties: {
176
- task: {
177
- type: 'string',
178
- description: 'The task or decision to debate. Be specific — include approach, tech stack, and constraints.',
179
- },
180
- context: {
181
- type: 'string',
182
- description: 'Optional: additional context such as codebase state, prior decisions, or constraints.',
183
- },
184
- session_id: {
185
- type: 'string',
186
- description: 'Optional: session ID to associate this council outcome with an active session.',
187
- },
188
- },
189
- required: ['task'],
190
- },
191
- },
192
- {
193
- name: 'veto_agent_plan',
194
- description: 'Gets a domain-expert execution plan from a specific worker agent. Returns approach, ordered steps, checklist, patterns, and pitfalls for the task.',
195
- inputSchema: {
196
- type: 'object',
197
- properties: {
198
- agent: {
199
- type: 'string',
200
- description: 'The worker agent to consult.',
201
- enum: ['coder','reviewer','tester','debugger','refactor','database','api','frontend','backend','devops','performance','migration','security-scanner','auth','privacy','secrets','dependency-audit','penetration','context-manager','decision-logger','project-mapper','pattern-learner','knowledge-base','researcher','tech-advisor','cost-analyzer','competitor-analyzer','risk-assessor','estimator','ethics-bias','code-quality','documentation','accessibility','compatibility','error-handling','task-planner','task-coordinator','file-manager','git-agent','search-agent','reporter','automation'],
202
- },
203
- task: { type: 'string', description: 'The task for the agent to plan.' },
204
- context: { type: 'string', description: 'Optional additional context.' },
205
- },
206
- required: ['agent', 'task'],
207
- },
208
- },
209
- {
210
- name: 'veto_code_review',
211
- description: 'Runs the Code Reviewer agent on provided code. Returns scored findings (complexity, error handling, magic numbers, nesting, dead code) with severity and fixes.',
212
- inputSchema: {
213
- type: 'object',
214
- properties: {
215
- code: { type: 'string', description: 'The code to review.' },
216
- context: { type: 'string', description: 'Optional: file name, module description, or review focus.' },
217
- },
218
- required: ['code'],
219
- },
220
- },
221
- {
222
- name: 'veto_security_scan',
223
- description: 'Runs the Security Scanner (OWASP Top 10) on provided code. Returns vulnerabilities with severity, CWE/OWASP category, and remediation steps.',
224
- inputSchema: {
225
- type: 'object',
226
- properties: {
227
- code: { type: 'string', description: 'The code to scan.' },
228
- context: { type: 'string', description: 'Optional: language, framework, or specific concerns.' },
229
- },
230
- required: ['code'],
231
- },
232
- },
233
- {
234
- name: 'veto_secrets_scan',
235
- description: 'Scans text or code for exposed credentials — API keys, tokens, passwords, connection strings, private keys. Returns findings with masked values and line numbers.',
236
- inputSchema: {
237
- type: 'object',
238
- properties: {
239
- text: { type: 'string', description: 'The text or code to scan for secrets.' },
240
- },
241
- required: ['text'],
242
- },
243
- },
244
- {
245
- name: 'veto_execute_parallel',
246
- description: 'Runs multiple worker agents simultaneously via Promise.all. Use to get domain expert input from several agents in one round-trip — e.g. coder + tester + security-scanner all planning the same feature together.',
247
- inputSchema: {
248
- type: 'object',
249
- properties: {
250
- tasks: {
251
- type: 'array',
252
- description: 'List of agent tasks to run in parallel.',
253
- items: {
254
- type: 'object',
255
- properties: {
256
- id: { type: 'string', description: 'Unique ID for this task (use any string).' },
257
- agent: { type: 'string', description: 'Worker agent type.' },
258
- task: { type: 'string', description: 'Task description for this agent.' },
259
- code: { type: 'string', description: 'Optional code to analyze (triggers analyze() instead of plan()).' },
260
- context: { type: 'string', description: 'Optional additional context.' },
261
- },
262
- required: ['id', 'agent', 'task'],
263
- },
264
- },
265
- },
266
- required: ['tasks'],
267
- },
268
- },
269
- {
270
- name: 'veto_memory_store',
271
- description: 'Stores a knowledge entry (solution, pattern, error, reference, or decision) in the local knowledge base for retrieval across sessions. Search before storing to avoid duplicates.',
272
- inputSchema: {
273
- type: 'object',
274
- properties: {
275
- title: { type: 'string', description: 'Precise, searchable title. Bad: "Fixed bug". Good: "Fix: Node sqlite fails on Windows without --experimental-sqlite".' },
276
- content: { type: 'string', description: 'Self-contained content: problem → root cause → solution. Future agents must understand it without original context.' },
277
- type: {
278
- type: 'string',
279
- description: 'Entry type.',
280
- enum: ['solution', 'pattern', 'context', 'error', 'reference', 'decision'],
281
- },
282
- tags: { type: 'array', items: { type: 'string' }, description: 'Search tags (3–5 recommended). Examples: ["typescript", "auth", "jwt"].' },
283
- project_dir: { type: 'string', description: 'Absolute project path. Include for project-specific knowledge; omit for general programming knowledge.' },
284
- session_id: { type: 'string', description: 'Optional: associate this knowledge entry with an active session.' },
285
- relevance: { type: 'number', description: 'Initial relevance score 0.0–1.0 (default 1.0).' },
286
- },
287
- required: ['title', 'content'],
288
- },
289
- },
290
- {
291
- name: 'veto_memory_search',
292
- description: 'Searches the local knowledge base for entries matching a query. Call at the start of every task to find prior solutions before solving from scratch.',
293
- inputSchema: {
294
- type: 'object',
295
- properties: {
296
- query: { type: 'string', description: 'Search terms (full-text search on title and content).' },
297
- type: {
298
- type: 'string',
299
- description: 'Filter by entry type.',
300
- enum: ['solution', 'pattern', 'context', 'error', 'reference', 'decision'],
301
- },
302
- project_dir: { type: 'string', description: 'Filter to a specific project directory.' },
303
- limit: { type: 'number', description: 'Max results to return (default 10, max 50).' },
304
- },
305
- required: [],
306
- },
307
- },
308
- {
309
- name: 'veto_memory_delete',
310
- description: 'Deletes a knowledge entry by ID. Use to remove stale or duplicate entries found via veto_memory_search.',
311
- inputSchema: {
312
- type: 'object',
313
- properties: {
314
- id: { type: 'string', description: 'The knowledge entry ID (from veto_memory_search results).' },
315
- },
316
- required: ['id'],
317
- },
318
- },
319
- {
320
- name: 'veto_project_map_update',
321
- description: 'Updates the project structure map for a directory. Call after creating, deleting, or moving files. The map enables fast codebase navigation without filesystem scans.',
322
- inputSchema: {
323
- type: 'object',
324
- properties: {
325
- project_dir: { type: 'string', description: 'Absolute path to the project root.' },
326
- structure: { type: 'string', description: 'JSON string representing the directory tree. Example: {"src/":{"agents/":["coder.ts","reviewer.ts"],"router/":["index.ts"]}}' },
327
- key_modules: {
328
- type: 'array',
329
- items: { type: 'string' },
330
- description: 'The 10–20 most important files with their roles. Example: ["src/server.ts (MCP entry point)", "src/router/index.ts (task router)"].',
331
- },
332
- tech_stack: {
333
- type: 'array',
334
- items: { type: 'string' },
335
- description: 'Frameworks and key libraries. Example: ["TypeScript", "Node.js 22", "Express", "SQLite"].',
336
- },
337
- },
338
- required: ['project_dir', 'structure'],
339
- },
340
- },
341
- {
342
- name: 'veto_project_map_get',
343
- description: 'Returns the stored project structure map for a directory. Use to navigate the codebase without scanning the filesystem.',
344
- inputSchema: {
345
- type: 'object',
346
- properties: {
347
- project_dir: { type: 'string', description: 'Absolute path to the project root.' },
348
- },
349
- required: ['project_dir'],
350
- },
351
- },
352
- {
353
- name: 'veto_pattern_store',
354
- description: 'Stores or updates a coding pattern observed in the codebase. Patterns are keyed by category.pattern-name and confidence increases with repeated observation.',
355
- inputSchema: {
356
- type: 'object',
357
- properties: {
358
- pattern_key: { type: 'string', description: 'Pattern identifier in category.pattern-name format. Example: "code.async-pattern" or "naming.variable-case".' },
359
- pattern_val: { type: 'string', description: 'The observed pattern value. Example: "async/await with try/catch, no raw Promise chains".' },
360
- confidence: { type: 'number', description: 'Confidence score 0.0–1.0 (default 1.0). Increases automatically on repeated observation.' },
361
- },
362
- required: ['pattern_key', 'pattern_val'],
363
- },
364
- },
365
- {
366
- name: 'veto_patterns_list',
367
- description: 'Returns stored coding patterns. Filter by prefix to get patterns in a specific category (e.g. prefix="naming." for all naming conventions).',
368
- inputSchema: {
369
- type: 'object',
370
- properties: {
371
- prefix: { type: 'string', description: 'Optional prefix filter. Example: "code." or "naming." or "testing.".' },
372
- limit: { type: 'number', description: 'Max patterns to return (default 20).' },
373
- },
374
- required: [],
375
- },
376
- },
377
- {
378
- name: 'veto_memory_export',
379
- description: 'Exports all local memory (sessions, knowledge, patterns, decisions, project maps) to a portable JSON file. Copy the file to another machine and run veto_memory_import there to resume work. No external services required.',
380
- inputSchema: {
381
- type: 'object',
382
- properties: {
383
- output_path: {
384
- type: 'string',
385
- description: 'Where to write the export file. Defaults to ~/.veto/veto-export.json. Use a path on shared storage (Dropbox, OneDrive, USB) to make transfer easy.',
386
- },
387
- },
388
- required: [],
389
- },
390
- },
391
- {
392
- name: 'veto_memory_import',
393
- description: 'Imports memory from a JSON file exported by veto_memory_export on another machine. Merges into local SQLite using INSERT OR IGNORE — existing local rows are never overwritten. Call veto_sessions_list after import to confirm sessions arrived.',
394
- inputSchema: {
395
- type: 'object',
396
- properties: {
397
- input_path: {
398
- type: 'string',
399
- description: 'Path to the export JSON file. Defaults to ~/.veto/veto-export.json.',
400
- },
401
- },
402
- required: [],
403
- },
404
- },
405
- {
406
- name: 'veto_record_outcome',
407
- description: 'Records a task outcome (quality score) to feed the self-learning router. Call after completing any task. After 20+ outcomes, call veto_learning_apply to update tier thresholds.',
408
- inputSchema: {
409
- type: 'object',
410
- properties: {
411
- task_type: { type: 'string', description: 'Short consistent label for the task category (e.g. "write-unit-tests", "fix-auth-bug"). Use the same label for similar tasks to enable pattern detection.' },
412
- complexity: { type: 'number', description: 'The complexity score from veto_route_task (0–100).' },
413
- model_tier: { type: 'number', description: 'The tier that was actually used (1, 2, or 3).', enum: [1, 2, 3] },
414
- output_quality: { type: 'number', description: 'Output quality score 0–100. 90–100=excellent, 70–89=good, 50–69=acceptable, 30–49=poor, 0–29=failed.' },
415
- agent: { type: 'string', description: 'The worker agent type used (optional but useful for agent performance tracking).' },
416
- tokens_used: { type: 'number', description: 'Approximate tokens used (optional).' },
417
- },
418
- required: ['task_type', 'complexity', 'model_tier', 'output_quality'],
419
- },
420
- },
421
- {
422
- name: 'veto_learning_stats',
423
- description: 'Returns the self-learning router dashboard: tier distribution, per-agent quality stats, suggested threshold adjustments, and council insights. Use to understand how the router is performing and where to improve.',
424
- inputSchema: {
425
- type: 'object',
426
- properties: {
427
- include_agent_stats: { type: 'boolean', description: 'Include per-agent quality breakdown (default true).' },
428
- include_task_types: { type: 'boolean', description: 'Include per-task-type breakdown (default false, verbose).' },
429
- include_council_insights: { type: 'boolean', description: 'Include council decision → debugging correlation (default false).' },
430
- },
431
- required: [],
432
- },
433
- },
434
- {
435
- name: 'veto_learning_apply',
436
- description: 'Applies learned tier thresholds to the router based on recorded task outcomes. Requires at least 20 recorded outcomes. The router immediately uses the new thresholds on the next veto_route_task call.',
437
- inputSchema: {
438
- type: 'object',
439
- properties: {},
440
- required: [],
441
- },
442
- },
443
- {
444
- name: 'veto_handoff',
445
- description: 'Saves the current session and returns step-by-step instructions to continue on another AI platform (Gemini or Codex). Call this when Claude is approaching its rate limit. The receiving platform calls veto_continue to restore full context instantly.',
446
- inputSchema: {
447
- type: 'object',
448
- properties: {
449
- summary: { type: 'string', description: 'What was accomplished this session — one or two sentences.' },
450
- context: { type: 'string', description: 'Key context the next platform needs: active decisions, file paths, constraints.' },
451
- task_state: { type: 'string', description: 'Current task state — what is done, what is in progress, what is next.' },
452
- from_platform: { type: 'string', enum: ['claude', 'gemini', 'codex'], description: 'Platform handing off (default: claude).' },
453
- to_platform: { type: 'string', enum: ['gemini', 'codex', 'claude'], description: 'Target platform. If omitted, Veto picks the platform with the most headroom.' },
454
- project_dir: { type: 'string', description: 'Absolute path to the current project directory.' },
455
- token_count: { type: 'number', description: 'Approximate tokens used this session.' },
456
- },
457
- required: ['summary', 'context'],
458
- },
459
- },
460
- {
461
- name: 'veto_continue',
462
- description: 'Restores the most recent session on any platform. Call this immediately after switching platforms — Veto returns the full context, summary, and next action. Nothing needs to be re-explained.',
463
- inputSchema: {
464
- type: 'object',
465
- properties: {
466
- session_id: { type: 'string', description: 'Optional. Session ID from veto_handoff. If omitted, the most recent saved session is restored.' },
467
- },
468
- required: [],
469
- },
470
- },
471
- {
472
- name: 'veto_platform_setup',
473
- description: 'Returns the exact MCP config and setup steps to connect a specific AI platform to this Veto server.',
474
- inputSchema: {
475
- type: 'object',
476
- properties: {
477
- platform: { type: 'string', enum: ['claude', 'gemini', 'codex'], description: 'The platform to get setup instructions for.' },
478
- veto_server_path: { type: 'string', description: 'Absolute path to the built veto server (dist/server.js).' },
479
- },
480
- required: ['platform', 'veto_server_path'],
481
- },
482
- },
483
- ],
484
- }));
485
-
486
- // ─── Tool Handlers ────────────────────────────────────────────────────────────
487
-
488
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
489
- const { name, arguments: args } = request.params;
490
-
491
- switch (name) {
492
- case 'veto_status': {
493
- return {
494
- content: [
495
- {
496
- type: 'text',
497
- text: JSON.stringify(
498
- {
499
- status: 'running',
500
- version: VERSION,
501
- server: 'veto',
502
- phase: 8,
503
- capabilities: ['session_save', 'session_restore', 'router', 'rate_monitor', 'council_debate', 'agent_plan', 'code_review', 'security_scan', 'secrets_scan', 'parallel_exec', 'memory_store', 'memory_search', 'project_map', 'pattern_store', 'memory_export', 'memory_import', 'learning_stats', 'learning_apply', 'record_outcome', 'handoff', 'continue', 'platform_setup'],
504
- db_path: getDbPath(),
505
- uptime_ms: process.uptime() * 1000,
506
- timestamp: new Date().toISOString(),
507
- },
508
- null,
509
- 2
510
- ),
511
- },
512
- ],
513
- };
514
- }
515
-
516
- case 'veto_session_save': {
517
- const result = saveSession({
518
- summary: String(args?.summary ?? ''),
519
- context: String(args?.context ?? ''),
520
- task_state: args?.task_state ? String(args.task_state) : undefined,
521
- platform: args?.platform ? String(args.platform) : 'claude',
522
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
523
- token_count: typeof args?.token_count === 'number' ? args.token_count : 0,
524
- });
525
-
526
- return {
527
- content: [
528
- {
529
- type: 'text',
530
- text: JSON.stringify(
531
- {
532
- success: true,
533
- message: 'Session saved. Use this ID to restore on any AI platform.',
534
- ...result,
535
- },
536
- null,
537
- 2
538
- ),
539
- },
540
- ],
541
- };
542
- }
543
-
544
- case 'veto_session_restore': {
545
- const session_id = String(args?.session_id ?? '');
546
- const result = restoreSession(session_id);
547
-
548
- if (!result.found) {
549
- return {
550
- content: [
551
- {
552
- type: 'text',
553
- text: JSON.stringify(
554
- { success: false, message: `No session found with id: ${session_id}` },
555
- null,
556
- 2
557
- ),
558
- },
559
- ],
560
- isError: true,
561
- };
562
- }
563
-
564
- const s = result.session!;
565
- return {
566
- content: [
567
- {
568
- type: 'text',
569
- text: JSON.stringify(
570
- {
571
- success: true,
572
- session_id: s.id,
573
- platform: s.platform,
574
- started_at: s.started_at,
575
- ended_at: s.ended_at,
576
- project_dir: s.project_dir,
577
- summary: s.summary,
578
- context: s.context ? JSON.parse(s.context) : null,
579
- task_state: s.task_state ? JSON.parse(s.task_state) : null,
580
- token_count: s.token_count,
581
- },
582
- null,
583
- 2
584
- ),
585
- },
586
- ],
587
- };
588
- }
589
-
590
- case 'veto_sessions_list': {
591
- const limit = Math.min(typeof args?.limit === 'number' ? args.limit : 10, 50);
592
- const sessions = listSessions(limit);
593
-
594
- return {
595
- content: [
596
- {
597
- type: 'text',
598
- text: JSON.stringify(
599
- {
600
- count: sessions.length,
601
- sessions: sessions.map((s) => ({
602
- id: s.id,
603
- platform: s.platform,
604
- started_at: s.started_at,
605
- ended_at: s.ended_at,
606
- project_dir: s.project_dir,
607
- summary: s.summary,
608
- token_count: s.token_count,
609
- })),
610
- },
611
- null,
612
- 2
613
- ),
614
- },
615
- ],
616
- };
617
- }
618
-
619
- case 'veto_route_task': {
620
- const result = routeTask(String(args?.task ?? ''), {
621
- agentType: args?.agent_type ? (String(args.agent_type) as AgentType) : undefined,
622
- filesAffected: typeof args?.files_affected === 'number' ? args.files_affected : undefined,
623
- forceCouncil: args?.force_council === true,
624
- context: args?.context ? String(args.context) : undefined,
625
- preferredPlatform: args?.preferred_platform ? (String(args.preferred_platform) as Platform) : 'claude',
626
- });
627
- return {
628
- content: [
629
- {
630
- type: 'text',
631
- text: JSON.stringify(result, null, 2),
632
- },
633
- ],
634
- };
635
- }
636
-
637
- case 'veto_rate_status': {
638
- return {
639
- content: [
640
- {
641
- type: 'text',
642
- text: JSON.stringify(getRateStatus(), null, 2),
643
- },
644
- ],
645
- };
646
- }
647
-
648
- case 'veto_council_debate': {
649
- const task = String(args?.task ?? '').trim();
650
- if (!task) {
651
- return {
652
- content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'task is required.' }) }],
653
- isError: true,
654
- };
655
- }
656
-
657
- const result = await runDebate({
658
- task,
659
- context: args?.context ? String(args.context) : undefined,
660
- });
661
-
662
- const outcomeId = saveCouncilOutcome({
663
- session_id: args?.session_id ? String(args.session_id) : undefined,
664
- task,
665
- verdict: result.final_verdict,
666
- lead_dev: JSON.stringify(result.votes.lead_dev),
667
- pm: JSON.stringify(result.votes.pm),
668
- architect: JSON.stringify(result.votes.architect),
669
- ux: JSON.stringify(result.votes.ux),
670
- devil: JSON.stringify(result.votes.devil),
671
- legal: JSON.stringify(result.votes.legal),
672
- security: JSON.stringify(result.votes.security),
673
- recommended: result.recommended,
674
- });
675
-
676
- return {
677
- content: [
678
- {
679
- type: 'text',
680
- text: result.formatted_output + '\n\n' + JSON.stringify(
681
- {
682
- outcome_id: outcomeId,
683
- final_verdict: result.final_verdict,
684
- block_reasons: result.block_reasons,
685
- warnings: result.warnings,
686
- recommended: result.recommended,
687
- debated_at: result.debated_at,
688
- votes: {
689
- lead_dev: result.votes.lead_dev.verdict,
690
- pm: result.votes.pm.verdict,
691
- architect: result.votes.architect.verdict,
692
- ux: result.votes.ux.verdict,
693
- devil: result.votes.devil.verdict,
694
- legal: result.votes.legal.verdict,
695
- security: result.votes.security.verdict,
696
- },
697
- },
698
- null,
699
- 2
700
- ),
701
- },
702
- ],
703
- };
704
- }
705
-
706
- case 'veto_agent_plan': {
707
- const agentType = String(args?.agent ?? '') as WorkerAgentType;
708
- const task = String(args?.task ?? '').trim();
709
- if (!task) {
710
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'task is required.' }) }], isError: true };
711
- }
712
- const result = await executeOne({ id: 'plan-1', agent: agentType, task, context: args?.context ? String(args.context) : undefined });
713
- if (result.error) {
714
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
715
- }
716
- return { content: [{ type: 'text', text: JSON.stringify(result.plan ?? result.analysis, null, 2) }] };
717
- }
718
-
719
- case 'veto_code_review': {
720
- const code = String(args?.code ?? '').trim();
721
- if (!code) {
722
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'code is required.' }) }], isError: true };
723
- }
724
- const result = await executeOne({ id: 'review-1', agent: 'reviewer', task: 'review this code', code, context: args?.context ? String(args.context) : undefined });
725
- if (result.error) {
726
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
727
- }
728
- return { content: [{ type: 'text', text: JSON.stringify(result.analysis, null, 2) }] };
729
- }
730
-
731
- case 'veto_security_scan': {
732
- const code = String(args?.code ?? '').trim();
733
- if (!code) {
734
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'code is required.' }) }], isError: true };
735
- }
736
- const result = await executeOne({ id: 'scan-1', agent: 'security-scanner', task: 'scan this code for security issues', code, context: args?.context ? String(args.context) : undefined });
737
- if (result.error) {
738
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
739
- }
740
- return { content: [{ type: 'text', text: JSON.stringify(result.analysis, null, 2) }] };
741
- }
742
-
743
- case 'veto_secrets_scan': {
744
- const text = String(args?.text ?? '').trim();
745
- if (!text) {
746
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'text is required.' }) }], isError: true };
747
- }
748
- const result = await executeOne({ id: 'secrets-1', agent: 'secrets', task: 'scan for exposed credentials', code: text });
749
- if (result.error) {
750
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
751
- }
752
- return { content: [{ type: 'text', text: JSON.stringify(result.analysis, null, 2) }] };
753
- }
754
-
755
- case 'veto_execute_parallel': {
756
- const rawTasks = Array.isArray(args?.tasks) ? args.tasks : [];
757
- if (rawTasks.length === 0) {
758
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'tasks array is required and must not be empty.' }) }], isError: true };
759
- }
760
- const tasks: AgentTask[] = rawTasks.map((t: Record<string, unknown>) => ({
761
- id: String(t.id ?? ''),
762
- agent: String(t.agent ?? '') as WorkerAgentType,
763
- task: String(t.task ?? ''),
764
- code: t.code ? String(t.code) : undefined,
765
- context: t.context ? String(t.context) : undefined,
766
- }));
767
- const results = await executeParallel(tasks);
768
- return {
769
- content: [{
770
- type: 'text',
771
- text: JSON.stringify({
772
- count: results.length,
773
- total_duration_ms: results.reduce((s, r) => s + r.duration_ms, 0),
774
- results: results.map(r => ({
775
- id: r.id,
776
- agent: r.agent,
777
- duration_ms: r.duration_ms,
778
- error: r.error,
779
- output: r.plan ?? r.analysis,
780
- })),
781
- }, null, 2),
782
- }],
783
- };
784
- }
785
-
786
- case 'veto_memory_store': {
787
- const title = String(args?.title ?? '').trim();
788
- const content = String(args?.content ?? '').trim();
789
- if (!title || !content) {
790
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'title and content are required.' }) }], isError: true };
791
- }
792
- const id = storeKnowledge({
793
- title,
794
- content,
795
- type: args?.type ? String(args.type) as import('./memory/schema.js').KnowledgeType : 'solution',
796
- tags: Array.isArray(args?.tags) ? args.tags.map(String) : undefined,
797
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
798
- session_id: args?.session_id ? String(args.session_id) : undefined,
799
- relevance: typeof args?.relevance === 'number' ? args.relevance : 1.0,
800
- });
801
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, id, message: 'Knowledge stored.' }, null, 2) }] };
802
- }
803
-
804
- case 'veto_memory_search': {
805
- const results = searchKnowledge({
806
- query: args?.query ? String(args.query) : undefined,
807
- type: args?.type ? String(args.type) as import('./memory/schema.js').KnowledgeType : undefined,
808
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
809
- limit: typeof args?.limit === 'number' ? args.limit : 10,
810
- });
811
- return {
812
- content: [{
813
- type: 'text',
814
- text: JSON.stringify({
815
- count: results.length,
816
- results: results.map(r => ({
817
- id: r.id,
818
- type: r.type,
819
- title: r.title,
820
- content: r.content,
821
- tags: r.tags ? JSON.parse(r.tags) : [],
822
- project_dir: r.project_dir,
823
- relevance: r.relevance,
824
- accessed_count: r.accessed_count,
825
- created_at: r.created_at,
826
- })),
827
- }, null, 2),
828
- }],
829
- };
830
- }
831
-
832
- case 'veto_memory_delete': {
833
- const id = String(args?.id ?? '').trim();
834
- if (!id) {
835
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'id is required.' }) }], isError: true };
836
- }
837
- const deleted = deleteKnowledge(id);
838
- return { content: [{ type: 'text', text: JSON.stringify({ success: deleted, message: deleted ? 'Entry deleted.' : 'Entry not found.' }, null, 2) }] };
839
- }
840
-
841
- case 'veto_project_map_update': {
842
- const project_dir = String(args?.project_dir ?? '').trim();
843
- const structure = String(args?.structure ?? '').trim();
844
- if (!project_dir || !structure) {
845
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'project_dir and structure are required.' }) }], isError: true };
846
- }
847
- const id = updateProjectMap({
848
- project_dir,
849
- structure,
850
- key_modules: Array.isArray(args?.key_modules) ? args.key_modules.map(String) : undefined,
851
- tech_stack: Array.isArray(args?.tech_stack) ? args.tech_stack.map(String) : undefined,
852
- });
853
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, id, message: 'Project map updated.' }, null, 2) }] };
854
- }
855
-
856
- case 'veto_project_map_get': {
857
- const project_dir = String(args?.project_dir ?? '').trim();
858
- if (!project_dir) {
859
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'project_dir is required.' }) }], isError: true };
860
- }
861
- const row = getProjectMap(project_dir);
862
- if (!row) {
863
- return { content: [{ type: 'text', text: JSON.stringify({ found: false, message: 'No project map found. Call veto_project_map_update to create one.' }, null, 2) }] };
864
- }
865
- return {
866
- content: [{
867
- type: 'text',
868
- text: JSON.stringify({
869
- found: true,
870
- project_dir: row.project_dir,
871
- structure: JSON.parse(row.structure),
872
- key_modules: row.key_modules ? JSON.parse(row.key_modules) : [],
873
- tech_stack: row.tech_stack ? JSON.parse(row.tech_stack) : [],
874
- updated_at: row.updated_at,
875
- }, null, 2),
876
- }],
877
- };
878
- }
879
-
880
- case 'veto_pattern_store': {
881
- const pattern_key = String(args?.pattern_key ?? '').trim();
882
- const pattern_val = String(args?.pattern_val ?? '').trim();
883
- if (!pattern_key || !pattern_val) {
884
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'pattern_key and pattern_val are required.' }) }], isError: true };
885
- }
886
- upsertPattern({
887
- pattern_key,
888
- pattern_val,
889
- confidence: typeof args?.confidence === 'number' ? args.confidence : 1.0,
890
- });
891
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: 'Pattern stored.' }, null, 2) }] };
892
- }
893
-
894
- case 'veto_patterns_list': {
895
- const prefix = args?.prefix ? String(args.prefix) : undefined;
896
- const limit = typeof args?.limit === 'number' ? args.limit : 20;
897
- const patterns = getPatterns(prefix, limit);
898
- return {
899
- content: [{
900
- type: 'text',
901
- text: JSON.stringify({
902
- count: patterns.length,
903
- patterns: patterns.map(p => ({
904
- key: p.pattern_key,
905
- val: p.pattern_val,
906
- confidence: p.confidence,
907
- seen_count: p.seen_count,
908
- updated_at: p.updated_at,
909
- })),
910
- }, null, 2),
911
- }],
912
- };
913
- }
914
-
915
- case 'veto_handoff': {
916
- const summary = String(args?.summary ?? '').trim();
917
- const context = String(args?.context ?? '').trim();
918
- if (!summary || !context) {
919
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'summary and context are required.' }) }], isError: true };
920
- }
921
- const result = handoff({
922
- summary,
923
- context,
924
- task_state: args?.task_state ? String(args.task_state) : undefined,
925
- from_platform: args?.from_platform ? String(args.from_platform) as Platform : 'claude',
926
- to_platform: args?.to_platform ? String(args.to_platform) as Platform : undefined,
927
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
928
- token_count: typeof args?.token_count === 'number' ? args.token_count : 0,
929
- });
930
- return { content: [{ type: 'text', text: result.instructions + '\n\n' + JSON.stringify({ session_id: result.session_id, to_platform: result.to_platform, saved_at: result.saved_at, reason: result.reason }, null, 2) }] };
931
- }
932
-
933
- case 'veto_continue': {
934
- const result = continueSession(args?.session_id ? String(args.session_id) : undefined);
935
- if (!result.found) {
936
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: result.message }, null, 2) }], isError: true };
937
- }
938
- return {
939
- content: [{
940
- type: 'text',
941
- text: result.message + '\n\n' + JSON.stringify({
942
- session_id: result.session_id,
943
- platform: result.platform,
944
- summary: result.summary,
945
- context: result.context,
946
- task_state: result.task_state,
947
- next_action: result.next_action,
948
- project_dir: result.project_dir,
949
- token_count: result.token_count,
950
- restored_at: result.restored_at,
951
- }, null, 2),
952
- }],
953
- };
954
- }
955
-
956
- case 'veto_platform_setup': {
957
- const platform = String(args?.platform ?? '').trim() as Platform;
958
- const vetoServerPath = String(args?.veto_server_path ?? '').trim();
959
- if (!platform || !vetoServerPath) {
960
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'platform and veto_server_path are required.' }) }], isError: true };
961
- }
962
- const setup = getPlatformSetup(platform, vetoServerPath);
963
- return { content: [{ type: 'text', text: JSON.stringify(setup, null, 2) }] };
964
- }
965
-
966
- case 'veto_record_outcome': {
967
- const task_type = String(args?.task_type ?? '').trim();
968
- const complexity = typeof args?.complexity === 'number' ? args.complexity : 50;
969
- const model_tier = (typeof args?.model_tier === 'number' ? args.model_tier : 2) as 1 | 2 | 3;
970
- const output_quality = typeof args?.output_quality === 'number' ? args.output_quality : 70;
971
- if (!task_type) {
972
- return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: 'task_type is required.' }) }], isError: true };
973
- }
974
- recordOutcome(task_type, complexity, model_tier, args?.agent ? String(args.agent) : 'dynamic', output_quality, typeof args?.tokens_used === 'number' ? args.tokens_used : 0);
975
- const stats = getLearningStats();
976
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: 'Outcome recorded.', total_outcomes: stats.total_tasks, next_step: stats.total_tasks >= 20 ? 'You have 20+ outcomes. Call veto_learning_apply to update router thresholds.' : `Need ${20 - stats.total_tasks} more outcomes before veto_learning_apply can adjust thresholds.` }, null, 2) }] };
977
- }
978
-
979
- case 'veto_learning_stats': {
980
- const includeAgentStats = args?.include_agent_stats !== false;
981
- const includeTaskTypes = args?.include_task_types === true;
982
- const includeCouncil = args?.include_council_insights === true;
983
-
984
- const stats = getLearningStats();
985
- const result: Record<string, unknown> = {
986
- total_outcomes: stats.total_tasks,
987
- tier_breakdown: stats.tier_breakdown,
988
- current_thresholds: { tier1_max: 30, tier2_max: 70, note: 'defaults — call veto_learning_apply to update from data' },
989
- suggested_thresholds: stats.suggested_thresholds,
990
- ready_to_apply: stats.total_tasks >= 20,
991
- };
992
-
993
- if (includeAgentStats) {
994
- result['agent_performance'] = getAgentPerformanceStats();
995
- }
996
- if (includeTaskTypes) {
997
- result['task_type_breakdown'] = getTaskTypeBreakdown();
998
- }
999
- if (includeCouncil) {
1000
- result['council_insights'] = getCouncilInsights();
1001
- }
1002
-
1003
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1004
- }
1005
-
1006
- case 'veto_learning_apply': {
1007
- const result = applyLearnedThresholds();
1008
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1009
- }
1010
-
1011
- case 'veto_memory_export': {
1012
- const result = exportMemory(args?.output_path ? String(args.output_path) : undefined);
1013
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1014
- }
1015
-
1016
- case 'veto_memory_import': {
1017
- const result = importMemory(args?.input_path ? String(args.input_path) : undefined);
1018
- return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1019
- }
1020
-
1021
- default:
1022
- throw new Error(`Unknown tool: ${name}`);
1023
- }
1024
- });
1025
-
1026
- // ─── Start ─────────────────────────────────────────────────────────────────────
1027
-
1028
- async function main() {
1029
- const transport = new StdioServerTransport();
1030
- await server.connect(transport);
1031
- // stderr so it doesn't pollute MCP stdio
1032
- process.stderr.write(`Veto MCP server v${VERSION} running (stdio)\n`);
1033
- }
1034
-
1035
- main().catch((err) => {
1036
- process.stderr.write(`Fatal: ${err.message}\n`);
1037
- process.exit(1);
1038
- });