@martinloop/mcp 0.5.3 → 0.5.5

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 (45) hide show
  1. package/README.md +6 -6
  2. package/dist/server-validation.js +2 -0
  3. package/dist/server.js +30 -3
  4. package/dist/tools/run-loop.d.ts +1 -0
  5. package/dist/tools/run-loop.js +25 -4
  6. package/dist/tools/tool-response.d.ts +9 -1
  7. package/dist/tools/tool-response.js +7 -5
  8. package/dist/tools/tool-support.js +62 -271
  9. package/dist/vendor/adapters/claude-cli.d.ts +9 -0
  10. package/dist/vendor/adapters/claude-cli.js +19 -2
  11. package/dist/vendor/adapters/codex-capabilities.d.ts +12 -3
  12. package/dist/vendor/adapters/codex-capabilities.js +46 -18
  13. package/dist/vendor/adapters/codex-cli.d.ts +2 -1
  14. package/dist/vendor/adapters/codex-cli.js +12 -2
  15. package/dist/vendor/adapters/codex-host.d.ts +4 -1
  16. package/dist/vendor/adapters/codex-host.js +34 -16
  17. package/dist/vendor/adapters/codex-launcher.d.ts +1 -1
  18. package/dist/vendor/adapters/codex-launcher.js +1 -1
  19. package/dist/vendor/adapters/index.d.ts +2 -1
  20. package/dist/vendor/adapters/index.js +1 -0
  21. package/dist/vendor/contracts/agent-execution.d.ts +9 -0
  22. package/dist/vendor/contracts/agent-execution.js +14 -0
  23. package/dist/vendor/contracts/index.d.ts +9 -0
  24. package/dist/vendor/contracts/index.js +1 -0
  25. package/dist/vendor/core/grounding.js +42 -7
  26. package/dist/vendor/core/index.d.ts +5 -2
  27. package/dist/vendor/core/index.js +12 -3
  28. package/dist/vendor/presentation/governed-plan.d.ts +33 -0
  29. package/dist/vendor/presentation/governed-plan.js +146 -0
  30. package/dist/vendor/presentation/index.d.ts +7 -0
  31. package/dist/vendor/presentation/index.js +7 -0
  32. package/dist/vendor/presentation/motion.d.ts +12 -0
  33. package/dist/vendor/presentation/motion.js +123 -0
  34. package/dist/vendor/presentation/status.d.ts +8 -0
  35. package/dist/vendor/presentation/status.js +11 -0
  36. package/dist/vendor/presentation/table.d.ts +11 -0
  37. package/dist/vendor/presentation/table.js +47 -0
  38. package/dist/vendor/presentation/text.d.ts +6 -0
  39. package/dist/vendor/presentation/text.js +30 -0
  40. package/dist/vendor/presentation/theme.d.ts +31 -0
  41. package/dist/vendor/presentation/theme.js +82 -0
  42. package/dist/vendor/presentation/verified-handoff.d.ts +15 -0
  43. package/dist/vendor/presentation/verified-handoff.js +151 -0
  44. package/package.json +3 -2
  45. package/server.json +2 -2
package/README.md CHANGED
@@ -57,27 +57,27 @@ These aren't hypotheticals. The $28 overshoot happened in production testing. We
57
57
 
58
58
  ### Claude Code
59
59
  ```sh
60
- claude mcp add martin-loop -- npx -y @martinloop/mcp@0.5.3
60
+ claude mcp add martin-loop -- npx -y @martinloop/mcp@0.5.5
61
61
  ```
62
62
 
63
63
  Windows:
64
64
  ```sh
65
- claude mcp add --transport stdio --scope user martin-loop -- cmd /c npx -y @martinloop/mcp@0.5.3
65
+ claude mcp add --transport stdio --scope user martin-loop -- cmd /c npx -y @martinloop/mcp@0.5.5
66
66
  ```
67
67
 
68
68
  ### Codex
69
69
  ```sh
70
- codex mcp add martin-loop -- npx -y @martinloop/mcp@0.5.3
70
+ codex mcp add martin-loop -- npx -y @martinloop/mcp@0.5.5
71
71
  ```
