@martinloop/mcp 0.2.7 → 0.3.1
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/README.md +49 -104
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/prompts.d.ts +1 -1
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +2 -2
- package/dist/server-validation.d.ts +1 -0
- package/dist/server-validation.js +8 -0
- package/dist/server.js +87 -9
- package/dist/tools/doctor.d.ts +39 -1
- package/dist/tools/doctor.js +68 -9
- package/dist/tools/eval.js +3 -2
- package/dist/tools/get-run.d.ts +3 -0
- package/dist/tools/get-run.js +3 -1
- package/dist/tools/get-verification-results.d.ts +3 -0
- package/dist/tools/get-verification-results.js +3 -1
- package/dist/tools/plan.js +4 -2
- package/dist/tools/pr-tools.js +2 -1
- package/dist/tools/preflight.d.ts +41 -1
- package/dist/tools/preflight.js +74 -19
- package/dist/tools/run-dossier.d.ts +3 -0
- package/dist/tools/run-dossier.js +5 -2
- package/dist/tools/run-loop.d.ts +7 -2
- package/dist/tools/run-loop.js +67 -35
- package/dist/tools/run-store.js +67 -15
- package/dist/tools/tool-errors.js +1 -1
- package/dist/tools/tool-support.d.ts +8 -3
- package/dist/tools/tool-support.js +61 -18
- package/dist/tools/workflow-governance.d.ts +19 -3
- package/dist/tools/workflow-governance.js +107 -55
- package/dist/vendor/adapters/claude-cli.d.ts +45 -3
- package/dist/vendor/adapters/claude-cli.js +465 -45
- package/dist/vendor/adapters/cli-bridge.d.ts +46 -0
- package/dist/vendor/adapters/cli-bridge.js +147 -38
- package/dist/vendor/adapters/codex-launcher.d.ts +76 -0
- package/dist/vendor/adapters/codex-launcher.js +538 -0
- package/dist/vendor/adapters/index.d.ts +3 -2
- package/dist/vendor/adapters/index.js +3 -2
- package/dist/vendor/adapters/openai-compatible.d.ts +19 -4
- package/dist/vendor/adapters/openai-compatible.js +50 -19
- package/dist/vendor/adapters/runtime-support.d.ts +3 -0
- package/dist/vendor/adapters/runtime-support.js +9 -1
- package/dist/vendor/adapters/stub-direct-provider.js +3 -0
- package/dist/vendor/adapters/verifier-only.d.ts +2 -0
- package/dist/vendor/adapters/verifier-only.js +11 -4
- package/dist/vendor/contracts/index.d.ts +39 -0
- package/dist/vendor/contracts/index.js +2 -0
- package/dist/vendor/core/context-integrity.js +28 -3
- package/dist/vendor/core/grounding.d.ts +1 -0
- package/dist/vendor/core/grounding.js +6 -2
- package/dist/vendor/core/index.d.ts +24 -3
- package/dist/vendor/core/index.js +113 -21
- package/dist/vendor/core/leash.js +85 -8
- package/dist/vendor/core/persistence/index.d.ts +2 -0
- package/dist/vendor/core/persistence/index.js +1 -0
- package/dist/vendor/core/persistence/integrity.d.ts +38 -0
- package/dist/vendor/core/persistence/integrity.js +248 -0
- package/dist/vendor/core/persistence/store.d.ts +7 -0
- package/dist/vendor/core/persistence/store.js +25 -1
- package/dist/vendor/core/policy.d.ts +9 -0
- package/dist/workflow-state.d.ts +9 -0
- package/dist/workflow-state.js +46 -3
- package/package.json +2 -2
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -1,79 +1,21 @@
|
|
|
1
1
|
# @martinloop/mcp
|
|
2
2
|
|
|
3
|
-
Governed MCP server for AI coding agents
|
|
3
|
+
Governed MCP server for AI coding agents over local stdio.
|
|
4
4
|
|
|
5
|
-
`@martinloop/mcp` is the
|
|
5
|
+
`@martinloop/mcp@0.3.0` is the live public baseline today. `0.3.1` is the current in-repo release candidate for the next public cut.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This package stays local-first and stdio-first in the public OSS lane.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Public release train
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- `0.2.7` made the guided MCP workflow easier to adopt and harder to misuse.
|
|
12
|
+
- `0.3.0` is the live adoption baseline that made host setup and onboarding clearer.
|
|
13
|
+
- `0.3.1` is the review and handoff release candidate currently staged in-repo.
|
|
14
|
+
- `0.3.2` remains the planned follow-on for opt-in execution controls.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
## What ships today
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
npx martin-loop start
|
|
19
|
-
npx martin-loop tour
|
|
20
|
-
npx martin-loop doctor
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Install
|
|
24
|
-
|
|
25
|
-
Run the packaged server directly:
|
|
26
|
-
|
|
27
|
-
```sh
|
|
28
|
-
npx -y @martinloop/mcp
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Add it to Codex:
|
|
32
|
-
|
|
33
|
-
```sh
|
|
34
|
-
codex mcp add martin-loop -- npx -y @martinloop/mcp
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Add it to Claude Code:
|
|
38
|
-
|
|
39
|
-
```sh
|
|
40
|
-
claude mcp add --transport stdio --scope user martin-loop -- npx -y @martinloop/mcp
|
|
41
|
-
claude mcp add --transport stdio --scope user martin-loop -- cmd /c npx -y @martinloop/mcp
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Generate host config from the root CLI:
|
|
45
|
-
|
|
46
|
-
```sh
|
|
47
|
-
npx martin-loop mcp print-config --host codex --transport stdio --profile minimal
|
|
48
|
-
npx martin-loop mcp print-config --host claude --transport stdio --profile diagnostic
|
|
49
|
-
npx martin-loop mcp print-config --host gemini --transport stdio --profile full-local
|
|
50
|
-
npx martin-loop mcp print-config --host generic --transport stdio --profile github-review
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Registry/server identifier: `io.github.Keesan12/martin-loop`
|
|
54
|
-
|
|
55
|
-
## Recommended Flow
|
|
56
|
-
|
|
57
|
-
1. `martin_doctor`
|
|
58
|
-
2. `martin_plan`
|
|
59
|
-
3. `martin_preflight`
|
|
60
|
-
4. `martin_run`
|
|
61
|
-
5. `martin_status` or `martin_logs`
|
|
62
|
-
6. `martin_dossier` or the `martin_get_*` tools
|
|
63
|
-
7. `martin_eval`
|
|
64
|
-
8. `martin_pr_summary` or `martin_review_pr` when a host is preparing GitHub review output
|
|
65
|
-
|
|
66
|
-
`martin_run` is the primary coding execution entrypoint. In `0.2.7`, it hard-blocks until the matching readiness, planning, and preflight receipts exist for the same task. That keeps the default flow honest for both humans and agents.
|
|
67
|
-
|
|
68
|
-
## Profiles
|
|
69
|
-
|
|
70
|
-
- `minimal`: read-heavy default for safe host setup
|
|
71
|
-
- `diagnostic`: deeper inspection and evaluator support
|
|
72
|
-
- `full-local`: includes execution and run-control helpers for local workflows
|
|
73
|
-
- `github-review`: adds PR review helpers for GitHub-oriented hosts
|
|
74
|
-
- `starter` and `full`: compatibility aliases that map onto the same discovery surface
|
|
75
|
-
|
|
76
|
-
## Tools
|
|
18
|
+
### Tools
|
|
77
19
|
|
|
78
20
|
- `martin_doctor`
|
|
79
21
|
- `martin_plan`
|
|
@@ -97,7 +39,7 @@ Registry/server identifier: `io.github.Keesan12/martin-loop`
|
|
|
97
39
|
- `martin_create_pr`
|
|
98
40
|
- `martin_review_pr`
|
|
99
41
|
|
|
100
|
-
|
|
42
|
+
### Resources
|
|
101
43
|
|
|
102
44
|
- `martin://server/health`
|
|
103
45
|
- `martin://runs/recent`
|
|
@@ -114,19 +56,9 @@ Registry/server identifier: `io.github.Keesan12/martin-loop`
|
|
|
114
56
|
- `martin://agent/next-step`
|
|
115
57
|
- `martin://guides/mcp-usage`
|
|
116
58
|
- `martin://guides/agent-start`
|
|
117
|
-
- `martin://guides/command-map`
|
|
118
|
-
- `martin://guides/ide-onboarding`
|
|
119
|
-
- `martin://guides/operating-rules`
|
|
120
59
|
- `martin://guides/publish-readiness`
|
|
121
60
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- `martin://runs/{loopId}`
|
|
125
|
-
- `martin://runs/{loopId}/dossier`
|
|
126
|
-
- `martin://runs/{loopId}/attempts/{attemptIndex}`
|
|
127
|
-
- `martin://runs/{loopId}/verification`
|
|
128
|
-
|
|
129
|
-
## Prompts
|
|
61
|
+
### Prompts
|
|
130
62
|
|
|
131
63
|
- `martin_start`
|
|
132
64
|
- `martin_preflight`
|
|
@@ -138,40 +70,56 @@ Registry/server identifier: `io.github.Keesan12/martin-loop`
|
|
|
138
70
|
- `martin_debug_failed_run`
|
|
139
71
|
- `martin_publish_readiness_review`
|
|
140
72
|
- `martin_triage_run_store`
|
|
141
|
-
- `safe_bug_fix`
|
|
142
|
-
- `write_tests_first`
|
|
143
|
-
- `small_refactor`
|
|
144
|
-
- `security_review`
|
|
145
|
-
- `pr_review`
|
|
146
|
-
- `release_check`
|
|
147
73
|
|
|
148
|
-
##
|
|
74
|
+
## Recommended flow
|
|
149
75
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
76
|
+
1. `martin_doctor`
|
|
77
|
+
2. `martin_plan`
|
|
78
|
+
3. `martin_preflight`
|
|
79
|
+
4. `martin_run`
|
|
80
|
+
5. `martin_status` or `martin_logs`
|
|
81
|
+
6. `martin_dossier`
|
|
82
|
+
7. `martin_eval`
|
|
157
83
|
|
|
158
|
-
##
|
|
84
|
+
## Install
|
|
159
85
|
|
|
160
|
-
|
|
86
|
+
```sh
|
|
87
|
+
npx -y @martinloop/mcp
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Codex:
|
|
161
91
|
|
|
162
92
|
```sh
|
|
163
|
-
|
|
93
|
+
codex mcp add martin-loop -- npx -y @martinloop/mcp
|
|
164
94
|
```
|
|
165
95
|
|
|
166
|
-
|
|
96
|
+
Claude Code:
|
|
167
97
|
|
|
168
98
|
```sh
|
|
169
|
-
|
|
99
|
+
# macOS/Linux
|
|
100
|
+
claude mcp add --transport stdio --scope user martin-loop -- npx -y @martinloop/mcp
|
|
101
|
+
|
|
102
|
+
# Windows PowerShell or cmd.exe
|
|
103
|
+
claude mcp add --transport stdio --scope user martin-loop -- cmd /c npx -y @martinloop/mcp
|
|
170
104
|
```
|
|
171
105
|
|
|
172
|
-
|
|
106
|
+
If you want generated host config, use the MartinLoop CLI:
|
|
173
107
|
|
|
174
|
-
|
|
108
|
+
```sh
|
|
109
|
+
martin mcp print-config --host codex --transport stdio --profile minimal
|
|
110
|
+
martin mcp print-config --host claude --transport stdio --profile diagnostic
|
|
111
|
+
martin mcp print-config --host gemini --transport stdio --profile full-local
|
|
112
|
+
martin mcp print-config --host generic --transport stdio --profile github-review
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Compatibility posture
|
|
116
|
+
|
|
117
|
+
- `martin_run` remains the single execution entrypoint.
|
|
118
|
+
- Read-only inspection stays available without execution-capable profiles.
|
|
119
|
+
- The OSS package stays focused on local stdio workflows. Hosted and team features live on a separate product track.
|
|
120
|
+
- Later `0.3.x` releases should widen adoption and guidance without blurring those boundaries.
|
|
121
|
+
|
|
122
|
+
## Verification
|
|
175
123
|
|
|
176
124
|
```sh
|
|
177
125
|
pnpm --filter @martinloop/mcp lint
|
|
@@ -180,7 +128,4 @@ pnpm --filter @martinloop/mcp build
|
|
|
180
128
|
pnpm --filter @martinloop/mcp smoke:pack
|
|
181
129
|
pnpm --filter @martinloop/mcp smoke:published:pack
|
|
182
130
|
pnpm --filter @martinloop/mcp verify:release
|
|
183
|
-
pnpm --filter @martin/cli verify:hosts:live
|
|
184
131
|
```
|
|
185
|
-
|
|
186
|
-
See [MCP setup](../../docs/getting-started/mcp.md), [MCP tool reference](../../docs/reference/mcp-tools.md), and [MCP compatibility](../../docs/reference/mcp-compatibility.md).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MARTIN_MCP_PACKAGE_VERSION = "0.
|
|
1
|
+
export declare const MARTIN_MCP_PACKAGE_VERSION = "0.3.1";
|
package/dist/package-version.js
CHANGED
package/dist/prompts.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface MartinGetPromptInput {
|
|
|
5
5
|
arguments?: Record<string, string>;
|
|
6
6
|
runsDir?: string;
|
|
7
7
|
workingDirectory?: string;
|
|
8
|
-
engine?: "claude" | "codex";
|
|
8
|
+
engine?: "claude" | "codex" | "gemini";
|
|
9
9
|
}
|
|
10
10
|
export declare function listMartinPrompts(): {
|
|
11
11
|
prompts: Prompt[];
|
package/dist/resources.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface MartinReadResourceInput {
|
|
|
26
26
|
uri: string;
|
|
27
27
|
runsDir?: string;
|
|
28
28
|
workingDirectory?: string;
|
|
29
|
-
engine?: "claude" | "codex";
|
|
29
|
+
engine?: "claude" | "codex" | "gemini";
|
|
30
30
|
}
|
|
31
31
|
export declare function listMartinResources(): {
|
|
32
32
|
resources: Resource[];
|
package/dist/resources.js
CHANGED
|
@@ -338,7 +338,7 @@ async function loadLatestRunForCompactResource(runsRoot) {
|
|
|
338
338
|
empty: true,
|
|
339
339
|
warnings: [
|
|
340
340
|
"No Martin run records were found yet.",
|
|
341
|
-
"Run `npx martin-loop demo`, then run
|
|
341
|
+
"Run `npx martin-loop demo`, then run `npx martin-loop run ... --proof --verify <command>` for a no-spend local proof pass."
|
|
342
342
|
]
|
|
343
343
|
};
|
|
344
344
|
}
|
|
@@ -561,7 +561,7 @@ function compactEmptyState(kind, runsRoot, warnings) {
|
|
|
561
561
|
status: "empty",
|
|
562
562
|
runsRoot,
|
|
563
563
|
summary: "No Martin run records are available yet.",
|
|
564
|
-
nextStep: "Run `martin doctor`, create the demo workspace with `npx martin-loop demo`, then run
|
|
564
|
+
nextStep: "Run `npx martin-loop doctor`, create the demo workspace with `npx martin-loop demo`, then run `npx martin-loop run ... --proof --verify <command>`.",
|
|
565
565
|
warnings
|
|
566
566
|
};
|
|
567
567
|
}
|
|
@@ -2,6 +2,7 @@ type ToolName = "martin_run" | "martin_inspect" | "martin_status" | "martin_doct
|
|
|
2
2
|
export { sanitizeToolErrorMessage } from "./tools/tool-errors.js";
|
|
3
3
|
export declare function validateToolInput(name: ToolName, args: unknown): unknown;
|
|
4
4
|
export declare function resolveSafeRepoRoot(repoRoot?: string, workspaceRoot?: string): string;
|
|
5
|
+
export declare function resolveTrustedLoopRepoRoot(repoRoot?: string, workspaceRoot?: string): string;
|
|
5
6
|
export declare function resolveSafeRunsJsonPath(file: string, runsRoot?: string): string;
|
|
6
7
|
export declare function resolveSafeRunsPath(file: string, runsRoot?: string): string;
|
|
7
8
|
export declare function resolveSafeRunsRootPath(runsRoot?: string, fallbackRunsRoot?: string): string;
|
|
@@ -57,6 +57,14 @@ export function resolveSafeRepoRoot(repoRoot, workspaceRoot = process.env.MARTIN
|
|
|
57
57
|
});
|
|
58
58
|
return candidate;
|
|
59
59
|
}
|
|
60
|
+
export function resolveTrustedLoopRepoRoot(repoRoot, workspaceRoot = process.env.MARTIN_MCP_WORKSPACE_ROOT ?? process.cwd()) {
|
|
61
|
+
try {
|
|
62
|
+
return resolveSafeRepoRoot(repoRoot, workspaceRoot);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
throw invalidPathError("Run record points outside the trusted workspace.", "Inspect or promote only loop records that were created under the current workspace root.");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
60
68
|
export function resolveSafeRunsJsonPath(file, runsRoot = resolveRunsRoot(process.env)) {
|
|
61
69
|
const baseRoot = resolve(runsRoot);
|
|
62
70
|
const candidate = resolve(baseRoot, file);
|
package/dist/server.js
CHANGED
|
@@ -44,8 +44,9 @@ import { martinTriageRunsTool } from "./tools/triage-runs.js";
|
|
|
44
44
|
import { runLoopTool } from "./tools/run-loop.js";
|
|
45
45
|
import { createToolErrorResult, createToolSuccessResult } from "./tools/tool-response.js";
|
|
46
46
|
import { MartinToolError, toToolFailure } from "./tools/tool-errors.js";
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
47
|
+
import { normalizeLoopBudget } from "./tools/workflow-governance.js";
|
|
48
|
+
import { resolveSafeRepoRoot, sanitizeToolErrorMessage, validateToolInput } from "./server-validation.js";
|
|
49
|
+
import { evaluateMcpRunGate, recordMcpWorkflowStep } from "./workflow-state.js";
|
|
49
50
|
const stringArraySchema = {
|
|
50
51
|
type: "array",
|
|
51
52
|
items: { type: "string" }
|
|
@@ -122,10 +123,37 @@ const verificationSchema = {
|
|
|
122
123
|
latestAttemptIndex: { type: "integer" },
|
|
123
124
|
completedAt: { type: "string" },
|
|
124
125
|
summary: { type: "string" },
|
|
126
|
+
steps: {
|
|
127
|
+
type: "array",
|
|
128
|
+
items: {
|
|
129
|
+
type: "object",
|
|
130
|
+
additionalProperties: true,
|
|
131
|
+
properties: {
|
|
132
|
+
command: { type: "string" },
|
|
133
|
+
launched: { type: "boolean" },
|
|
134
|
+
exitCode: { type: "integer" },
|
|
135
|
+
timedOut: { type: "boolean" },
|
|
136
|
+
fastFail: { type: "boolean" },
|
|
137
|
+
detail: { type: "string" }
|
|
138
|
+
},
|
|
139
|
+
required: ["command", "launched"]
|
|
140
|
+
}
|
|
141
|
+
},
|
|
125
142
|
warnings: stringArraySchema
|
|
126
143
|
},
|
|
127
144
|
required: ["status", "eventCount", "ledgerEventCount", "warnings"]
|
|
128
145
|
};
|
|
146
|
+
const receiptScopeSchema = {
|
|
147
|
+
type: "object",
|
|
148
|
+
additionalProperties: false,
|
|
149
|
+
properties: {
|
|
150
|
+
invocationRoot: { type: "string" },
|
|
151
|
+
workingDirectory: { type: "string" },
|
|
152
|
+
repoRoot: { type: "string" },
|
|
153
|
+
runsRoot: { type: "string" }
|
|
154
|
+
},
|
|
155
|
+
required: ["invocationRoot", "workingDirectory", "repoRoot", "runsRoot"]
|
|
156
|
+
};
|
|
129
157
|
const artifactSummarySchema = {
|
|
130
158
|
type: "object",
|
|
131
159
|
additionalProperties: true,
|
|
@@ -370,11 +398,12 @@ const doctorOutputSchema = {
|
|
|
370
398
|
workspaceRoot: { type: "string" },
|
|
371
399
|
workingDirectory: { type: "string" },
|
|
372
400
|
runsRoot: { type: "string" },
|
|
373
|
-
mode: { type: "string", enum: ["live", "
|
|
401
|
+
mode: { type: "string", enum: ["live", "proof"] },
|
|
374
402
|
liveMode: { type: "boolean" }
|
|
375
403
|
},
|
|
376
404
|
required: ["workspaceRoot", "workingDirectory", "runsRoot", "mode", "liveMode"]
|
|
377
405
|
},
|
|
406
|
+
receiptScope: receiptScopeSchema,
|
|
378
407
|
engines: {
|
|
379
408
|
type: "object",
|
|
380
409
|
additionalProperties: true
|
|
@@ -392,7 +421,7 @@ const doctorOutputSchema = {
|
|
|
392
421
|
},
|
|
393
422
|
warnings: stringArraySchema
|
|
394
423
|
},
|
|
395
|
-
required: ["status", "summary", "server", "environment", "engines", "runStore", "warnings"]
|
|
424
|
+
required: ["status", "summary", "server", "environment", "receiptScope", "engines", "runStore", "warnings"]
|
|
396
425
|
};
|
|
397
426
|
const preflightOutputSchema = {
|
|
398
427
|
type: "object",
|
|
@@ -401,11 +430,12 @@ const preflightOutputSchema = {
|
|
|
401
430
|
ok: { type: "boolean" },
|
|
402
431
|
summary: { type: "string" },
|
|
403
432
|
warnings: stringArraySchema,
|
|
433
|
+
receiptScope: receiptScopeSchema,
|
|
404
434
|
readiness: {
|
|
405
435
|
type: "object",
|
|
406
436
|
additionalProperties: false,
|
|
407
437
|
properties: {
|
|
408
|
-
mode: { type: "string", enum: ["live", "
|
|
438
|
+
mode: { type: "string", enum: ["live", "proof"] },
|
|
409
439
|
liveMode: { type: "boolean" },
|
|
410
440
|
engineReady: { type: "boolean" }
|
|
411
441
|
},
|
|
@@ -476,7 +506,7 @@ const preflightOutputSchema = {
|
|
|
476
506
|
required: ["requestedEngine", "engineAvailability", "runsRoot", "pathScope", "expectedRunLayout"]
|
|
477
507
|
}
|
|
478
508
|
},
|
|
479
|
-
required: ["ok", "summary", "warnings", "readiness", "normalized", "execution"]
|
|
509
|
+
required: ["ok", "summary", "warnings", "receiptScope", "readiness", "normalized", "execution"]
|
|
480
510
|
};
|
|
481
511
|
const listRunsOutputSchema = {
|
|
482
512
|
type: "object",
|
|
@@ -1381,6 +1411,36 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1381
1411
|
try {
|
|
1382
1412
|
if (name === "martin_run") {
|
|
1383
1413
|
const input = validateToolInput("martin_run", args);
|
|
1414
|
+
const runsRoot = resolveRunsRoot(process.env);
|
|
1415
|
+
const workingDirectory = input.workingDirectory ?? resolveSafeRepoRoot();
|
|
1416
|
+
const receiptScope = {
|
|
1417
|
+
invocationRoot: resolveSafeRepoRoot(),
|
|
1418
|
+
workingDirectory,
|
|
1419
|
+
repoRoot: workingDirectory,
|
|
1420
|
+
runsRoot
|
|
1421
|
+
};
|
|
1422
|
+
const gate = await evaluateMcpRunGate({
|
|
1423
|
+
runsRoot,
|
|
1424
|
+
workingDirectory,
|
|
1425
|
+
objective: input.objective,
|
|
1426
|
+
engine: input.engine,
|
|
1427
|
+
verificationPlan: input.verificationPlan,
|
|
1428
|
+
receiptScope,
|
|
1429
|
+
allowedPaths: input.allowedPaths,
|
|
1430
|
+
deniedPaths: input.deniedPaths,
|
|
1431
|
+
budget: normalizeRunBudget(input)
|
|
1432
|
+
});
|
|
1433
|
+
if (!gate.allowed) {
|
|
1434
|
+
throw new MartinToolError("policy_blocked", gate.summary, {
|
|
1435
|
+
category: "policy_blocked",
|
|
1436
|
+
suggestion: gate.nextAction,
|
|
1437
|
+
retryable: false,
|
|
1438
|
+
details: {
|
|
1439
|
+
missingSteps: gate.missingSteps,
|
|
1440
|
+
receiptScope
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1384
1444
|
const output = await runLoopTool(input);
|
|
1385
1445
|
return createToolSuccessResult(output, `Run ${output.loopId} is ${output.status}/${output.lifecycleState} after ${output.attempts} attempt(s); spend ${output.costUsd.toFixed(2)} USD.`);
|
|
1386
1446
|
}
|
|
@@ -1401,7 +1461,8 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1401
1461
|
runsRoot: output.environment.runsRoot,
|
|
1402
1462
|
step: "doctor",
|
|
1403
1463
|
workingDirectory: output.environment.workingDirectory,
|
|
1404
|
-
engine: input.engine
|
|
1464
|
+
engine: input.engine,
|
|
1465
|
+
receiptScope: output.receiptScope
|
|
1405
1466
|
}).catch(() => { });
|
|
1406
1467
|
return createToolSuccessResult(output, output.summary);
|
|
1407
1468
|
}
|
|
@@ -1412,7 +1473,13 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1412
1473
|
runsRoot: resolveRunsRoot(process.env),
|
|
1413
1474
|
step: "plan",
|
|
1414
1475
|
workingDirectory: output.workingDirectory,
|
|
1415
|
-
objective: output.objective
|
|
1476
|
+
objective: output.objective,
|
|
1477
|
+
receiptScope: {
|
|
1478
|
+
invocationRoot: resolveSafeRepoRoot(),
|
|
1479
|
+
workingDirectory: output.workingDirectory,
|
|
1480
|
+
repoRoot: output.workingDirectory,
|
|
1481
|
+
runsRoot: resolveRunsRoot(process.env)
|
|
1482
|
+
}
|
|
1416
1483
|
}).catch(() => { });
|
|
1417
1484
|
return createToolSuccessResult(output, `Plan ready for ${output.objective} with ${output.risk.level} risk and ${output.approvalRecommendation.replace(/_/gu, " ")} approval.`);
|
|
1418
1485
|
}
|
|
@@ -1426,7 +1493,11 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1426
1493
|
workingDirectory: output.normalized.workingDirectory,
|
|
1427
1494
|
objective: output.normalized.objective,
|
|
1428
1495
|
engine: output.normalized.engine,
|
|
1429
|
-
verificationPlan: output.normalized.verificationPlan
|
|
1496
|
+
verificationPlan: output.normalized.verificationPlan,
|
|
1497
|
+
receiptScope: output.receiptScope,
|
|
1498
|
+
allowedPaths: output.normalized.allowedPaths,
|
|
1499
|
+
deniedPaths: output.normalized.deniedPaths,
|
|
1500
|
+
budget: output.normalized.budget
|
|
1430
1501
|
}).catch(() => { });
|
|
1431
1502
|
}
|
|
1432
1503
|
return createToolSuccessResult(output, output.summary);
|
|
@@ -1516,6 +1587,13 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1516
1587
|
});
|
|
1517
1588
|
return server;
|
|
1518
1589
|
}
|
|
1590
|
+
function normalizeRunBudget(input) {
|
|
1591
|
+
return normalizeLoopBudget({
|
|
1592
|
+
maxUsd: input.maxUsd,
|
|
1593
|
+
maxIterations: input.maxIterations,
|
|
1594
|
+
maxTokens: input.maxTokens
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1519
1597
|
export async function connectMartinMcpStdioServer() {
|
|
1520
1598
|
const server = createMartinMcpServer();
|
|
1521
1599
|
const transport = new StdioServerTransport();
|
package/dist/tools/doctor.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CodexHostPlatform } from "../vendor/adapters/index.js";
|
|
1
2
|
import { type LoopPreview, type MartinEngine } from "./tool-support.js";
|
|
2
3
|
import { type MartinReadinessReport } from "./workflow-governance.js";
|
|
3
4
|
export interface MartinDoctorInput {
|
|
@@ -17,13 +18,50 @@ export interface MartinDoctorOutput {
|
|
|
17
18
|
workspaceRoot: string;
|
|
18
19
|
workingDirectory: string;
|
|
19
20
|
runsRoot: string;
|
|
20
|
-
mode: "live" | "
|
|
21
|
+
mode: "live" | "proof";
|
|
21
22
|
liveMode: boolean;
|
|
22
23
|
};
|
|
24
|
+
scope: {
|
|
25
|
+
invocationRoot: string;
|
|
26
|
+
workingDirectory: string;
|
|
27
|
+
repoRoot: string;
|
|
28
|
+
runsRoot: string;
|
|
29
|
+
};
|
|
30
|
+
receiptScope: {
|
|
31
|
+
invocationRoot: string;
|
|
32
|
+
workingDirectory: string;
|
|
33
|
+
repoRoot: string;
|
|
34
|
+
runsRoot: string;
|
|
35
|
+
};
|
|
23
36
|
engines: Record<MartinEngine, {
|
|
24
37
|
available: boolean;
|
|
25
38
|
detail: string;
|
|
26
39
|
resolvedPath?: string;
|
|
40
|
+
candidatePaths?: string[];
|
|
41
|
+
selectedPath?: string;
|
|
42
|
+
hostPlatform?: CodexHostPlatform;
|
|
43
|
+
installKind?: string;
|
|
44
|
+
nativeInstallValid?: boolean;
|
|
45
|
+
invocationMode?: string;
|
|
46
|
+
sandboxMode?: string;
|
|
47
|
+
sandboxCompatible?: boolean;
|
|
48
|
+
nativeDependencyStatus?: string;
|
|
49
|
+
nativeDependencyPackage?: string;
|
|
50
|
+
launchReady?: boolean;
|
|
51
|
+
probeSummary?: string;
|
|
52
|
+
remediation?: string;
|
|
53
|
+
candidateProbeResults?: Array<{
|
|
54
|
+
path: string;
|
|
55
|
+
installKind: string;
|
|
56
|
+
invocationMode: string;
|
|
57
|
+
nativeInstallValid: boolean;
|
|
58
|
+
sandboxCompatible: boolean;
|
|
59
|
+
launchReady: boolean;
|
|
60
|
+
summary: string;
|
|
61
|
+
remediation?: string;
|
|
62
|
+
nativeDependencyStatus?: string;
|
|
63
|
+
nativeDependencyPackage?: string;
|
|
64
|
+
}>;
|
|
27
65
|
}>;
|
|
28
66
|
requestedEngine?: MartinEngine;
|
|
29
67
|
runStore: {
|
package/dist/tools/doctor.js
CHANGED
|
@@ -1,28 +1,54 @@
|
|
|
1
|
+
import { probeCodexLaunch, resolveCliCommandAvailability } from "../vendor/adapters/index.js";
|
|
1
2
|
import { resolveRunsRoot } from "../vendor/core/index.js";
|
|
2
3
|
import { resolveSafeRepoRoot, resolveSafeRunsRootPath } from "../server-validation.js";
|
|
3
|
-
import { getEngineAvailability, inspectRunsRoot, resolveExecutionMode } from "./tool-support.js";
|
|
4
|
+
import { createSkippedCliAvailability, getEngineAvailability, inspectRunsRoot, resolveExecutionMode } from "./tool-support.js";
|
|
4
5
|
import { buildReadinessReport, inspectRepoSignals } from "./workflow-governance.js";
|
|
5
6
|
export async function martinDoctorTool(input) {
|
|
6
7
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
7
8
|
const runsRoot = resolveSafeRunsRootPath(input.runsDir, resolveRunsRoot(process.env));
|
|
9
|
+
const workspaceRoot = resolveSafeRepoRoot();
|
|
8
10
|
const executionMode = resolveExecutionMode();
|
|
9
|
-
const claude =
|
|
10
|
-
|
|
11
|
+
const claude = executionMode.liveMode
|
|
12
|
+
? getEngineAvailability("claude")
|
|
13
|
+
: createSkippedCliAvailability("claude");
|
|
14
|
+
const codex = executionMode.liveMode
|
|
15
|
+
? resolveCliCommandAvailability("codex")
|
|
16
|
+
: createSkippedCliAvailability("codex");
|
|
17
|
+
const gemini = executionMode.liveMode
|
|
18
|
+
? getEngineAvailability("gemini")
|
|
19
|
+
: createSkippedCliAvailability("gemini");
|
|
20
|
+
const codexProbe = executionMode.liveMode && input.engine === "codex" && codex.available
|
|
21
|
+
? probeCodexLaunch({
|
|
22
|
+
workingDirectory,
|
|
23
|
+
availability: codex
|
|
24
|
+
})
|
|
25
|
+
: undefined;
|
|
11
26
|
const runStore = await inspectRunsRoot(runsRoot);
|
|
12
|
-
const signals = inspectRepoSignals(workingDirectory
|
|
27
|
+
const signals = inspectRepoSignals(workingDirectory, {
|
|
28
|
+
includeHostAvailability: executionMode.liveMode
|
|
29
|
+
});
|
|
13
30
|
const readiness = buildReadinessReport(signals, runStore);
|
|
14
31
|
const warnings = [];
|
|
32
|
+
const receiptScope = {
|
|
33
|
+
invocationRoot: workspaceRoot,
|
|
34
|
+
workingDirectory,
|
|
35
|
+
repoRoot: workingDirectory,
|
|
36
|
+
runsRoot
|
|
37
|
+
};
|
|
15
38
|
if (!runStore.exists) {
|
|
16
39
|
warnings.push("Configured Martin runs root does not exist yet.");
|
|
17
40
|
}
|
|
18
|
-
if (executionMode.liveMode && !claude.available && !codex.available) {
|
|
19
|
-
warnings.push("
|
|
41
|
+
if (executionMode.liveMode && !claude.available && !codex.available && !gemini.available) {
|
|
42
|
+
warnings.push("None of claude, codex, or gemini is currently available on PATH for live runs.");
|
|
20
43
|
}
|
|
21
44
|
if (input.engine && executionMode.liveMode) {
|
|
22
|
-
const selected = input.engine === "claude" ? claude : codex;
|
|
45
|
+
const selected = input.engine === "claude" ? claude : input.engine === "gemini" ? gemini : codex;
|
|
23
46
|
if (!selected.available) {
|
|
24
47
|
warnings.push(`Requested engine '${input.engine}' is not available on PATH.`);
|
|
25
48
|
}
|
|
49
|
+
if (input.engine === "codex" && codexProbe && !codexProbe.ok) {
|
|
50
|
+
warnings.push(codexProbe.summary);
|
|
51
|
+
}
|
|
26
52
|
}
|
|
27
53
|
warnings.push(...runStore.warnings);
|
|
28
54
|
const status = warnings.length === 0 ? "ok" : "degraded";
|
|
@@ -38,12 +64,16 @@ export async function martinDoctorTool(input) {
|
|
|
38
64
|
platform: process.platform
|
|
39
65
|
},
|
|
40
66
|
environment: {
|
|
41
|
-
workspaceRoot
|
|
67
|
+
workspaceRoot,
|
|
42
68
|
workingDirectory,
|
|
43
69
|
runsRoot,
|
|
44
70
|
mode: executionMode.mode,
|
|
45
71
|
liveMode: executionMode.liveMode
|
|
46
72
|
},
|
|
73
|
+
scope: {
|
|
74
|
+
...receiptScope
|
|
75
|
+
},
|
|
76
|
+
receiptScope,
|
|
47
77
|
engines: {
|
|
48
78
|
claude: {
|
|
49
79
|
available: claude.available,
|
|
@@ -53,7 +83,36 @@ export async function martinDoctorTool(input) {
|
|
|
53
83
|
codex: {
|
|
54
84
|
available: codex.available,
|
|
55
85
|
detail: codex.detail,
|
|
56
|
-
...(codex.resolvedPath ? { resolvedPath: codex.resolvedPath } : {})
|
|
86
|
+
...(codex.resolvedPath ? { resolvedPath: codex.resolvedPath } : {}),
|
|
87
|
+
...(codex.candidatePaths?.length ? { candidatePaths: codex.candidatePaths } : {}),
|
|
88
|
+
...(codexProbe
|
|
89
|
+
? {
|
|
90
|
+
selectedPath: codexProbe.command,
|
|
91
|
+
hostPlatform: codexProbe.diagnosis.hostPlatform,
|
|
92
|
+
installKind: codexProbe.diagnosis.installKind,
|
|
93
|
+
nativeInstallValid: codexProbe.diagnosis.nativeInstallValid,
|
|
94
|
+
invocationMode: codexProbe.diagnosis.invocationMode,
|
|
95
|
+
sandboxMode: codexProbe.diagnosis.sandboxMode,
|
|
96
|
+
sandboxCompatible: codexProbe.diagnosis.sandboxCompatible,
|
|
97
|
+
...(codexProbe.diagnosis.nativeDependencyStatus
|
|
98
|
+
? { nativeDependencyStatus: codexProbe.diagnosis.nativeDependencyStatus }
|
|
99
|
+
: {}),
|
|
100
|
+
...(codexProbe.diagnosis.nativeDependencyPackage
|
|
101
|
+
? { nativeDependencyPackage: codexProbe.diagnosis.nativeDependencyPackage }
|
|
102
|
+
: {}),
|
|
103
|
+
launchReady: codexProbe.ok,
|
|
104
|
+
probeSummary: codexProbe.summary,
|
|
105
|
+
...(codexProbe.diagnosis.remediation ? { remediation: codexProbe.diagnosis.remediation } : {}),
|
|
106
|
+
...(codexProbe.candidateProbeResults?.length
|
|
107
|
+
? { candidateProbeResults: codexProbe.candidateProbeResults }
|
|
108
|
+
: {})
|
|
109
|
+
}
|
|
110
|
+
: {})
|
|
111
|
+
},
|
|
112
|
+
gemini: {
|
|
113
|
+
available: gemini.available,
|
|
114
|
+
detail: gemini.detail,
|
|
115
|
+
...(gemini.resolvedPath ? { resolvedPath: gemini.resolvedPath } : {})
|
|
57
116
|
}
|
|
58
117
|
},
|
|
59
118
|
...(input.engine ? { requestedEngine: input.engine } : {}),
|
package/dist/tools/eval.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { loadDetailedLoopRecord, readLedgerEvents } from "./run-store.js";
|
|
2
|
+
import { resolveTrustedLoopRepoRoot } from "../server-validation.js";
|
|
2
3
|
import { buildVerificationSummary } from "./tool-support.js";
|
|
3
4
|
import { assessRunRisk, inspectRepoSignals } from "./workflow-governance.js";
|
|
4
5
|
export async function martinEvalTool(input) {
|
|
5
6
|
const detail = await loadDetailedLoopRecord(input);
|
|
6
7
|
const ledgerEvents = await readLedgerEvents(detail);
|
|
7
8
|
const verification = buildVerificationSummary(detail.loop, ledgerEvents);
|
|
8
|
-
const repoRoot = detail.loop.task?.repoRoot;
|
|
9
|
-
const signals = inspectRepoSignals(repoRoot
|
|
9
|
+
const repoRoot = resolveTrustedLoopRepoRoot(detail.loop.task?.repoRoot);
|
|
10
|
+
const signals = inspectRepoSignals(repoRoot);
|
|
10
11
|
const risk = assessRunRisk({
|
|
11
12
|
objective: detail.loop.task?.objective ?? detail.loop.loopId,
|
|
12
13
|
allowedPaths: detail.loop.task?.allowedPaths ?? [],
|