@geraldmaron/construct 1.4.1 → 1.4.2
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/bin/construct +2 -1
- package/bin/construct-postinstall.mjs +27 -2
- package/config/tag-vocabulary.json +264 -0
- package/lib/config/schema.mjs +1 -1
- package/lib/doctor/diagnosis.mjs +20 -0
- package/lib/doctor/index.mjs +5 -0
- package/lib/embed/worker.mjs +5 -0
- package/lib/env-config.mjs +10 -2
- package/lib/export-validate.mjs +34 -2
- package/lib/host/readiness.mjs +109 -0
- package/lib/host-disposition.mjs +10 -1
- package/lib/install/stage-project.mjs +41 -4
- package/lib/intake/prepare.mjs +2 -0
- package/lib/mcp/destructive-approval.mjs +57 -0
- package/lib/mcp/server.mjs +208 -34
- package/lib/mcp/tool-rate-limit.mjs +47 -0
- package/lib/mcp/tool-safety.mjs +94 -0
- package/lib/mcp/tool-surface-parity.mjs +60 -0
- package/lib/mcp/tools/orchestration-run.mjs +45 -7
- package/lib/mcp/tools/project.mjs +25 -8
- package/lib/mcp/tools/storage.mjs +9 -1
- package/lib/mcp/tools/web-search-governance.mjs +96 -0
- package/lib/mcp/tools/web-search.mjs +5 -76
- package/lib/mcp-platform-config.mjs +27 -18
- package/lib/oracle/daemon-entry.mjs +6 -0
- package/lib/orchestration/runtime.mjs +81 -42
- package/lib/orchestration/web-capability.mjs +59 -0
- package/lib/orchestration/worker.mjs +263 -19
- package/lib/output-quality.mjs +61 -2
- package/lib/path-policy.mjs +56 -0
- package/lib/providers/secret-audit-wiring.mjs +35 -18
- package/lib/providers/secret-resolver.mjs +28 -13
- package/lib/registry/catalog.mjs +6 -0
- package/lib/registry/loader.mjs +7 -1
- package/lib/registry/validate.mjs +3 -3
- package/lib/sandbox.mjs +1 -1
- package/lib/service-manager.mjs +59 -9
- package/lib/storage/admin.mjs +7 -2
- package/package.json +6 -1
- package/registry/agent-manifest.json +117 -0
- package/registry/capabilities.json +1880 -0
- package/schemas/brand-voice.schema.json +24 -0
- package/schemas/capability-registry.schema.json +72 -0
- package/schemas/certification-run.schema.json +130 -0
- package/schemas/demo-recording.schema.json +46 -0
- package/schemas/eval-dataset.schema.json +79 -0
- package/schemas/execution-capability-profile.schema.json +46 -0
- package/schemas/execution-policy.schema.json +114 -0
- package/schemas/improvement-proposal.schema.json +65 -0
- package/schemas/mcp-tool-output.schema.json +61 -0
- package/schemas/platform-capabilities.schema.json +83 -0
- package/schemas/project-config.schema.json +227 -0
- package/schemas/project-demo.schema.json +60 -0
- package/schemas/provider-behavior-matrix.schema.json +91 -0
- package/schemas/scope.schema.json +197 -0
- package/schemas/specialist-trace.schema.json +107 -0
- package/schemas/team.schema.json +99 -0
- package/schemas/unified-registry.schema.json +548 -0
- package/scripts/sync-specialists.mjs +52 -25
- package/specialists/org/specialists/cx-researcher.json +1 -0
- package/specialists/prompts/cx-researcher.md +9 -8
- package/vendor/pandoc-ext/README.md +3 -0
- package/vendor/pandoc-ext/diagram.lua +687 -0
package/lib/mcp/server.mjs
CHANGED
|
@@ -18,6 +18,9 @@ import { loadConstructEnv, prepareConstructEnv } from '../runtime-env.mjs';
|
|
|
18
18
|
import { getInstalledVersion } from '../version.mjs';
|
|
19
19
|
import { getDeploymentMode } from '../deployment-mode.mjs';
|
|
20
20
|
import { withGenAiSpan, GenAiAttrs, extractTraceContext, injectTraceContext } from '../telemetry/otel-tracer.mjs';
|
|
21
|
+
import { TOOL_SAFETY, DEFAULT_OUTPUT_SCHEMA } from './tool-safety.mjs';
|
|
22
|
+
import { ToolRateLimiter, ToolRateLimited } from './tool-rate-limit.mjs';
|
|
23
|
+
import { appendAuditRecord } from '../audit-trail.mjs';
|
|
21
24
|
|
|
22
25
|
// Apply config.env values to process.env, letting config.env win over shell env
|
|
23
26
|
// so telemetry/OpenRouter credentials are always correct regardless of host env.
|
|
@@ -63,6 +66,8 @@ import { modelResolve, triageRecommend, workflowInvoke, capabilityDescribe, exec
|
|
|
63
66
|
import { authorArtifact } from './tools/artifact-author.mjs';
|
|
64
67
|
import { findTool } from './tools/find-tool.mjs';
|
|
65
68
|
import { recoverToolName, recordToolNameMiss, isGatewayName } from './tool-recovery.mjs';
|
|
69
|
+
import { assertCoreSubsetOfCatalog } from './tool-surface-parity.mjs';
|
|
70
|
+
import { enableSecretAuditTrail } from '../providers/secret-audit-wiring.mjs';
|
|
66
71
|
import { buildOrchestrationReadiness } from '../orchestration/readiness.mjs';
|
|
67
72
|
|
|
68
73
|
const DEFAULT_ROOT_DIR = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
@@ -125,12 +130,25 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
|
|
|
125
130
|
|
|
126
131
|
// The full construct-mcp tool catalog. Only a curated core is exposed flat in
|
|
127
132
|
// ListTools; the long tail is reachable through the construct_call meta-tool, so
|
|
128
|
-
// the serialized tool surface stays small on every host and model. The
|
|
129
|
-
// flat surface alone (~
|
|
133
|
+
// the serialized tool surface stays small on every host and model. The 75-tool
|
|
134
|
+
// flat surface alone (~14.8k tokens) overran a 32k local-model window.
|
|
135
|
+
|
|
136
|
+
// Every tool def gains a declared outputSchema and a safety block (class, fs/
|
|
137
|
+
// network/process scope) from TOOL_SAFETY. A tool missing from that table throws
|
|
138
|
+
// here rather than shipping unclassified — see lib/mcp/tool-safety.mjs.
|
|
139
|
+
|
|
140
|
+
function withSafetyEnvelope(def) {
|
|
141
|
+
const safety = TOOL_SAFETY[def.name];
|
|
142
|
+
if (!safety) {
|
|
143
|
+
throw new Error(`tool-safety: "${def.name}" has no safety classification — add one to lib/mcp/tool-safety.mjs`);
|
|
144
|
+
}
|
|
145
|
+
return { ...def, outputSchema: def.outputSchema ?? DEFAULT_OUTPUT_SCHEMA, safety };
|
|
146
|
+
}
|
|
130
147
|
|
|
131
148
|
const ALL_TOOL_DEFS = [
|
|
132
149
|
{
|
|
133
150
|
name: 'agent_health',
|
|
151
|
+
outputSchema: { type: 'object' },
|
|
134
152
|
description: 'Returns agent health summaries from the most recent performance review.',
|
|
135
153
|
inputSchema: {
|
|
136
154
|
type: 'object',
|
|
@@ -144,6 +162,7 @@ const ALL_TOOL_DEFS = [
|
|
|
144
162
|
},
|
|
145
163
|
{
|
|
146
164
|
name: 'summarize_diff',
|
|
165
|
+
outputSchema: { type: 'object' },
|
|
147
166
|
description: 'Summarizes the git diff between the current state and a base ref.',
|
|
148
167
|
inputSchema: {
|
|
149
168
|
type: 'object',
|
|
@@ -161,6 +180,7 @@ const ALL_TOOL_DEFS = [
|
|
|
161
180
|
},
|
|
162
181
|
{
|
|
163
182
|
name: 'scan_file',
|
|
183
|
+
outputSchema: { type: 'object' },
|
|
164
184
|
description: 'Scans a file for secrets and code quality issues.',
|
|
165
185
|
inputSchema: {
|
|
166
186
|
type: 'object',
|
|
@@ -175,6 +195,7 @@ const ALL_TOOL_DEFS = [
|
|
|
175
195
|
},
|
|
176
196
|
{
|
|
177
197
|
name: 'extract_document_text',
|
|
198
|
+
outputSchema: { type: 'object' },
|
|
178
199
|
description: 'Extracts readable text from a local document path. Uses node-native extractors (unpdf/mammoth) first; escalates to the docling Python sidecar or whisper.cpp when needed (same pipeline as `construct ingest`). Supports PDF, DOCX, XLSX, PPTX, HTML, plain text, email, and transcripts.',
|
|
179
200
|
inputSchema: {
|
|
180
201
|
type: 'object',
|
|
@@ -193,6 +214,7 @@ const ALL_TOOL_DEFS = [
|
|
|
193
214
|
},
|
|
194
215
|
{
|
|
195
216
|
name: 'ingest_document',
|
|
217
|
+
outputSchema: { type: 'object' },
|
|
196
218
|
description: 'Converts a local document into a normalized markdown file, placing it into an indexed project path by default.',
|
|
197
219
|
inputSchema: {
|
|
198
220
|
type: 'object',
|
|
@@ -227,6 +249,7 @@ const ALL_TOOL_DEFS = [
|
|
|
227
249
|
},
|
|
228
250
|
{
|
|
229
251
|
name: 'infer_document_schema',
|
|
252
|
+
outputSchema: { type: 'object' },
|
|
230
253
|
description: 'Infers a structured field schema from a local document using AI. Returns field names, types, formats, examples, and confidence. Supports all document types handled by extract_document_text. Pass multiple file_paths to get a reconciled unified schema across documents.',
|
|
231
254
|
inputSchema: {
|
|
232
255
|
type: 'object',
|
|
@@ -265,6 +288,7 @@ const ALL_TOOL_DEFS = [
|
|
|
265
288
|
},
|
|
266
289
|
{
|
|
267
290
|
name: 'list_schema_artifacts',
|
|
291
|
+
outputSchema: { type: 'object' },
|
|
268
292
|
description: 'Lists all inferred schema artifacts (.schema.json files) in the project.',
|
|
269
293
|
inputSchema: {
|
|
270
294
|
type: 'object',
|
|
@@ -278,6 +302,7 @@ const ALL_TOOL_DEFS = [
|
|
|
278
302
|
},
|
|
279
303
|
{
|
|
280
304
|
name: 'storage_status',
|
|
305
|
+
outputSchema: { type: 'object' },
|
|
281
306
|
description: 'Returns SQL, local vector index, and ingested-artifact status for the current project.',
|
|
282
307
|
inputSchema: {
|
|
283
308
|
type: 'object',
|
|
@@ -367,6 +392,7 @@ const ALL_TOOL_DEFS = [
|
|
|
367
392
|
},
|
|
368
393
|
{
|
|
369
394
|
name: 'project_context',
|
|
395
|
+
outputSchema: { type: 'object' },
|
|
370
396
|
description: 'Returns project context: .cx/context.md content, recent commits, and working tree status.',
|
|
371
397
|
inputSchema: {
|
|
372
398
|
type: 'object',
|
|
@@ -380,6 +406,7 @@ const ALL_TOOL_DEFS = [
|
|
|
380
406
|
},
|
|
381
407
|
{
|
|
382
408
|
name: 'get_skill',
|
|
409
|
+
outputSchema: { type: 'object' },
|
|
383
410
|
description: 'Reads a specific skill playbook from the Construct knowledge base (e.g. "docs/adr-workflow", "roles/engineer", "architecture/security-arch").',
|
|
384
411
|
inputSchema: {
|
|
385
412
|
type: 'object',
|
|
@@ -391,6 +418,7 @@ const ALL_TOOL_DEFS = [
|
|
|
391
418
|
},
|
|
392
419
|
{
|
|
393
420
|
name: 'orchestration_policy',
|
|
421
|
+
outputSchema: { type: 'object' },
|
|
394
422
|
description: 'Classifies a request into intent, execution track, specialists, approval boundaries, and the contract chain that applies. The contractChain field names the typed producer→consumer handoffs expected for this dispatch plan. When `candidates` is supplied, also returns `contextPackets` keyed by specialist with the role-filtered artifact bundle each specialist should receive (omitted artifacts include the reason they were dropped).',
|
|
395
423
|
inputSchema: {
|
|
396
424
|
type: 'object',
|
|
@@ -442,6 +470,7 @@ const ALL_TOOL_DEFS = [
|
|
|
442
470
|
},
|
|
443
471
|
{
|
|
444
472
|
name: 'list_skills',
|
|
473
|
+
outputSchema: { type: 'object' },
|
|
445
474
|
description: 'Lists all available categories and playbooks in the Construct knowledge base.',
|
|
446
475
|
inputSchema: {
|
|
447
476
|
type: 'object',
|
|
@@ -450,6 +479,7 @@ const ALL_TOOL_DEFS = [
|
|
|
450
479
|
},
|
|
451
480
|
{
|
|
452
481
|
name: 'worker_run',
|
|
482
|
+
outputSchema: { type: 'object' },
|
|
453
483
|
description: 'Runs a bounded shell command via the worker plane: timeout, path-policy denial, restricted env, stdout/stderr artifacts under `.cx/runtime/worker/<jobId>.{stdout,stderr}.log`. When `graphId` + `nodeId` are supplied, an evidence record is appended to that task graph node so it can transition to `done`. Emits `worker.started` / `worker.completed` / `evidence.recorded` trace events correlated by `traceId`.',
|
|
454
484
|
inputSchema: {
|
|
455
485
|
type: 'object',
|
|
@@ -475,6 +505,7 @@ const ALL_TOOL_DEFS = [
|
|
|
475
505
|
},
|
|
476
506
|
{
|
|
477
507
|
name: 'broker_check',
|
|
508
|
+
outputSchema: { type: 'object' },
|
|
478
509
|
description: 'Queries the MCP broker policy gate for a pending action without executing it. Agents call this BEFORE attempting a high-risk action so the response (allowed / approvalRequired / reason / source) can be surfaced to the user in the agent\'s voice rather than triggering a denial after the fact. In solo mode the broker is off by default — returns `brokerActive: false` with `allowed: true` so the call is cheap and agents don\'t waste tokens on an inactive gate. Always emits a `tool.called` trace event for audit-trail parity. Reads specialists/org fence rules in team / enterprise mode.',
|
|
479
510
|
inputSchema: {
|
|
480
511
|
type: 'object',
|
|
@@ -491,6 +522,7 @@ const ALL_TOOL_DEFS = [
|
|
|
491
522
|
},
|
|
492
523
|
{
|
|
493
524
|
name: 'agent_contract',
|
|
525
|
+
outputSchema: { type: 'object' },
|
|
494
526
|
description: 'Looks up explicit agent-to-agent service contracts (from specialists/org). Specialists should call this at the start of a handoff to see the expected input shape, preconditions, and what postconditions they must satisfy. Use without args to get all contracts; pass producer/consumer to narrow; pass id for a specific contract.',
|
|
495
527
|
inputSchema: {
|
|
496
528
|
type: 'object',
|
|
@@ -503,6 +535,7 @@ const ALL_TOOL_DEFS = [
|
|
|
503
535
|
},
|
|
504
536
|
{
|
|
505
537
|
name: 'find_tool',
|
|
538
|
+
outputSchema: { type: 'object' },
|
|
506
539
|
description: 'Find Construct tools by intent when you do not know the exact name. Describe what you want to do; returns the best-matching tools with their input schemas, ranked by hybrid semantic + lexical relevance. Then invoke a result via the `call` gateway (or directly if it is a flat tool).',
|
|
507
540
|
inputSchema: {
|
|
508
541
|
type: 'object',
|
|
@@ -515,6 +548,7 @@ const ALL_TOOL_DEFS = [
|
|
|
515
548
|
},
|
|
516
549
|
{
|
|
517
550
|
name: 'get_template',
|
|
551
|
+
outputSchema: { type: 'object' },
|
|
518
552
|
description: 'Reads a doc template by name (e.g. "prd", "meta-prd", "prfaq", "evidence-brief", "adr", "runbook"). Resolves .cx/templates/docs/{name}.md first, then templates/docs/{name}.md.',
|
|
519
553
|
inputSchema: {
|
|
520
554
|
type: 'object',
|
|
@@ -526,11 +560,14 @@ const ALL_TOOL_DEFS = [
|
|
|
526
560
|
},
|
|
527
561
|
{
|
|
528
562
|
name: 'list_templates',
|
|
563
|
+
outputSchema: { type: 'object' },
|
|
529
564
|
description: 'Lists shipped and project-override doc templates.',
|
|
530
565
|
inputSchema: { type: 'object', properties: {} },
|
|
531
566
|
},
|
|
532
567
|
{
|
|
533
568
|
name: 'search_skills',
|
|
569
|
+
outputSchema: { type: 'object' },
|
|
570
|
+
category: 'retrieval',
|
|
534
571
|
description: 'Searches for a pattern within the Construct knowledge base skills.',
|
|
535
572
|
inputSchema: {
|
|
536
573
|
type: 'object',
|
|
@@ -542,11 +579,13 @@ const ALL_TOOL_DEFS = [
|
|
|
542
579
|
},
|
|
543
580
|
{
|
|
544
581
|
name: 'list_teams',
|
|
582
|
+
outputSchema: { type: 'object' },
|
|
545
583
|
description: 'Lists all available Construct team templates with members, focus, and promotion gates.',
|
|
546
584
|
inputSchema: { type: 'object', properties: {} },
|
|
547
585
|
},
|
|
548
586
|
{
|
|
549
587
|
name: 'suggest_skills',
|
|
588
|
+
outputSchema: { type: 'object' },
|
|
550
589
|
description: 'Rank skills from the central catalog for a natural-language intent. Optional specialistId filters entitlement metadata.',
|
|
551
590
|
inputSchema: {
|
|
552
591
|
type: 'object',
|
|
@@ -560,6 +599,7 @@ const ALL_TOOL_DEFS = [
|
|
|
560
599
|
},
|
|
561
600
|
{
|
|
562
601
|
name: 'cx_trace',
|
|
602
|
+
outputSchema: { type: 'object' },
|
|
563
603
|
description: 'Records an agent trace for observability. Call at the start of every significant task with your agent name and the user goal.',
|
|
564
604
|
inputSchema: {
|
|
565
605
|
type: 'object',
|
|
@@ -577,6 +617,7 @@ const ALL_TOOL_DEFS = [
|
|
|
577
617
|
},
|
|
578
618
|
{
|
|
579
619
|
name: 'cx_score',
|
|
620
|
+
outputSchema: { type: 'object' },
|
|
580
621
|
description: 'Attaches a quality score to a trace. Call after producing a significant deliverable.',
|
|
581
622
|
inputSchema: {
|
|
582
623
|
type: 'object',
|
|
@@ -591,6 +632,7 @@ const ALL_TOOL_DEFS = [
|
|
|
591
632
|
},
|
|
592
633
|
{
|
|
593
634
|
name: 'cx_trace_update',
|
|
635
|
+
outputSchema: { type: 'object' },
|
|
594
636
|
description: 'Updates an existing trace with output and metadata. Use when a trace was created early but the result becomes available later.',
|
|
595
637
|
inputSchema: {
|
|
596
638
|
type: 'object',
|
|
@@ -604,6 +646,7 @@ const ALL_TOOL_DEFS = [
|
|
|
604
646
|
},
|
|
605
647
|
{
|
|
606
648
|
name: 'session_list',
|
|
649
|
+
outputSchema: { type: 'object' },
|
|
607
650
|
description: 'List construct sessions for the current project. Returns distilled session index entries with id, project, status, and summary.',
|
|
608
651
|
inputSchema: {
|
|
609
652
|
type: 'object',
|
|
@@ -616,6 +659,7 @@ const ALL_TOOL_DEFS = [
|
|
|
616
659
|
},
|
|
617
660
|
{
|
|
618
661
|
name: 'session_load',
|
|
662
|
+
outputSchema: { type: 'object' },
|
|
619
663
|
description: 'Load a full distilled session record by ID. Returns summary, decisions, files changed, open questions, and task snapshot.',
|
|
620
664
|
inputSchema: {
|
|
621
665
|
type: 'object',
|
|
@@ -628,6 +672,7 @@ const ALL_TOOL_DEFS = [
|
|
|
628
672
|
},
|
|
629
673
|
{
|
|
630
674
|
name: 'session_search',
|
|
675
|
+
outputSchema: { type: 'object' },
|
|
631
676
|
description: 'Search sessions by keyword in summary or project name.',
|
|
632
677
|
inputSchema: {
|
|
633
678
|
type: 'object',
|
|
@@ -640,6 +685,7 @@ const ALL_TOOL_DEFS = [
|
|
|
640
685
|
},
|
|
641
686
|
{
|
|
642
687
|
name: 'session_save',
|
|
688
|
+
outputSchema: { type: 'object' },
|
|
643
689
|
description: 'Update the active session with distilled context: summary, decisions, files changed, open questions, task snapshot.',
|
|
644
690
|
inputSchema: {
|
|
645
691
|
type: 'object',
|
|
@@ -658,6 +704,8 @@ const ALL_TOOL_DEFS = [
|
|
|
658
704
|
},
|
|
659
705
|
{
|
|
660
706
|
name: 'memory_search',
|
|
707
|
+
outputSchema: { type: 'object' },
|
|
708
|
+
category: 'retrieval',
|
|
661
709
|
description: 'Search the observation store for patterns, decisions, and insights learned by specialists across sessions. Returns semantically matched observations scoped by role, category, or project.',
|
|
662
710
|
inputSchema: {
|
|
663
711
|
type: 'object',
|
|
@@ -673,6 +721,7 @@ const ALL_TOOL_DEFS = [
|
|
|
673
721
|
},
|
|
674
722
|
{
|
|
675
723
|
name: 'memory_add_observations',
|
|
724
|
+
outputSchema: { type: 'object' },
|
|
676
725
|
description: 'Record observations (patterns, insights, decisions, anti-patterns) that specialists discover during work. These are indexed for semantic search and surface in future sessions.',
|
|
677
726
|
inputSchema: {
|
|
678
727
|
type: 'object',
|
|
@@ -699,6 +748,7 @@ const ALL_TOOL_DEFS = [
|
|
|
699
748
|
},
|
|
700
749
|
{
|
|
701
750
|
name: 'memory_create_entities',
|
|
751
|
+
outputSchema: { type: 'object' },
|
|
702
752
|
description: 'Track recurring entities (components, services, APIs, dependencies) that specialists encounter. Enables "what do we know about X?" queries.',
|
|
703
753
|
inputSchema: {
|
|
704
754
|
type: 'object',
|
|
@@ -723,6 +773,7 @@ const ALL_TOOL_DEFS = [
|
|
|
723
773
|
},
|
|
724
774
|
{
|
|
725
775
|
name: 'memory_recent',
|
|
776
|
+
outputSchema: { type: 'object' },
|
|
726
777
|
description: 'Returns the most recent observations for the current project, deduplicated by (role, summary). Use this when the session-start hint indicates prior observations are available — fetch them on demand instead of paying for them every session.',
|
|
727
778
|
inputSchema: {
|
|
728
779
|
type: 'object',
|
|
@@ -735,6 +786,8 @@ const ALL_TOOL_DEFS = [
|
|
|
735
786
|
},
|
|
736
787
|
{
|
|
737
788
|
name: 'rovo_search',
|
|
789
|
+
outputSchema: { type: 'object' },
|
|
790
|
+
category: 'retrieval',
|
|
738
791
|
description: 'Cross-system semantic search via Atlassian Rovo. Searches Jira, Confluence, and other accessible sources. Returns excerpts with source attribution. Does NOT store results in the observation store. Use for broad queries across all sources, not just focal projects.',
|
|
739
792
|
inputSchema: {
|
|
740
793
|
type: 'object',
|
|
@@ -748,6 +801,7 @@ const ALL_TOOL_DEFS = [
|
|
|
748
801
|
},
|
|
749
802
|
{
|
|
750
803
|
name: 'efficiency_snapshot',
|
|
804
|
+
outputSchema: { type: 'object' },
|
|
751
805
|
description: 'Returns the read-efficiency snapshot for the current session — repeated reads, large reads, hot-spot files, and recommendations. Use this when investigating why a session feels slow or to surface optimization opportunities.',
|
|
752
806
|
inputSchema: {
|
|
753
807
|
type: 'object',
|
|
@@ -758,6 +812,7 @@ const ALL_TOOL_DEFS = [
|
|
|
758
812
|
},
|
|
759
813
|
{
|
|
760
814
|
name: 'session_usage',
|
|
815
|
+
outputSchema: { type: 'object' },
|
|
761
816
|
description: 'Returns locally recorded interaction token and cost usage for the current Construct session.',
|
|
762
817
|
inputSchema: {
|
|
763
818
|
type: 'object',
|
|
@@ -769,6 +824,8 @@ const ALL_TOOL_DEFS = [
|
|
|
769
824
|
},
|
|
770
825
|
{
|
|
771
826
|
name: 'provider_fetch',
|
|
827
|
+
outputSchema: { type: 'object' },
|
|
828
|
+
category: 'retrieval',
|
|
772
829
|
description: 'Look up current data for a configured repo, project, or team. This is an internal lookup against sources the operator has already authorized (set in config.env). Call this immediately — no user approval needed — whenever the user asks about a specific repo, project, or team name (e.g. "what is project iverson", "cloud-reliability status", "PLAT issues"). Pass the user\'s query and the tool resolves the right source automatically. Returns repo metadata, README, docs, open PRs, issues, and recent commits, then stores them as observations.',
|
|
773
830
|
inputSchema: {
|
|
774
831
|
type: 'object',
|
|
@@ -783,6 +840,7 @@ const ALL_TOOL_DEFS = [
|
|
|
783
840
|
},
|
|
784
841
|
{
|
|
785
842
|
name: 'scope_show',
|
|
843
|
+
outputSchema: { type: 'object' },
|
|
786
844
|
description: 'Return the active Construct scope (id, displayName, roles, departments, intake taxonomy, doc templates). Use when a specialist needs to know which role set, classification taxonomy, or doc templates apply before drafting work.',
|
|
787
845
|
inputSchema: {
|
|
788
846
|
type: 'object',
|
|
@@ -794,11 +852,13 @@ const ALL_TOOL_DEFS = [
|
|
|
794
852
|
},
|
|
795
853
|
{
|
|
796
854
|
name: 'scope_list',
|
|
855
|
+
outputSchema: { type: 'object' },
|
|
797
856
|
description: 'List the curated org scope catalog (rnd, operations, creative, research) with role/department counts. Use to discover which scopes are available before suggesting `construct scope set`.',
|
|
798
857
|
inputSchema: { type: 'object', properties: {} },
|
|
799
858
|
},
|
|
800
859
|
{
|
|
801
860
|
name: 'scope_drafts',
|
|
861
|
+
outputSchema: { type: 'object' },
|
|
802
862
|
description: 'List in-progress draft scopes under `.cx/scopes/draft-*` and any user-defined custom scope at `.cx/scope.json`. Use to see what scope work is pending before scaffolding another draft.',
|
|
803
863
|
inputSchema: {
|
|
804
864
|
type: 'object',
|
|
@@ -807,6 +867,7 @@ const ALL_TOOL_DEFS = [
|
|
|
807
867
|
},
|
|
808
868
|
{
|
|
809
869
|
name: 'scope_health',
|
|
870
|
+
outputSchema: { type: 'object' },
|
|
810
871
|
description: 'Per-scope health rollup over a window: observation count, per-role outcome runs and success rates. Use to check whether a scope is producing data before recommending changes or archive.',
|
|
811
872
|
inputSchema: {
|
|
812
873
|
type: 'object',
|
|
@@ -819,6 +880,7 @@ const ALL_TOOL_DEFS = [
|
|
|
819
880
|
},
|
|
820
881
|
{
|
|
821
882
|
name: 'outcomes_summary',
|
|
883
|
+
outputSchema: { type: 'object' },
|
|
822
884
|
description: 'Read `.cx/outcomes/_summary.json` (per-role success rate, 30-day trend). Pass `aggregate=true` to rebuild the summary from JSONL outcome files first. Use to ground tiebreakers and improvement suggestions in real specialist performance.',
|
|
823
885
|
inputSchema: {
|
|
824
886
|
type: 'object',
|
|
@@ -830,6 +892,7 @@ const ALL_TOOL_DEFS = [
|
|
|
830
892
|
},
|
|
831
893
|
{
|
|
832
894
|
name: 'outcomes_record',
|
|
895
|
+
outputSchema: { type: 'object' },
|
|
833
896
|
description: 'Append a specialist outcome line to `.cx/outcomes/<role>.jsonl` (writes durable state — requires `confirm=true`). Use when a specialist wants to self-report success/failure outside the automatic agent-tracker path.',
|
|
834
897
|
inputSchema: {
|
|
835
898
|
type: 'object',
|
|
@@ -850,6 +913,7 @@ const ALL_TOOL_DEFS = [
|
|
|
850
913
|
},
|
|
851
914
|
{
|
|
852
915
|
name: 'knowledge_add',
|
|
916
|
+
outputSchema: { type: 'object' },
|
|
853
917
|
description: 'Persist a research finding as `.cx/knowledge/external/research/<slug>.md` with research-specific frontmatter (topic, confidence, sources, expiresAt, scope). Writes durable state — requires `confirm=true`. `confidence=confirmed` requires at least one source.',
|
|
854
918
|
inputSchema: {
|
|
855
919
|
type: 'object',
|
|
@@ -880,6 +944,7 @@ const ALL_TOOL_DEFS = [
|
|
|
880
944
|
},
|
|
881
945
|
{
|
|
882
946
|
name: 'scope_create',
|
|
947
|
+
outputSchema: { type: 'object' },
|
|
883
948
|
description: 'Scaffold a draft org scope under `.cx/scopes/draft-<id>/` (requirements.md + scope.json + persona stubs + department charters). Writes durable state — requires `confirm=true`. For curated catalog work, follow `docs/guides/concepts/scope-lifecycle.md` after creation.',
|
|
884
949
|
inputSchema: {
|
|
885
950
|
type: 'object',
|
|
@@ -911,6 +976,7 @@ const ALL_TOOL_DEFS = [
|
|
|
911
976
|
},
|
|
912
977
|
{
|
|
913
978
|
name: 'scope_archive',
|
|
979
|
+
outputSchema: { type: 'object' },
|
|
914
980
|
description: 'Archive a curated scope: moves `specialists/org/scopes/<id>.json` and its intake table into `archive/scopes/<id>/` with an archive note. Destructive — requires `confirm=true` and a substantive `reason` (>=8 chars). Observations and outcomes are preserved.',
|
|
915
981
|
inputSchema: {
|
|
916
982
|
type: 'object',
|
|
@@ -924,11 +990,14 @@ const ALL_TOOL_DEFS = [
|
|
|
924
990
|
},
|
|
925
991
|
{
|
|
926
992
|
name: 'sandbox_list',
|
|
993
|
+
outputSchema: { type: 'object' },
|
|
927
994
|
description: 'List Construct sandboxes under `~/.cx/sandboxes/` (id, path, createdAt). Use to find an isolated environment for QA or dry-runs without polluting the active project.',
|
|
928
995
|
inputSchema: { type: 'object', properties: {} },
|
|
929
996
|
},
|
|
930
997
|
{
|
|
931
998
|
name: 'knowledge_graph_ask',
|
|
999
|
+
outputSchema: { type: 'object' },
|
|
1000
|
+
category: 'retrieval',
|
|
932
1001
|
description: 'GraphRAG-style global query over the entity graph in `.cx/observations/`. Detects communities via label propagation, ranks them by BM25 against the query, and returns each top community with its central members and extractive summary. Use for "tell me about how X relates across the project" questions that pure semantic retrieval handles poorly.',
|
|
933
1002
|
inputSchema: {
|
|
934
1003
|
type: 'object',
|
|
@@ -945,6 +1014,7 @@ const ALL_TOOL_DEFS = [
|
|
|
945
1014
|
},
|
|
946
1015
|
{
|
|
947
1016
|
name: 'learning_status',
|
|
1017
|
+
outputSchema: { type: 'object' },
|
|
948
1018
|
description: 'One-shot mirror of `npm run learning:status`: active scope, observation counts (last 24h + total), research finding count, per-role outcome rollup. Use to answer "is Construct learning?" without spawning a shell.',
|
|
949
1019
|
inputSchema: {
|
|
950
1020
|
type: 'object',
|
|
@@ -953,6 +1023,7 @@ const ALL_TOOL_DEFS = [
|
|
|
953
1023
|
},
|
|
954
1024
|
{
|
|
955
1025
|
name: 'document_export',
|
|
1026
|
+
outputSchema: { type: 'object' },
|
|
956
1027
|
description: 'Convert a markdown file into a distributable document — PDF, DOCX, legacy DOC (LibreOffice), deck HTML, PPTX, HTML, RTF, ODT, EPUB, LaTeX, plain text, or Markdown — via Pandoc and Typst (PDF engine) plus optional pptxgenjs/LibreOffice, per ADR-0024. Engines are optional dependencies discovered at runtime — the tool returns a structured "install <binary>" error when tooling is absent, never crashes. Use `detect_only=true` to check availability without running an export.',
|
|
957
1028
|
inputSchema: {
|
|
958
1029
|
type: 'object',
|
|
@@ -968,6 +1039,7 @@ const ALL_TOOL_DEFS = [
|
|
|
968
1039
|
},
|
|
969
1040
|
{
|
|
970
1041
|
name: 'publish_detect',
|
|
1042
|
+
outputSchema: { type: 'object' },
|
|
971
1043
|
description: 'Detect availability of publish pipeline tooling: Pandoc/Typst export, pandoc-ext/diagram figures, and VHS terminal demos.',
|
|
972
1044
|
inputSchema: {
|
|
973
1045
|
type: 'object',
|
|
@@ -980,6 +1052,7 @@ const ALL_TOOL_DEFS = [
|
|
|
980
1052
|
},
|
|
981
1053
|
{
|
|
982
1054
|
name: 'publish_run',
|
|
1055
|
+
outputSchema: { type: 'object' },
|
|
983
1056
|
description: 'Run the publish pipeline: export markdown with optional figure filter and optional demo recordings. Use dry_run=true to probe tooling only.',
|
|
984
1057
|
inputSchema: {
|
|
985
1058
|
type: 'object',
|
|
@@ -1000,6 +1073,8 @@ const ALL_TOOL_DEFS = [
|
|
|
1000
1073
|
},
|
|
1001
1074
|
{
|
|
1002
1075
|
name: 'knowledge_search',
|
|
1076
|
+
outputSchema: { type: 'object' },
|
|
1077
|
+
category: 'retrieval',
|
|
1003
1078
|
description: 'Search Construct\'s own documentation, knowledge base, and distilled embed observations. Call this immediately — no approval needed — when the user asks what Construct is, how a feature works, what commands exist, or anything about Construct\'s architecture or configuration. Also searches embed observations from GitHub, Jira, and other configured sources. Returns relevant excerpts with source file and heading.',
|
|
1004
1079
|
inputSchema: {
|
|
1005
1080
|
type: 'object',
|
|
@@ -1014,6 +1089,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1014
1089
|
},
|
|
1015
1090
|
{
|
|
1016
1091
|
name: 'workflow_init',
|
|
1092
|
+
outputSchema: { type: 'object' },
|
|
1017
1093
|
description: 'Initialize a new workflow for the current project. Creates plan.md state if not already present and returns the initial workflow envelope.',
|
|
1018
1094
|
inputSchema: {
|
|
1019
1095
|
type: 'object',
|
|
@@ -1026,6 +1102,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1026
1102
|
},
|
|
1027
1103
|
{
|
|
1028
1104
|
name: 'workflow_add_task',
|
|
1105
|
+
outputSchema: { type: 'object' },
|
|
1029
1106
|
description: 'Add a task to the current workflow. Pass `request` for intent-based routing (the classifier picks track + specialist) or pass explicit task fields (`key`, `title`, etc.) for manual entry.',
|
|
1030
1107
|
inputSchema: {
|
|
1031
1108
|
type: 'object',
|
|
@@ -1052,6 +1129,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1052
1129
|
},
|
|
1053
1130
|
{
|
|
1054
1131
|
name: 'workflow_update_task',
|
|
1132
|
+
outputSchema: { type: 'object' },
|
|
1055
1133
|
description: 'Update fields on an existing workflow task. Requires the task `key`. Only fields supplied are changed.',
|
|
1056
1134
|
inputSchema: {
|
|
1057
1135
|
type: 'object',
|
|
@@ -1072,6 +1150,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1072
1150
|
},
|
|
1073
1151
|
{
|
|
1074
1152
|
name: 'workflow_needs_main_input',
|
|
1153
|
+
outputSchema: { type: 'object' },
|
|
1075
1154
|
description: 'Mark a workflow task as blocked pending user input. Sets status to blocked_needs_user and writes a packet describing the blocker for the orchestrator to surface.',
|
|
1076
1155
|
inputSchema: {
|
|
1077
1156
|
type: 'object',
|
|
@@ -1087,6 +1166,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1087
1166
|
},
|
|
1088
1167
|
{
|
|
1089
1168
|
name: 'workflow_validate',
|
|
1169
|
+
outputSchema: { type: 'object' },
|
|
1090
1170
|
description: 'Validate the current workflow state against the schema and run consistency checks (no orphan tasks, no circular dependencies, every owner resolves to a known persona).',
|
|
1091
1171
|
inputSchema: {
|
|
1092
1172
|
type: 'object',
|
|
@@ -1097,6 +1177,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1097
1177
|
},
|
|
1098
1178
|
{
|
|
1099
1179
|
name: 'workflow_status',
|
|
1180
|
+
outputSchema: { type: 'object' },
|
|
1100
1181
|
description: 'Return the full workflow snapshot for the current project: tasks, summary, alignment health, and the public-health surface used by the dashboard.',
|
|
1101
1182
|
inputSchema: {
|
|
1102
1183
|
type: 'object',
|
|
@@ -1107,6 +1188,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1107
1188
|
},
|
|
1108
1189
|
{
|
|
1109
1190
|
name: 'workflow_contract_validate',
|
|
1191
|
+
outputSchema: { type: 'object' },
|
|
1110
1192
|
description: 'Validate a producer→consumer handoff against specialists/org. Required when a specialist hands off to another role: enforces input.mustContain, output schema, disk-artifact postconditions, and binary postconditions per producer (rubber-stamp prevention, post-hoc threat-model prevention, etc.). Self-enforcing: a producer with binary rules MUST pass `packet`, or the call itself is a contract violation.',
|
|
1111
1193
|
inputSchema: {
|
|
1112
1194
|
type: 'object',
|
|
@@ -1123,6 +1205,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1123
1205
|
},
|
|
1124
1206
|
{
|
|
1125
1207
|
name: 'workflow_import_plan',
|
|
1208
|
+
outputSchema: { type: 'object' },
|
|
1126
1209
|
description: 'Bulk-add tasks from a markdown plan to the current workflow. Parses headings and bullet structure to extract task titles, owners, and acceptance criteria.',
|
|
1127
1210
|
inputSchema: {
|
|
1128
1211
|
type: 'object',
|
|
@@ -1142,6 +1225,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1142
1225
|
},
|
|
1143
1226
|
{
|
|
1144
1227
|
name: 'cx_trace_telemetry',
|
|
1228
|
+
outputSchema: { type: 'object' },
|
|
1145
1229
|
description: 'Record a single CX telemetry trace for an agent invocation. Use to log start/end, model used, token cost, and outcome verdict for performance review.',
|
|
1146
1230
|
inputSchema: {
|
|
1147
1231
|
type: 'object',
|
|
@@ -1155,6 +1239,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1155
1239
|
},
|
|
1156
1240
|
{
|
|
1157
1241
|
name: 'artifact_workflow',
|
|
1242
|
+
outputSchema: { type: 'object' },
|
|
1158
1243
|
description: 'Plan a manifest-backed document artifact workflow and return a truthful provenance report. It separates planned steps from locally executed validation/export and never claims a host-planned specialist review or rewrite was completed. Durable local export requires approval_mode=allow-durable-write.',
|
|
1159
1244
|
inputSchema: {
|
|
1160
1245
|
type: 'object',
|
|
@@ -1172,6 +1257,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1172
1257
|
},
|
|
1173
1258
|
{
|
|
1174
1259
|
name: 'author_artifact',
|
|
1260
|
+
outputSchema: { type: 'object' },
|
|
1175
1261
|
description: 'Materialize a typed Construct artifact you have drafted (prd, prd-platform, prd-business, meta-prd, adr, rfc, research-brief, evidence-brief, runbook) to disk and run the release gate. YOU draft the full markdown — start with a single # title and include the type\'s required ## sections (call get_template first for the shape) — and pass it as draft_markdown; the canonical file is written and the gate verdict + errors are returned so you can fix and re-call. This is the Construct author→materialize→validate pass for supported hosts.',
|
|
1176
1262
|
inputSchema: {
|
|
1177
1263
|
type: 'object',
|
|
@@ -1185,6 +1271,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1185
1271
|
},
|
|
1186
1272
|
{
|
|
1187
1273
|
name: 'model_resolve',
|
|
1274
|
+
outputSchema: { type: 'object' },
|
|
1188
1275
|
description: 'Resolve which model an embedded Construct workflow should use given the host/IDE provider context. Precedence: host model → same-provider-family fallback → Construct tier default → structured config error. Never reads or returns credential values (requiresCredential is a boolean) and never claims unverified provider health. Read-only; performs no writes.',
|
|
1189
1276
|
inputSchema: {
|
|
1190
1277
|
type: 'object',
|
|
@@ -1201,6 +1288,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1201
1288
|
},
|
|
1202
1289
|
{
|
|
1203
1290
|
name: 'triage_recommend',
|
|
1291
|
+
outputSchema: { type: 'object' },
|
|
1204
1292
|
description: 'Classify an artifact and return a role-aware plan (primary owner, role chain with rationale, suggested skills, evidence requirements, expected outputs, approval requirements, risks, next steps, canExecute) WITHOUT enqueuing or executing. Classification confidence is reported distinctly from any generation confidence. Read-only; performs no durable write.',
|
|
1205
1293
|
inputSchema: {
|
|
1206
1294
|
type: 'object',
|
|
@@ -1218,6 +1306,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1218
1306
|
},
|
|
1219
1307
|
{
|
|
1220
1308
|
name: 'workflow_invoke',
|
|
1309
|
+
outputSchema: { type: 'object' },
|
|
1221
1310
|
description: 'Invoke a named Construct workflow (roles/skills) non-interactively and return a provenanced execution plan: selected roles, rationale, applied skills, resolved model, evidence requirements, output contract, risks, and a traceId. Construct returns the orchestration plan; the host runtime performs specialist reasoning. Durable writes occur ONLY when approval_mode is allow-durable-write; proposal-only and requires-human-approval perform no durable writes.',
|
|
1222
1311
|
inputSchema: {
|
|
1223
1312
|
type: 'object',
|
|
@@ -1239,6 +1328,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1239
1328
|
},
|
|
1240
1329
|
{
|
|
1241
1330
|
name: 'capability_describe',
|
|
1331
|
+
outputSchema: { type: 'object' },
|
|
1242
1332
|
description: 'Describe what this Construct install can do: versions, contract interfaces (CLI/MCP/SDK), roles, skills, workflows, schemas, models/providers, policies, telemetry posture, and plugins. Read-only and secret-free — provider entries carry env-key names and a configured boolean only, never credential values. Reads live registries so the published contract cannot drift from reality.',
|
|
1243
1333
|
inputSchema: {
|
|
1244
1334
|
type: 'object',
|
|
@@ -1249,6 +1339,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1249
1339
|
},
|
|
1250
1340
|
{
|
|
1251
1341
|
name: 'construct_execution_resolve',
|
|
1342
|
+
outputSchema: { type: 'object' },
|
|
1252
1343
|
description: 'Resolve the execution-capability contract for an embedded workflow BEFORE/at workflow start: returns executionMode (construct-orchestrated | construct-prompt-only | host-direct | same-family-fallback), constructCapabilitiesActive (subset of personas/skills/workflow-routing/prompt-envelope), degraded + machine-readable degradationReason, requestedStrategy vs effectiveStrategy, and the resolved provider/model. Descriptive, not enforced: reports what Construct planned and can resolve a model for, never an observation that the host ran personas (see the semantics field). Read-only and secret-free.',
|
|
1253
1344
|
inputSchema: {
|
|
1254
1345
|
type: 'object',
|
|
@@ -1267,6 +1358,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1267
1358
|
},
|
|
1268
1359
|
{
|
|
1269
1360
|
name: 'orchestration_run',
|
|
1361
|
+
outputSchema: { type: 'object' },
|
|
1270
1362
|
description: 'EXECUTE a real multi-specialist orchestration run and return per-specialist output — the executing counterpart to workflow_invoke (which only plans). For MCP hosts with no subagent primitive (VS Code/Copilot, Cursor), this is how you actually run a specialist chain: the engine owns orchestration, this tool is the thin client (ADR-0022). Solo runs execute in-process — no daemon, no port, no token; a remote/team orchestration service is opt-in via CONSTRUCT_ORCHESTRATION_URL. Real specialist output requires the `provider` worker backend (a provider key configured); the default `inline` backend prepares tasks only.',
|
|
1271
1363
|
inputSchema: {
|
|
1272
1364
|
type: 'object',
|
|
@@ -1288,6 +1380,8 @@ const ALL_TOOL_DEFS = [
|
|
|
1288
1380
|
},
|
|
1289
1381
|
{
|
|
1290
1382
|
name: 'web_search',
|
|
1383
|
+
outputSchema: { type: 'object' },
|
|
1384
|
+
category: 'retrieval',
|
|
1291
1385
|
description: 'Search the PUBLIC WEB and return CITED results — the only search surface that reaches the open web, kept distinct from knowledge_search / provider_fetch / repo search so it is never conflated or faked. Requires a governed provider (WEB_SEARCH_URL); without one it returns a typed degradation (capability-unavailable) and zero results, never source/repo results dressed as web. Every result carries a verifiable URL, a claim-relative class, and an Admiralty grade with derived confidence (ADR-0017; high reserved for A1/A2/B1).',
|
|
1292
1386
|
inputSchema: {
|
|
1293
1387
|
type: 'object',
|
|
@@ -1301,6 +1395,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1301
1395
|
},
|
|
1302
1396
|
{
|
|
1303
1397
|
name: 'orchestration_status',
|
|
1398
|
+
outputSchema: { type: 'object' },
|
|
1304
1399
|
description: 'Inspect orchestration runs on the local Construct daemon: pass run_id for the full record (status, per-task status/executor/output/error), or omit it for a list of recent runs. Fails fast if the daemon is unreachable.',
|
|
1305
1400
|
inputSchema: {
|
|
1306
1401
|
type: 'object',
|
|
@@ -1312,6 +1407,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1312
1407
|
},
|
|
1313
1408
|
{
|
|
1314
1409
|
name: 'orchestration_readiness',
|
|
1410
|
+
outputSchema: { type: 'object' },
|
|
1315
1411
|
description: 'Report whether this MCP session has the required Construct orchestration tools attached and reachable now. Returns a pass/fail verdict, typed reasonCode, one next step, required/observed/missing tools, and a redacted diagnostic bundle for support.',
|
|
1316
1412
|
inputSchema: {
|
|
1317
1413
|
type: 'object',
|
|
@@ -1326,7 +1422,7 @@ const ALL_TOOL_DEFS = [
|
|
|
1326
1422
|
},
|
|
1327
1423
|
},
|
|
1328
1424
|
},
|
|
1329
|
-
];
|
|
1425
|
+
].map(withSafetyEnvelope);
|
|
1330
1426
|
|
|
1331
1427
|
// Curated flat core: high-frequency, low-arg tools the orchestrator and the
|
|
1332
1428
|
// built-in Build/Plan agents actually reach for. Everything else collapses
|
|
@@ -1340,15 +1436,22 @@ const CORE_TOOL_NAMES = new Set([
|
|
|
1340
1436
|
'workflow_invoke', 'triage_recommend', 'orchestration_readiness',
|
|
1341
1437
|
]);
|
|
1342
1438
|
|
|
1343
|
-
const LONG_TAIL_DEFS = ALL_TOOL_DEFS.filter((t) => !CORE_TOOL_NAMES.has(t.name));
|
|
1344
1439
|
const KNOWN_TOOL_NAMES = new Set(ALL_TOOL_DEFS.map((t) => t.name));
|
|
1345
1440
|
|
|
1441
|
+
// The core/long-tail split is hand-maintained beside the catalog; a typo in a
|
|
1442
|
+
// core name would silently drop a tool from both the flat surface and the gateway
|
|
1443
|
+
// enum. Fail fast at module load against the catalog as the single source of truth.
|
|
1444
|
+
|
|
1445
|
+
assertCoreSubsetOfCatalog(CORE_TOOL_NAMES, KNOWN_TOOL_NAMES);
|
|
1446
|
+
|
|
1447
|
+
const LONG_TAIL_DEFS = ALL_TOOL_DEFS.filter((t) => !CORE_TOOL_NAMES.has(t.name));
|
|
1448
|
+
|
|
1346
1449
|
// One dispatcher for the long tail. `tool` is constrained to an enum of valid
|
|
1347
1450
|
// names (≈1 token each — kills hallucinated names, the key small-model lever),
|
|
1348
1451
|
// and the description carries a compact one-line catalog instead of ~10k of full
|
|
1349
1452
|
// schemas. Dispatch reuses the same handlers via dispatchToolByName.
|
|
1350
1453
|
|
|
1351
|
-
const CONSTRUCT_CALL_TOOL = {
|
|
1454
|
+
const CONSTRUCT_CALL_TOOL = withSafetyEnvelope({
|
|
1352
1455
|
name: 'call',
|
|
1353
1456
|
description:
|
|
1354
1457
|
'Invoke any non-core Construct tool by name: provide `tool` and `args`. '
|
|
@@ -1363,7 +1466,7 @@ const CONSTRUCT_CALL_TOOL = {
|
|
|
1363
1466
|
},
|
|
1364
1467
|
required: ['tool'],
|
|
1365
1468
|
},
|
|
1366
|
-
};
|
|
1469
|
+
});
|
|
1367
1470
|
|
|
1368
1471
|
export function exposedTools() {
|
|
1369
1472
|
return [...ALL_TOOL_DEFS.filter((t) => CORE_TOOL_NAMES.has(t.name)), CONSTRUCT_CALL_TOOL];
|
|
@@ -1543,14 +1646,33 @@ async function resolveUnknownToolName(name, args = {}) {
|
|
|
1543
1646
|
return { error: `Unknown tool: ${name}. Core tools are flat; invoke any other tool via { name: 'call', arguments: { tool: '<name>', args: {…} } }.` };
|
|
1544
1647
|
}
|
|
1545
1648
|
|
|
1649
|
+
// Solo mode never instantiates lib/mcp/broker.mjs's Broker (role/policy-based,
|
|
1650
|
+
// wired only for team/enterprise), so the live dispatch path otherwise has no
|
|
1651
|
+
// rate bound and no audit trail in the default deployment. windowMs 0 disables,
|
|
1652
|
+
// mirroring the CONSTRUCT_MCP_TOOL_TIMEOUT_MS override convention below.
|
|
1653
|
+
|
|
1654
|
+
const toolRateLimiter = new ToolRateLimiter({
|
|
1655
|
+
windowMs: (() => {
|
|
1656
|
+
const raw = Number(process.env.CONSTRUCT_MCP_TOOL_RATE_WINDOW_MS);
|
|
1657
|
+
return Number.isFinite(raw) && raw >= 0 ? raw : undefined;
|
|
1658
|
+
})(),
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1546
1661
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1547
1662
|
const { name, arguments: args = {} } = request.params;
|
|
1663
|
+
const callStart = Date.now();
|
|
1548
1664
|
|
|
1549
1665
|
// Extract W3C traceparent from params._meta (SEP-414 propagation). Tracing
|
|
1550
1666
|
// must never break dispatch — a malformed _meta should not fail the call.
|
|
1551
1667
|
let parentCtx = {};
|
|
1552
1668
|
try { parentCtx = await extractTraceContext(request.params?._meta || {}); } catch { /* tracing optional */ }
|
|
1553
|
-
|
|
1669
|
+
|
|
1670
|
+
// The `call` gateway's own class would conflate every long-tail tool's budget
|
|
1671
|
+
// into one bucket; rate-limit and audit-log the real underlying tool instead.
|
|
1672
|
+
|
|
1673
|
+
const innerTool = name === 'call' && typeof args?.tool === 'string' && !isGatewayName(args.tool) ? args.tool : null;
|
|
1674
|
+
const auditedTool = innerTool ?? name;
|
|
1675
|
+
const safetyClass = TOOL_SAFETY[auditedTool]?.class ?? 'read';
|
|
1554
1676
|
|
|
1555
1677
|
// Bound every tool call. A tool that stalls (a stuck external extractor, a slow
|
|
1556
1678
|
// model load, a wedged subprocess) must surface a clean timeout error to the
|
|
@@ -1561,38 +1683,84 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1561
1683
|
return Number.isFinite(raw) && raw >= 0 ? raw : 120_000;
|
|
1562
1684
|
})();
|
|
1563
1685
|
|
|
1564
|
-
const dispatch = (async () => {
|
|
1565
|
-
let result;
|
|
1566
|
-
try {
|
|
1567
|
-
result = await dispatchToolByName(name, args);
|
|
1568
|
-
} catch (err) {
|
|
1569
|
-
result = { error: err.message ?? String(err) };
|
|
1570
|
-
}
|
|
1571
|
-
return result;
|
|
1572
|
-
})();
|
|
1573
|
-
|
|
1574
1686
|
let toolResult;
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1687
|
+
try {
|
|
1688
|
+
toolRateLimiter.check(auditedTool, safetyClass);
|
|
1689
|
+
|
|
1690
|
+
// A GenAI span per dispatch records the real underlying tool, its safety
|
|
1691
|
+
// class, and the serialized result size (a token proxy) for every call, so
|
|
1692
|
+
// per-tool calls/latency/errors are measured. Tracing never fails the call:
|
|
1693
|
+
// on any dispatch error the inner catch resolves to an { error } object, so
|
|
1694
|
+
// the span closes OK and the client still gets a structured error payload.
|
|
1695
|
+
|
|
1696
|
+
toolResult = await withGenAiSpan(
|
|
1697
|
+
`execute_tool ${auditedTool}`,
|
|
1698
|
+
{ [GenAiAttrs.TOOL_NAME]: auditedTool, 'construct.tool.safety_class': safetyClass, [GenAiAttrs.MCP_METHOD]: 'tools/call' },
|
|
1699
|
+
async (span) => {
|
|
1700
|
+
const dispatch = (async () => {
|
|
1701
|
+
let result;
|
|
1702
|
+
try {
|
|
1703
|
+
result = await dispatchToolByName(name, args);
|
|
1704
|
+
} catch (err) {
|
|
1705
|
+
result = { error: err.message ?? String(err) };
|
|
1706
|
+
}
|
|
1707
|
+
return result;
|
|
1708
|
+
})();
|
|
1709
|
+
|
|
1710
|
+
let out;
|
|
1711
|
+
if (!TOOL_TIMEOUT_MS) {
|
|
1712
|
+
out = await dispatch;
|
|
1713
|
+
} else {
|
|
1714
|
+
let timer;
|
|
1715
|
+
const timeout = new Promise((_, reject) => {
|
|
1716
|
+
timer = setTimeout(
|
|
1717
|
+
() => reject(new Error(`tool ${name} timed out after ${Math.round(TOOL_TIMEOUT_MS / 1000)}s`)),
|
|
1718
|
+
TOOL_TIMEOUT_MS,
|
|
1719
|
+
);
|
|
1720
|
+
});
|
|
1721
|
+
try {
|
|
1722
|
+
out = await Promise.race([dispatch, timeout]);
|
|
1723
|
+
} catch (err) {
|
|
1724
|
+
out = { error: err.message ?? String(err) };
|
|
1725
|
+
} finally {
|
|
1726
|
+
clearTimeout(timer);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const isError = Boolean(out && typeof out === 'object' && 'error' in out);
|
|
1731
|
+
span.setAttribute('construct.tool.result_bytes', JSON.stringify(out ?? null).length);
|
|
1732
|
+
span.setAttribute('construct.tool.ok', !isError);
|
|
1733
|
+
return out;
|
|
1734
|
+
},
|
|
1735
|
+
parentCtx,
|
|
1736
|
+
);
|
|
1737
|
+
} catch (err) {
|
|
1738
|
+
if (err instanceof ToolRateLimited) toolResult = { error: err.message };
|
|
1739
|
+
else throw err;
|
|
1592
1740
|
}
|
|
1593
1741
|
|
|
1742
|
+
// Value-free: tool name and safety class only, never call args or result
|
|
1743
|
+
// content. Logging must never break dispatch, matching the tracing guard above.
|
|
1744
|
+
|
|
1745
|
+
try {
|
|
1746
|
+
appendAuditRecord({
|
|
1747
|
+
ts: new Date().toISOString(),
|
|
1748
|
+
agent: 'mcp-server',
|
|
1749
|
+
tool: auditedTool,
|
|
1750
|
+
target: safetyClass,
|
|
1751
|
+
ok: !(toolResult && typeof toolResult === 'object' && 'error' in toolResult),
|
|
1752
|
+
duration_ms: Date.now() - callStart,
|
|
1753
|
+
});
|
|
1754
|
+
} catch { /* audit trail unavailable must not fail the call */ }
|
|
1755
|
+
|
|
1756
|
+
// Every tool now declares an outputSchema (see withSafetyEnvelope); the MCP SDK
|
|
1757
|
+
// client validates that declaration against the response and rejects a tool
|
|
1758
|
+
// call whose result omits structuredContent. toolResult is always a JSON object
|
|
1759
|
+
// across every dispatch branch, so it satisfies each tool's schema directly.
|
|
1760
|
+
|
|
1594
1761
|
return {
|
|
1595
1762
|
content: [{ type: 'text', text: JSON.stringify(toolResult, null, 2) }],
|
|
1763
|
+
structuredContent: toolResult,
|
|
1596
1764
|
};
|
|
1597
1765
|
});
|
|
1598
1766
|
|
|
@@ -1619,6 +1787,12 @@ const argv1Real = (() => { try { return realpathSync(process.argv[1]); } catch {
|
|
|
1619
1787
|
if (fileURLToPath(import.meta.url) === argv1Real) {
|
|
1620
1788
|
console.error('[construct-mcp] server started');
|
|
1621
1789
|
|
|
1790
|
+
// Orchestration and provider-backed tools resolve credentials in the server's
|
|
1791
|
+
// own process; wiring the sink at the entry records those op reads on the same
|
|
1792
|
+
// trail as the CLI. Scoped to the server entry so importing this module for a
|
|
1793
|
+
// test never installs the process-global sink.
|
|
1794
|
+
enableSecretAuditTrail();
|
|
1795
|
+
|
|
1622
1796
|
// A long-running stdio server must survive a single malformed request: a
|
|
1623
1797
|
// background rejection from one ingest (e.g. a broken docling sidecar that
|
|
1624
1798
|
// settles late) must not terminate the process and close the client
|