72
72
 
73
73
  ### Gemini CLI
74
74
  ```sh
75
- gemini mcp add martin-loop -- npx -y @martinloop/mcp@0.5.3
75
+ gemini mcp add martin-loop -- npx -y @martinloop/mcp@0.5.5
76
76
  ```
77
77
 
78
78
  ### Any MCP Host
79
79
  ```sh
80
- npx -y @martinloop/mcp@0.5.3
80
+ npx -y @martinloop/mcp@0.5.5
81
81
  ```
82
82
 
83
83
  ## How a Governed Run Works
@@ -139,7 +139,7 @@ MartinLoop does not round failure into success. It preserves the evidence and fo
139
139
  | `git restore` fails mid-rollback | Throws, leaves dirty state | Retries once, falls back to `git checkout`, cleans up |
140
140
  | Invalid `--profile` flag | Crashes | Warns, falls back to `minimal`, keeps running |
141
141
 
142
- ## Codex compatibility in 0.5.3
142
+ ## Codex compatibility in 0.5.5
143
143
 
144
144
  MartinLoop no longer assumes one Codex host or one fixed Codex flag set. The CLI resolves the exact Codex executable, reads its advertised capabilities, negotiates a supported write strategy, proves that strategy before reporting launch readiness, and reuses the same contract for the governed run.
145
145
 
@@ -151,6 +151,7 @@ function validateRunInput(args) {
151
151
  "maxIterations",
152
152
  "maxTokens",
153
153
  "verifyTimeoutMs",
154
+ "providerExecutionTimeoutMs",
154
155
  "maxMinutes",
155
156
  "maxFilesChanged",
156
157
  "maxCommands",
@@ -180,6 +181,7 @@ function validateRunInput(args) {
180
181
  ...optionalPositiveInteger(record.maxIterations, "maxIterations"),
181
182
  ...optionalPositiveInteger(record.maxTokens, "maxTokens"),
182
183
  ...optionalPositiveInteger(record.verifyTimeoutMs, "verifyTimeoutMs"),
184
+ ...optionalPositiveInteger(record.providerExecutionTimeoutMs, "providerExecutionTimeoutMs"),
183
185
  ...optionalPositiveInteger(record.maxMinutes, "maxMinutes"),
184
186
  ...optionalPositiveInteger(record.maxFilesChanged, "maxFilesChanged"),
185
187
  ...optionalPositiveInteger(record.maxCommands, "maxCommands"),
package/dist/server.js CHANGED
@@ -47,6 +47,7 @@ import { runLoopTool } from "./tools/run-loop.js";
47
47
  import { MARTIN_ENGINE_VALUES } from "./tools/tool-support.js";
48
48
  import { classifyRoute } from "./vendor/core/index.js";
49
49
  import { createToolErrorResult, createToolSuccessResult } from "./tools/tool-response.js";
50
+ import { buildGovernedPlanStages, renderGovernedRunPlanMarkdown, renderVerifiedHandoffMarkdown } from "./vendor/presentation/index.js";
50
51
  import { MartinToolError, toToolFailure } from "./tools/tool-errors.js";
51
52
  import { normalizeLoopBudget } from "./tools/workflow-governance.js";
52
53
  import { resolveSafeRepoRoot, sanitizeToolErrorMessage, validateToolInput } from "./server-validation.js";
@@ -793,6 +794,16 @@ export function createMartinMcpServer(serverInfo) {
793
794
  exclusiveMinimum: 0,
794
795
  description: "Maximum total tokens across all attempts."
795
796
  },
797
+ verifyTimeoutMs: {
798
+ type: "integer",
799
+ exclusiveMinimum: 0,
800
+ description: "Hard timeout for each verifier command in milliseconds."
801
+ },
802
+ providerExecutionTimeoutMs: {
803
+ type: "integer",
804
+ exclusiveMinimum: 0,
805
+ description: "Hard timeout for each provider coding process in milliseconds."
806
+ },
796
807
  verificationPlan: {
797
808
  type: "array",
798
809
  items: { type: "string" },
@@ -1594,7 +1605,23 @@ export function createMartinMcpServer(serverInfo) {
1594
1605
  budget: output.normalized.budget
1595
1606
  }).catch(() => { });
1596
1607
  }
1597
- return createToolSuccessResult(output, output.summary);
1608
+ const prefPlanView = {
1609
+ ready: output.ok,
1610
+ task: output.normalized.objective,
1611
+ engine: output.normalized.engine,
1612
+ mode: output.readiness.liveMode ? "live" : "proof",
1613
+ budget: output.normalized.budget,
1614
+ verifier: output.normalized.verificationPlan,
1615
+ receiptScope: output.receiptScope,
1616
+ policyProfile: output.policy.name,
1617
+ blockingIssues: output.ok ? [] : [output.summary],
1618
+ warnings: output.warnings,
1619
+ stages: []
1620
+ };
1621
+ prefPlanView.stages = buildGovernedPlanStages(prefPlanView);
1622
+ return createToolSuccessResult(output, output.summary, {
1623
+ human: renderGovernedRunPlanMarkdown(prefPlanView)
1624
+ });
1598
1625
  }
1599
1626
  if (name === "martin_logs") {
1600
1627
  const input = validateToolInput("martin_logs", args);
@@ -1644,12 +1671,12 @@ export function createMartinMcpServer(serverInfo) {
1644
1671
  if (name === "martin_run_dossier") {
1645
1672
  const input = validateToolInput("martin_run_dossier", args);
1646
1673
  const output = await martinRunDossierTool(input);
1647
- return createToolSuccessResult(output, `Dossier ready for Martin run ${output.loop.loopId} with ${output.attempts.length} attempt(s).`);
1674
+ return createToolSuccessResult(output, `Dossier ready for Martin run ${output.loop.loopId} with ${output.attempts.length} attempt(s).`, { human: renderVerifiedHandoffMarkdown(output.verifiedHandoff) });
1648
1675
  }
1649
1676
  if (name === "martin_dossier") {
1650
1677
  const input = validateToolInput("martin_dossier", args);
1651
1678
  const output = await martinRunDossierTool(input);
1652
- return createToolSuccessResult(output, `Dossier ready for Martin run ${output.loop.loopId} in ${output.format} format.`);
1679
+ return createToolSuccessResult(output, `Dossier ready for Martin run ${output.loop.loopId} in ${output.format} format.`, { human: renderVerifiedHandoffMarkdown(output.verifiedHandoff) });
1653
1680
  }
1654
1681
  if (name === "martin_eval") {
1655
1682
  const input = validateToolInput("martin_eval", args);
@@ -11,6 +11,7 @@ export interface RunLoopInput {
11
11
  maxIterations?: number;
12
12
  maxTokens?: number;
13
13
  verifyTimeoutMs?: number;
14
+ providerExecutionTimeoutMs?: number;
14
15
  verificationPlan?: string[];
15
16
  allowedPaths?: string[];
16
17
  deniedPaths?: string[];
@@ -30,7 +30,7 @@ export async function runLoopTool(input) {
30
30
  repoRoot: workingDirectory,
31
31
  runsRoot
32
32
  };
33
- let codexCommandOverride;
33
+ let codexProbeOverride;
34
34
  if (executionMode.liveMode) {
35
35
  if (engine === "codex") {
36
36
  const engineAvailability = resolveCliCommandAvailability("codex");
@@ -43,7 +43,10 @@ export async function runLoopTool(input) {
43
43
  }
44
44
  const codexProbe = probeCodexLaunch({
45
45
  workingDirectory,
46
- availability: engineAvailability
46
+ availability: engineAvailability,
47
+ ...(input.providerExecutionTimeoutMs !== undefined
48
+ ? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
49
+ : {})
47
50
  });
48
51
  if (!codexProbe.ok) {
49
52
  throw new MartinToolError("engine_unavailable", codexProbe.summary, {
@@ -52,7 +55,7 @@ export async function runLoopTool(input) {
52
55
  retryable: false
53
56
  });
54
57
  }
55
- codexCommandOverride = codexProbe.command;
58
+ codexProbeOverride = codexProbe;
56
59
  }
57
60
  else {
58
61
  const engineAvailability = getEngineAvailability(engine);
@@ -76,18 +79,33 @@ export async function runLoopTool(input) {
76
79
  ? createCodexCliAdapter({
77
80
  workingDirectory,
78
81
  ...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
82
+ ...(input.providerExecutionTimeoutMs !== undefined
83
+ ? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
84
+ : {}),
79
85
  ...(model ? { model } : {}),
80
- ...(codexCommandOverride ? { command: codexCommandOverride } : {})
86
+ ...(codexProbeOverride
87
+ ? {
88
+ command: codexProbeOverride.command,
89
+ capabilityProfile: codexProbeOverride.capabilityProfile,
90
+ autonomyResolution: codexProbeOverride.autonomyResolution
91
+ }
92
+ : {})
81
93
  })
82
94
  : engine === "gemini"
83
95
  ? createGeminiCliAdapter({
84
96
  workingDirectory,
85
97
  ...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
98
+ ...(input.providerExecutionTimeoutMs !== undefined
99
+ ? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
100
+ : {}),
86
101
  ...(model ? { model } : {})
87
102
  })
88
103
  : createClaudeCliAdapter({
89
104
  workingDirectory,
90
105
  ...(input.verifyTimeoutMs !== undefined ? { verifyTimeoutMs: input.verifyTimeoutMs } : {}),
106
+ ...(input.providerExecutionTimeoutMs !== undefined
107
+ ? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
108
+ : {}),
91
109
  ...(model ? { model } : {})
92
110
  });
93
111
  const partialBudget = {};
@@ -113,6 +131,9 @@ export async function runLoopTool(input) {
113
131
  ...(input.verifyTimeoutMs !== undefined
114
132
  ? { verificationTimeoutMs: input.verifyTimeoutMs }
115
133
  : {}),
134
+ ...(input.providerExecutionTimeoutMs !== undefined
135
+ ? { providerExecutionTimeoutMs: input.providerExecutionTimeoutMs }
136
+ : {}),
116
137
  repoRoot: workingDirectory,
117
138
  ...(allowedPaths ? { allowedPaths } : {}),
118
139
  ...(deniedPaths ? { deniedPaths } : {})
@@ -3,7 +3,15 @@ interface TextContentBlock {
3
3
  type: "text";
4
4
  text: string;
5
5
  }
6
- export declare function createToolSuccessResult<T extends object>(output: T, summary: string): {
6
+ export interface ToolSuccessRenderOptions {
7
+ /** Explicit human-readable Markdown for content[0].
8
+ * Overrides resolveHumanArtifact fallback when provided. */
9
+ human?: string;
10
+ /** Include compatibility JSON block in content[]. Default: true.
11
+ * Set false only after real-host testing proves structuredContent alone suffices. */
12
+ includeCompatibilityJson?: boolean;
13
+ }
14
+ export declare function createToolSuccessResult<T extends object>(output: T, summary: string, options?: ToolSuccessRenderOptions): {
7
15
  content: TextContentBlock[];
8
16
  structuredContent: T;
9
17
  _meta: Record<string, unknown>;
@@ -8,12 +8,14 @@ function resolveHumanArtifact(output, summary) {
8
8
  const rendered = "rendered" in output ? output.rendered : undefined;
9
9
  return typeof rendered === "string" && rendered.trim().length > 0 ? rendered : summary;
10
10
  }
11
- export function createToolSuccessResult(output, summary) {
11
+ export function createToolSuccessResult(output, summary, options = {}) {
12
+ const humanText = options.human ?? resolveHumanArtifact(output, summary);
13
+ const content = [{ type: "text", text: humanText }];
14
+ if (options.includeCompatibilityJson !== false) {
15
+ content.push(createJsonBlock(output));
16
+ }
12
17
  return {
13
- content: [
14
- { type: "text", text: resolveHumanArtifact(output, summary) },
15
- createJsonBlock(output)
16
- ],
18
+ content,
17
19
  structuredContent: output,
18
20
  _meta: {
19
21
  "martinloop/summary": summary,