@martinloop/mcp 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -132
- package/dist/discovery-metadata.d.ts +10 -5
- package/dist/discovery-metadata.js +95 -5
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/prompts.d.ts +1 -1
- package/dist/prompts.js +93 -1
- package/dist/resources.d.ts +9 -1
- package/dist/resources.js +247 -16
- package/dist/server-validation.d.ts +2 -1
- package/dist/server-validation.js +124 -0
- package/dist/server.js +379 -5
- package/dist/tools/doctor.d.ts +14 -1
- package/dist/tools/doctor.js +43 -8
- package/dist/tools/eval.d.ts +24 -0
- package/dist/tools/eval.js +66 -0
- package/dist/tools/get-run.d.ts +2 -0
- package/dist/tools/get-run.js +2 -1
- package/dist/tools/get-status.d.ts +8 -0
- package/dist/tools/get-status.js +18 -0
- package/dist/tools/get-verification-results.d.ts +2 -0
- package/dist/tools/get-verification-results.js +2 -1
- package/dist/tools/logs.d.ts +25 -0
- package/dist/tools/logs.js +49 -0
- package/dist/tools/plan.d.ts +20 -0
- package/dist/tools/plan.js +10 -0
- package/dist/tools/pr-tools.d.ts +31 -0
- package/dist/tools/pr-tools.js +112 -0
- package/dist/tools/preflight.d.ts +24 -1
- package/dist/tools/preflight.js +47 -7
- package/dist/tools/run-controls.d.ts +36 -0
- package/dist/tools/run-controls.js +88 -0
- package/dist/tools/run-dossier.d.ts +16 -0
- package/dist/tools/run-dossier.js +64 -2
- package/dist/tools/run-loop.d.ts +3 -2
- package/dist/tools/run-loop.js +52 -13
- package/dist/tools/tool-errors.d.ts +1 -1
- package/dist/tools/tool-errors.js +1 -1
- package/dist/tools/tool-support.d.ts +6 -3
- package/dist/tools/tool-support.js +37 -3
- package/dist/tools/workflow-governance.d.ts +133 -0
- package/dist/tools/workflow-governance.js +581 -0
- package/dist/vendor/adapters/claude-cli.d.ts +25 -0
- package/dist/vendor/adapters/claude-cli.js +279 -19
- package/dist/vendor/adapters/cli-bridge.d.ts +6 -0
- package/dist/vendor/adapters/cli-bridge.js +58 -9
- package/dist/vendor/adapters/codex-launcher.d.ts +44 -0
- package/dist/vendor/adapters/codex-launcher.js +247 -0
- package/dist/vendor/adapters/index.d.ts +4 -2
- package/dist/vendor/adapters/index.js +4 -1
- package/dist/vendor/adapters/openai-compatible.d.ts +62 -0
- package/dist/vendor/adapters/openai-compatible.js +267 -0
- package/dist/vendor/adapters/runtime-support.d.ts +3 -0
- package/dist/vendor/adapters/runtime-support.js +8 -1
- package/dist/vendor/adapters/verifier-only.js +4 -3
- package/dist/vendor/contracts/index.d.ts +39 -0
- package/dist/vendor/contracts/index.js +2 -0
- package/dist/vendor/core/index.d.ts +23 -3
- package/dist/vendor/core/index.js +88 -15
- 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 +239 -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 +25 -0
- package/dist/workflow-state.js +102 -0
- package/package.json +3 -3
- package/server.json +2 -2
package/dist/server.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
import { realpathSync } from "node:fs";
|
|
21
21
|
import path from "node:path";
|
|
22
|
+
import { resolveRunsRoot } from "./vendor/core/index.js";
|
|
22
23
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
23
24
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
24
25
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -26,19 +27,25 @@ import { MARTIN_MCP_PACKAGE_VERSION } from "./package-version.js";
|
|
|
26
27
|
import { getMartinPrompt, listMartinPrompts } from "./prompts.js";
|
|
27
28
|
import { listMartinResources, listMartinResourceTemplates, readMartinResource } from "./resources.js";
|
|
28
29
|
import { martinDoctorTool } from "./tools/doctor.js";
|
|
30
|
+
import { martinEvalTool } from "./tools/eval.js";
|
|
29
31
|
import { martinGetAttemptTool } from "./tools/get-attempt.js";
|
|
30
32
|
import { martinGetRunTool } from "./tools/get-run.js";
|
|
31
33
|
import { martinGetVerificationResultsTool } from "./tools/get-verification-results.js";
|
|
32
34
|
import { getStatusTool } from "./tools/get-status.js";
|
|
33
35
|
import { inspectLoopTool } from "./tools/inspect-loop.js";
|
|
34
36
|
import { martinListRunsTool } from "./tools/list-runs.js";
|
|
37
|
+
import { martinLogsTool } from "./tools/logs.js";
|
|
38
|
+
import { martinPlanTool } from "./tools/plan.js";
|
|
35
39
|
import { martinPreflightTool } from "./tools/preflight.js";
|
|
40
|
+
import { martinCreatePrTool, martinPrSummaryTool, martinReviewPrTool } from "./tools/pr-tools.js";
|
|
36
41
|
import { martinRunDossierTool } from "./tools/run-dossier.js";
|
|
42
|
+
import { createRunControlReceipt } from "./tools/run-controls.js";
|
|
37
43
|
import { martinTriageRunsTool } from "./tools/triage-runs.js";
|
|
38
44
|
import { runLoopTool } from "./tools/run-loop.js";
|
|
39
45
|
import { createToolErrorResult, createToolSuccessResult } from "./tools/tool-response.js";
|
|
40
46
|
import { MartinToolError, toToolFailure } from "./tools/tool-errors.js";
|
|
41
47
|
import { sanitizeToolErrorMessage, validateToolInput } from "./server-validation.js";
|
|
48
|
+
import { recordMcpWorkflowStep } from "./workflow-state.js";
|
|
42
49
|
const stringArraySchema = {
|
|
43
50
|
type: "array",
|
|
44
51
|
items: { type: "string" }
|
|
@@ -115,6 +122,22 @@ const verificationSchema = {
|
|
|
115
122
|
latestAttemptIndex: { type: "integer" },
|
|
116
123
|
completedAt: { type: "string" },
|
|
117
124
|
summary: { type: "string" },
|
|
125
|
+
steps: {
|
|
126
|
+
type: "array",
|
|
127
|
+
items: {
|
|
128
|
+
type: "object",
|
|
129
|
+
additionalProperties: true,
|
|
130
|
+
properties: {
|
|
131
|
+
command: { type: "string" },
|
|
132
|
+
launched: { type: "boolean" },
|
|
133
|
+
exitCode: { type: "integer" },
|
|
134
|
+
timedOut: { type: "boolean" },
|
|
135
|
+
fastFail: { type: "boolean" },
|
|
136
|
+
detail: { type: "string" }
|
|
137
|
+
},
|
|
138
|
+
required: ["command", "launched"]
|
|
139
|
+
}
|
|
140
|
+
},
|
|
118
141
|
warnings: stringArraySchema
|
|
119
142
|
},
|
|
120
143
|
required: ["status", "eventCount", "ledgerEventCount", "warnings"]
|
|
@@ -363,7 +386,7 @@ const doctorOutputSchema = {
|
|
|
363
386
|
workspaceRoot: { type: "string" },
|
|
364
387
|
workingDirectory: { type: "string" },
|
|
365
388
|
runsRoot: { type: "string" },
|
|
366
|
-
mode: { type: "string", enum: ["live", "
|
|
389
|
+
mode: { type: "string", enum: ["live", "proof"] },
|
|
367
390
|
liveMode: { type: "boolean" }
|
|
368
391
|
},
|
|
369
392
|
required: ["workspaceRoot", "workingDirectory", "runsRoot", "mode", "liveMode"]
|
|
@@ -398,7 +421,7 @@ const preflightOutputSchema = {
|
|
|
398
421
|
type: "object",
|
|
399
422
|
additionalProperties: false,
|
|
400
423
|
properties: {
|
|
401
|
-
mode: { type: "string", enum: ["live", "
|
|
424
|
+
mode: { type: "string", enum: ["live", "proof"] },
|
|
402
425
|
liveMode: { type: "boolean" },
|
|
403
426
|
engineReady: { type: "boolean" }
|
|
404
427
|
},
|
|
@@ -649,6 +672,30 @@ const dossierOutputSchema = {
|
|
|
649
672
|
"warnings"
|
|
650
673
|
]
|
|
651
674
|
};
|
|
675
|
+
const planOutputSchema = {
|
|
676
|
+
type: "object",
|
|
677
|
+
additionalProperties: true
|
|
678
|
+
};
|
|
679
|
+
const logsOutputSchema = {
|
|
680
|
+
type: "object",
|
|
681
|
+
additionalProperties: true
|
|
682
|
+
};
|
|
683
|
+
const controlOutputSchema = {
|
|
684
|
+
type: "object",
|
|
685
|
+
additionalProperties: true
|
|
686
|
+
};
|
|
687
|
+
const evalOutputSchema = {
|
|
688
|
+
type: "object",
|
|
689
|
+
additionalProperties: true
|
|
690
|
+
};
|
|
691
|
+
const prSummaryOutputSchema = {
|
|
692
|
+
type: "object",
|
|
693
|
+
additionalProperties: true
|
|
694
|
+
};
|
|
695
|
+
const prReviewOutputSchema = {
|
|
696
|
+
type: "object",
|
|
697
|
+
additionalProperties: true
|
|
698
|
+
};
|
|
652
699
|
export function createMartinMcpServer(serverInfo) {
|
|
653
700
|
const server = new Server({
|
|
654
701
|
name: serverInfo?.name ?? "martin-loop",
|
|
@@ -658,7 +705,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
658
705
|
tools: [
|
|
659
706
|
{
|
|
660
707
|
name: "martin_run",
|
|
661
|
-
description: "Execute a governed Martin Loop run on a coding task and return the run summary, spend, artifact rollup, and verification state.",
|
|
708
|
+
description: "Execute a governed Martin Loop run on a coding task and return the run summary, spend, artifact rollup, and verification state. This hard-blocks until martin_doctor, martin_plan, and martin_preflight receipts exist for the same task.",
|
|
662
709
|
annotations: {
|
|
663
710
|
destructiveHint: true,
|
|
664
711
|
idempotentHint: false,
|
|
@@ -791,7 +838,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
791
838
|
},
|
|
792
839
|
{
|
|
793
840
|
name: "martin_doctor",
|
|
794
|
-
description: "Read-only environment and run-store diagnostics for the Martin MCP server.",
|
|
841
|
+
description: "Read-only environment and run-store diagnostics for the Martin MCP server. This is the expected first call before governed work begins.",
|
|
795
842
|
annotations: {
|
|
796
843
|
readOnlyHint: true,
|
|
797
844
|
idempotentHint: true
|
|
@@ -817,9 +864,44 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
817
864
|
},
|
|
818
865
|
outputSchema: doctorOutputSchema
|
|
819
866
|
},
|
|
867
|
+
{
|
|
868
|
+
name: "martin_plan",
|
|
869
|
+
description: "Read-only planning step that turns an objective into a scoped implementation plan, verifier proposal, policy pack, and risk recommendation. Use before preflight and before any real coding run.",
|
|
870
|
+
annotations: {
|
|
871
|
+
readOnlyHint: true,
|
|
872
|
+
idempotentHint: true
|
|
873
|
+
},
|
|
874
|
+
inputSchema: {
|
|
875
|
+
type: "object",
|
|
876
|
+
additionalProperties: false,
|
|
877
|
+
properties: {
|
|
878
|
+
objective: { type: "string", description: "The coding objective to plan." },
|
|
879
|
+
workingDirectory: {
|
|
880
|
+
type: "string",
|
|
881
|
+
description: "Optional repo-root override resolved under the MCP workspace root."
|
|
882
|
+
},
|
|
883
|
+
context: { type: "string", description: "Optional extra issue or bug context." },
|
|
884
|
+
policyPack: {
|
|
885
|
+
type: "string",
|
|
886
|
+
enum: ["solo-founder", "startup-team", "enterprise-strict", "oss-maintainer", "security-sensitive"]
|
|
887
|
+
},
|
|
888
|
+
verificationPlan: { type: "array", items: { type: "string" } },
|
|
889
|
+
allowedPaths: { type: "array", items: { type: "string" } },
|
|
890
|
+
deniedPaths: { type: "array", items: { type: "string" } },
|
|
891
|
+
maxUsd: { type: "number", exclusiveMinimum: 0 },
|
|
892
|
+
maxIterations: { type: "integer", exclusiveMinimum: 0 },
|
|
893
|
+
maxTokens: { type: "integer", exclusiveMinimum: 0 },
|
|
894
|
+
maxMinutes: { type: "integer", exclusiveMinimum: 0 },
|
|
895
|
+
maxFilesChanged: { type: "integer", exclusiveMinimum: 0 },
|
|
896
|
+
maxCommands: { type: "integer", exclusiveMinimum: 0 }
|
|
897
|
+
},
|
|
898
|
+
required: ["objective"]
|
|
899
|
+
},
|
|
900
|
+
outputSchema: planOutputSchema
|
|
901
|
+
},
|
|
820
902
|
{
|
|
821
903
|
name: "martin_preflight",
|
|
822
|
-
description: "Read-only validation of a planned Martin run before any execution or spend.",
|
|
904
|
+
description: "Read-only validation of a planned Martin run before any execution or spend. This is the last required step before martin_run.",
|
|
823
905
|
annotations: {
|
|
824
906
|
readOnlyHint: true,
|
|
825
907
|
idempotentHint: true
|
|
@@ -845,6 +927,14 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
845
927
|
type: "string",
|
|
846
928
|
description: "Model override passed to the CLI."
|
|
847
929
|
},
|
|
930
|
+
context: {
|
|
931
|
+
type: "string",
|
|
932
|
+
description: "Optional issue context carried into the run contract."
|
|
933
|
+
},
|
|
934
|
+
policyPack: {
|
|
935
|
+
type: "string",
|
|
936
|
+
enum: ["solo-founder", "startup-team", "enterprise-strict", "oss-maintainer", "security-sensitive"]
|
|
937
|
+
},
|
|
848
938
|
maxUsd: {
|
|
849
939
|
type: "number",
|
|
850
940
|
exclusiveMinimum: 0,
|
|
@@ -860,6 +950,21 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
860
950
|
exclusiveMinimum: 0,
|
|
861
951
|
description: "Maximum total tokens across all attempts."
|
|
862
952
|
},
|
|
953
|
+
maxMinutes: {
|
|
954
|
+
type: "integer",
|
|
955
|
+
exclusiveMinimum: 0,
|
|
956
|
+
description: "Estimated wall-clock minutes allowed for the run contract."
|
|
957
|
+
},
|
|
958
|
+
maxFilesChanged: {
|
|
959
|
+
type: "integer",
|
|
960
|
+
exclusiveMinimum: 0,
|
|
961
|
+
description: "Estimated maximum files changed for the run contract."
|
|
962
|
+
},
|
|
963
|
+
maxCommands: {
|
|
964
|
+
type: "integer",
|
|
965
|
+
exclusiveMinimum: 0,
|
|
966
|
+
description: "Estimated maximum commands allowed for the run contract."
|
|
967
|
+
},
|
|
863
968
|
verificationPlan: {
|
|
864
969
|
type: "array",
|
|
865
970
|
items: { type: "string" },
|
|
@@ -882,6 +987,93 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
882
987
|
},
|
|
883
988
|
outputSchema: preflightOutputSchema
|
|
884
989
|
},
|
|
990
|
+
{
|
|
991
|
+
name: "martin_logs",
|
|
992
|
+
description: "Read recent Martin loop events, ledger entries, and operator control receipts for live observability.",
|
|
993
|
+
annotations: {
|
|
994
|
+
readOnlyHint: true,
|
|
995
|
+
idempotentHint: true
|
|
996
|
+
},
|
|
997
|
+
inputSchema: {
|
|
998
|
+
type: "object",
|
|
999
|
+
additionalProperties: false,
|
|
1000
|
+
properties: {
|
|
1001
|
+
file: { type: "string" },
|
|
1002
|
+
loopId: { type: "string" },
|
|
1003
|
+
runsDir: { type: "string" },
|
|
1004
|
+
latest: { const: true },
|
|
1005
|
+
limit: { type: "integer", minimum: 1 }
|
|
1006
|
+
},
|
|
1007
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1008
|
+
},
|
|
1009
|
+
outputSchema: logsOutputSchema
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
name: "martin_pause",
|
|
1013
|
+
description: "Record a durable pause request for a Martin run so humans and runtimes can see that execution should pause before risky follow-up work.",
|
|
1014
|
+
annotations: {
|
|
1015
|
+
destructiveHint: true,
|
|
1016
|
+
idempotentHint: false
|
|
1017
|
+
},
|
|
1018
|
+
inputSchema: {
|
|
1019
|
+
type: "object",
|
|
1020
|
+
additionalProperties: false,
|
|
1021
|
+
properties: {
|
|
1022
|
+
file: { type: "string" },
|
|
1023
|
+
loopId: { type: "string" },
|
|
1024
|
+
runsDir: { type: "string" },
|
|
1025
|
+
latest: { const: true },
|
|
1026
|
+
reason: { type: "string" },
|
|
1027
|
+
requestedBy: { type: "string" }
|
|
1028
|
+
},
|
|
1029
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1030
|
+
},
|
|
1031
|
+
outputSchema: controlOutputSchema
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
name: "martin_cancel",
|
|
1035
|
+
description: "Record a durable cancellation request for a Martin run. This writes a control receipt; it does not silently kill a process without evidence.",
|
|
1036
|
+
annotations: {
|
|
1037
|
+
destructiveHint: true,
|
|
1038
|
+
idempotentHint: false
|
|
1039
|
+
},
|
|
1040
|
+
inputSchema: {
|
|
1041
|
+
type: "object",
|
|
1042
|
+
additionalProperties: false,
|
|
1043
|
+
properties: {
|
|
1044
|
+
file: { type: "string" },
|
|
1045
|
+
loopId: { type: "string" },
|
|
1046
|
+
runsDir: { type: "string" },
|
|
1047
|
+
latest: { const: true },
|
|
1048
|
+
reason: { type: "string" },
|
|
1049
|
+
requestedBy: { type: "string" }
|
|
1050
|
+
},
|
|
1051
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1052
|
+
},
|
|
1053
|
+
outputSchema: controlOutputSchema
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
name: "martin_continue",
|
|
1057
|
+
description: "Record a durable continue or resume request for a Martin run after a human pause or approval checkpoint.",
|
|
1058
|
+
annotations: {
|
|
1059
|
+
destructiveHint: true,
|
|
1060
|
+
idempotentHint: false
|
|
1061
|
+
},
|
|
1062
|
+
inputSchema: {
|
|
1063
|
+
type: "object",
|
|
1064
|
+
additionalProperties: false,
|
|
1065
|
+
properties: {
|
|
1066
|
+
file: { type: "string" },
|
|
1067
|
+
loopId: { type: "string" },
|
|
1068
|
+
runsDir: { type: "string" },
|
|
1069
|
+
latest: { const: true },
|
|
1070
|
+
reason: { type: "string" },
|
|
1071
|
+
requestedBy: { type: "string" }
|
|
1072
|
+
},
|
|
1073
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1074
|
+
},
|
|
1075
|
+
outputSchema: controlOutputSchema
|
|
1076
|
+
},
|
|
885
1077
|
{
|
|
886
1078
|
name: "martin_list_runs",
|
|
887
1079
|
description: "List recent Martin runs from the run store with lightweight filters for status, lifecycle, engine metadata, and recency.",
|
|
@@ -1055,6 +1247,114 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1055
1247
|
]
|
|
1056
1248
|
},
|
|
1057
1249
|
outputSchema: dossierOutputSchema
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
name: "martin_dossier",
|
|
1253
|
+
description: "Alias for martin_run_dossier with support for JSON, Markdown, or GitHub PR formatting. Use after martin_run to understand what happened and whether the result is actually safe to trust.",
|
|
1254
|
+
annotations: {
|
|
1255
|
+
readOnlyHint: true,
|
|
1256
|
+
idempotentHint: true
|
|
1257
|
+
},
|
|
1258
|
+
inputSchema: {
|
|
1259
|
+
type: "object",
|
|
1260
|
+
additionalProperties: false,
|
|
1261
|
+
properties: {
|
|
1262
|
+
file: { type: "string" },
|
|
1263
|
+
loopId: { type: "string" },
|
|
1264
|
+
runsDir: { type: "string" },
|
|
1265
|
+
latest: { const: true },
|
|
1266
|
+
format: { type: "string", enum: ["json", "md", "github-pr"] }
|
|
1267
|
+
},
|
|
1268
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1269
|
+
},
|
|
1270
|
+
outputSchema: dossierOutputSchema
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
name: "martin_eval",
|
|
1274
|
+
description: "Grade a Martin run for task completion, verifier health, diff discipline, risk, and reviewability.",
|
|
1275
|
+
annotations: {
|
|
1276
|
+
readOnlyHint: true,
|
|
1277
|
+
idempotentHint: true
|
|
1278
|
+
},
|
|
1279
|
+
inputSchema: {
|
|
1280
|
+
type: "object",
|
|
1281
|
+
additionalProperties: false,
|
|
1282
|
+
properties: {
|
|
1283
|
+
file: { type: "string" },
|
|
1284
|
+
loopId: { type: "string" },
|
|
1285
|
+
runsDir: { type: "string" },
|
|
1286
|
+
latest: { const: true }
|
|
1287
|
+
},
|
|
1288
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1289
|
+
},
|
|
1290
|
+
outputSchema: evalOutputSchema
|
|
1291
|
+
},
|
|
1292
|
+
{
|
|
1293
|
+
name: "martin_pr_summary",
|
|
1294
|
+
description: "Generate a PR title and body with a MartinLoop dossier block for a completed run.",
|
|
1295
|
+
annotations: {
|
|
1296
|
+
readOnlyHint: true,
|
|
1297
|
+
idempotentHint: true
|
|
1298
|
+
},
|
|
1299
|
+
inputSchema: {
|
|
1300
|
+
type: "object",
|
|
1301
|
+
additionalProperties: false,
|
|
1302
|
+
properties: {
|
|
1303
|
+
file: { type: "string" },
|
|
1304
|
+
loopId: { type: "string" },
|
|
1305
|
+
runsDir: { type: "string" },
|
|
1306
|
+
latest: { const: true },
|
|
1307
|
+
format: { type: "string", enum: ["json", "md", "github-pr"] }
|
|
1308
|
+
},
|
|
1309
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1310
|
+
},
|
|
1311
|
+
outputSchema: prSummaryOutputSchema
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
name: "martin_create_pr",
|
|
1315
|
+
description: "Create or preview a GitHub PR with a MartinLoop dossier body. Use execute=true to actually call gh.",
|
|
1316
|
+
annotations: {
|
|
1317
|
+
destructiveHint: true,
|
|
1318
|
+
idempotentHint: false
|
|
1319
|
+
},
|
|
1320
|
+
inputSchema: {
|
|
1321
|
+
type: "object",
|
|
1322
|
+
additionalProperties: false,
|
|
1323
|
+
properties: {
|
|
1324
|
+
file: { type: "string" },
|
|
1325
|
+
loopId: { type: "string" },
|
|
1326
|
+
runsDir: { type: "string" },
|
|
1327
|
+
latest: { const: true },
|
|
1328
|
+
format: { type: "string", enum: ["json", "md", "github-pr"] },
|
|
1329
|
+
title: { type: "string" },
|
|
1330
|
+
base: { type: "string" },
|
|
1331
|
+
execute: { type: "boolean" }
|
|
1332
|
+
},
|
|
1333
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1334
|
+
},
|
|
1335
|
+
outputSchema: prSummaryOutputSchema
|
|
1336
|
+
},
|
|
1337
|
+
{
|
|
1338
|
+
name: "martin_review_pr",
|
|
1339
|
+
description: "Review a PR or PR draft against the Martin dossier and evaluation evidence.",
|
|
1340
|
+
annotations: {
|
|
1341
|
+
readOnlyHint: true,
|
|
1342
|
+
idempotentHint: true
|
|
1343
|
+
},
|
|
1344
|
+
inputSchema: {
|
|
1345
|
+
type: "object",
|
|
1346
|
+
additionalProperties: false,
|
|
1347
|
+
properties: {
|
|
1348
|
+
file: { type: "string" },
|
|
1349
|
+
loopId: { type: "string" },
|
|
1350
|
+
runsDir: { type: "string" },
|
|
1351
|
+
latest: { const: true },
|
|
1352
|
+
format: { type: "string", enum: ["json", "md", "github-pr"] },
|
|
1353
|
+
prBody: { type: "string" }
|
|
1354
|
+
},
|
|
1355
|
+
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1356
|
+
},
|
|
1357
|
+
outputSchema: prReviewOutputSchema
|
|
1058
1358
|
}
|
|
1059
1359
|
]
|
|
1060
1360
|
}));
|
|
@@ -1113,11 +1413,58 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1113
1413
|
if (name === "martin_doctor") {
|
|
1114
1414
|
const input = validateToolInput("martin_doctor", args);
|
|
1115
1415
|
const output = await martinDoctorTool(input);
|
|
1416
|
+
await recordMcpWorkflowStep({
|
|
1417
|
+
runsRoot: output.environment.runsRoot,
|
|
1418
|
+
step: "doctor",
|
|
1419
|
+
workingDirectory: output.environment.workingDirectory,
|
|
1420
|
+
engine: input.engine
|
|
1421
|
+
}).catch(() => { });
|
|
1116
1422
|
return createToolSuccessResult(output, output.summary);
|
|
1117
1423
|
}
|
|
1424
|
+
if (name === "martin_plan") {
|
|
1425
|
+
const input = validateToolInput("martin_plan", args);
|
|
1426
|
+
const output = await martinPlanTool(input);
|
|
1427
|
+
await recordMcpWorkflowStep({
|
|
1428
|
+
runsRoot: resolveRunsRoot(process.env),
|
|
1429
|
+
step: "plan",
|
|
1430
|
+
workingDirectory: output.workingDirectory,
|
|
1431
|
+
objective: output.objective
|
|
1432
|
+
}).catch(() => { });
|
|
1433
|
+
return createToolSuccessResult(output, `Plan ready for ${output.objective} with ${output.risk.level} risk and ${output.approvalRecommendation.replace(/_/gu, " ")} approval.`);
|
|
1434
|
+
}
|
|
1118
1435
|
if (name === "martin_preflight") {
|
|
1119
1436
|
const input = validateToolInput("martin_preflight", args);
|
|
1120
1437
|
const output = await martinPreflightTool(input);
|
|
1438
|
+
if (output.ok) {
|
|
1439
|
+
await recordMcpWorkflowStep({
|
|
1440
|
+
runsRoot: output.execution.runsRoot,
|
|
1441
|
+
step: "preflight",
|
|
1442
|
+
workingDirectory: output.normalized.workingDirectory,
|
|
1443
|
+
objective: output.normalized.objective,
|
|
1444
|
+
engine: output.normalized.engine,
|
|
1445
|
+
verificationPlan: output.normalized.verificationPlan
|
|
1446
|
+
}).catch(() => { });
|
|
1447
|
+
}
|
|
1448
|
+
return createToolSuccessResult(output, output.summary);
|
|
1449
|
+
}
|
|
1450
|
+
if (name === "martin_logs") {
|
|
1451
|
+
const input = validateToolInput("martin_logs", args);
|
|
1452
|
+
const output = await martinLogsTool(input);
|
|
1453
|
+
return createToolSuccessResult(output, `Loaded ${output.logCount} log entries for Martin run ${output.loopId}.`);
|
|
1454
|
+
}
|
|
1455
|
+
if (name === "martin_pause") {
|
|
1456
|
+
const input = validateToolInput("martin_pause", args);
|
|
1457
|
+
const output = await createRunControlReceipt("pause", input);
|
|
1458
|
+
return createToolSuccessResult(output, output.summary);
|
|
1459
|
+
}
|
|
1460
|
+
if (name === "martin_cancel") {
|
|
1461
|
+
const input = validateToolInput("martin_cancel", args);
|
|
1462
|
+
const output = await createRunControlReceipt("cancel", input);
|
|
1463
|
+
return createToolSuccessResult(output, output.summary);
|
|
1464
|
+
}
|
|
1465
|
+
if (name === "martin_continue") {
|
|
1466
|
+
const input = validateToolInput("martin_continue", args);
|
|
1467
|
+
const output = await createRunControlReceipt("continue", input);
|
|
1121
1468
|
return createToolSuccessResult(output, output.summary);
|
|
1122
1469
|
}
|
|
1123
1470
|
if (name === "martin_list_runs") {
|
|
@@ -1150,6 +1497,33 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1150
1497
|
const output = await martinRunDossierTool(input);
|
|
1151
1498
|
return createToolSuccessResult(output, `Dossier ready for Martin run ${output.loop.loopId} with ${output.attempts.length} attempt(s).`);
|
|
1152
1499
|
}
|
|
1500
|
+
if (name === "martin_dossier") {
|
|
1501
|
+
const input = validateToolInput("martin_dossier", args);
|
|
1502
|
+
const output = await martinRunDossierTool(input);
|
|
1503
|
+
return createToolSuccessResult(output, `Dossier ready for Martin run ${output.loop.loopId} in ${output.format} format.`);
|
|
1504
|
+
}
|
|
1505
|
+
if (name === "martin_eval") {
|
|
1506
|
+
const input = validateToolInput("martin_eval", args);
|
|
1507
|
+
const output = await martinEvalTool(input);
|
|
1508
|
+
return createToolSuccessResult(output, `Evaluation for ${output.loopId}: ${output.grade} (${output.score}).`);
|
|
1509
|
+
}
|
|
1510
|
+
if (name === "martin_pr_summary") {
|
|
1511
|
+
const input = validateToolInput("martin_pr_summary", args);
|
|
1512
|
+
const output = await martinPrSummaryTool(input);
|
|
1513
|
+
return createToolSuccessResult(output, `PR summary ready for Martin run ${output.loopId}.`);
|
|
1514
|
+
}
|
|
1515
|
+
if (name === "martin_create_pr") {
|
|
1516
|
+
const input = validateToolInput("martin_create_pr", args);
|
|
1517
|
+
const output = await martinCreatePrTool(input);
|
|
1518
|
+
return createToolSuccessResult(output, output.created
|
|
1519
|
+
? `Created PR for Martin run ${output.loopId}.`
|
|
1520
|
+
: `PR preview ready for Martin run ${output.loopId}.`);
|
|
1521
|
+
}
|
|
1522
|
+
if (name === "martin_review_pr") {
|
|
1523
|
+
const input = validateToolInput("martin_review_pr", args);
|
|
1524
|
+
const output = await martinReviewPrTool(input);
|
|
1525
|
+
return createToolSuccessResult(output, `PR review verdict for ${output.loopId}: ${output.verdict}.`);
|
|
1526
|
+
}
|
|
1153
1527
|
return createToolErrorResult(toToolFailure(new Error(`Unknown tool: ${name}`)));
|
|
1154
1528
|
}
|
|
1155
1529
|
catch (error) {
|
package/dist/tools/doctor.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type CodexHostPlatform } from "../vendor/adapters/index.js";
|
|
1
2
|
import { type LoopPreview, type MartinEngine } from "./tool-support.js";
|
|
3
|
+
import { type MartinReadinessReport } from "./workflow-governance.js";
|
|
2
4
|
export interface MartinDoctorInput {
|
|
3
5
|
workingDirectory?: string;
|
|
4
6
|
runsDir?: string;
|
|
@@ -16,13 +18,23 @@ export interface MartinDoctorOutput {
|
|
|
16
18
|
workspaceRoot: string;
|
|
17
19
|
workingDirectory: string;
|
|
18
20
|
runsRoot: string;
|
|
19
|
-
mode: "live" | "
|
|
21
|
+
mode: "live" | "proof";
|
|
20
22
|
liveMode: boolean;
|
|
21
23
|
};
|
|
24
|
+
scope: {
|
|
25
|
+
invocationRoot: string;
|
|
26
|
+
workingDirectory: string;
|
|
27
|
+
repoRoot: string;
|
|
28
|
+
runsRoot: string;
|
|
29
|
+
};
|
|
22
30
|
engines: Record<MartinEngine, {
|
|
23
31
|
available: boolean;
|
|
24
32
|
detail: string;
|
|
25
33
|
resolvedPath?: string;
|
|
34
|
+
hostPlatform?: CodexHostPlatform;
|
|
35
|
+
nativeInstallValid?: boolean;
|
|
36
|
+
launchReady?: boolean;
|
|
37
|
+
probeSummary?: string;
|
|
26
38
|
}>;
|
|
27
39
|
requestedEngine?: MartinEngine;
|
|
28
40
|
runStore: {
|
|
@@ -30,6 +42,7 @@ export interface MartinDoctorOutput {
|
|
|
30
42
|
loopCount: number;
|
|
31
43
|
latestRun?: LoopPreview;
|
|
32
44
|
};
|
|
45
|
+
readiness: MartinReadinessReport;
|
|
33
46
|
warnings: string[];
|
|
34
47
|
}
|
|
35
48
|
export declare function martinDoctorTool(input: MartinDoctorInput): Promise<MartinDoctorOutput>;
|
package/dist/tools/doctor.js
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
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
4
|
import { getEngineAvailability, inspectRunsRoot, resolveExecutionMode } from "./tool-support.js";
|
|
5
|
+
import { buildReadinessReport, inspectRepoSignals } from "./workflow-governance.js";
|
|
4
6
|
export async function martinDoctorTool(input) {
|
|
5
7
|
const workingDirectory = resolveSafeRepoRoot(input.workingDirectory);
|
|
6
8
|
const runsRoot = resolveSafeRunsRootPath(input.runsDir, resolveRunsRoot(process.env));
|
|
9
|
+
const workspaceRoot = resolveSafeRepoRoot();
|
|
7
10
|
const executionMode = resolveExecutionMode();
|
|
8
11
|
const claude = getEngineAvailability("claude");
|
|
9
|
-
const codex =
|
|
12
|
+
const codex = resolveCliCommandAvailability("codex");
|
|
13
|
+
const gemini = getEngineAvailability("gemini");
|
|
14
|
+
const codexProbe = executionMode.liveMode && codex.available
|
|
15
|
+
? probeCodexLaunch({
|
|
16
|
+
workingDirectory,
|
|
17
|
+
availability: codex
|
|
18
|
+
})
|
|
19
|
+
: undefined;
|
|
10
20
|
const runStore = await inspectRunsRoot(runsRoot);
|
|
21
|
+
const signals = inspectRepoSignals(workingDirectory);
|
|
22
|
+
const readiness = buildReadinessReport(signals, runStore);
|
|
11
23
|
const warnings = [];
|
|
12
24
|
if (!runStore.exists) {
|
|
13
25
|
warnings.push("Configured Martin runs root does not exist yet.");
|
|
14
26
|
}
|
|
15
|
-
if (executionMode.liveMode && !claude.available && !codex.available) {
|
|
16
|
-
warnings.push("
|
|
27
|
+
if (executionMode.liveMode && !claude.available && !codex.available && !gemini.available) {
|
|
28
|
+
warnings.push("None of claude, codex, or gemini is currently available on PATH for live runs.");
|
|
17
29
|
}
|
|
18
30
|
if (input.engine && executionMode.liveMode) {
|
|
19
|
-
const selected = input.engine === "claude" ? claude : codex;
|
|
31
|
+
const selected = input.engine === "claude" ? claude : input.engine === "gemini" ? gemini : codex;
|
|
20
32
|
if (!selected.available) {
|
|
21
33
|
warnings.push(`Requested engine '${input.engine}' is not available on PATH.`);
|
|
22
34
|
}
|
|
35
|
+
if (input.engine === "codex" && codexProbe && !codexProbe.ok) {
|
|
36
|
+
warnings.push(codexProbe.summary);
|
|
37
|
+
}
|
|
23
38
|
}
|
|
24
39
|
warnings.push(...runStore.warnings);
|
|
25
40
|
const status = warnings.length === 0 ? "ok" : "degraded";
|
|
26
41
|
const summary = status === "ok"
|
|
27
|
-
? `Doctor passed: ${runStore.loopCount} run(s)
|
|
28
|
-
: `Doctor found ${warnings.length} issue(s);
|
|
42
|
+
? `Doctor passed: repo readiness ${readiness.score}/100 with ${runStore.loopCount} visible run(s).`
|
|
43
|
+
: `Doctor found ${warnings.length} issue(s); readiness ${readiness.score}/100 before live execution.`;
|
|
29
44
|
return {
|
|
30
45
|
status,
|
|
31
46
|
summary,
|
|
@@ -35,12 +50,18 @@ export async function martinDoctorTool(input) {
|
|
|
35
50
|
platform: process.platform
|
|
36
51
|
},
|
|
37
52
|
environment: {
|
|
38
|
-
workspaceRoot
|
|
53
|
+
workspaceRoot,
|
|
39
54
|
workingDirectory,
|
|
40
55
|
runsRoot,
|
|
41
56
|
mode: executionMode.mode,
|
|
42
57
|
liveMode: executionMode.liveMode
|
|
43
58
|
},
|
|
59
|
+
scope: {
|
|
60
|
+
invocationRoot: workspaceRoot,
|
|
61
|
+
workingDirectory,
|
|
62
|
+
repoRoot: workingDirectory,
|
|
63
|
+
runsRoot
|
|
64
|
+
},
|
|
44
65
|
engines: {
|
|
45
66
|
claude: {
|
|
46
67
|
available: claude.available,
|
|
@@ -50,7 +71,20 @@ export async function martinDoctorTool(input) {
|
|
|
50
71
|
codex: {
|
|
51
72
|
available: codex.available,
|
|
52
73
|
detail: codex.detail,
|
|
53
|
-
...(codex.resolvedPath ? { resolvedPath: codex.resolvedPath } : {})
|
|
74
|
+
...(codex.resolvedPath ? { resolvedPath: codex.resolvedPath } : {}),
|
|
75
|
+
...(codexProbe
|
|
76
|
+
? {
|
|
77
|
+
hostPlatform: codexProbe.diagnosis.hostPlatform,
|
|
78
|
+
nativeInstallValid: codexProbe.diagnosis.nativeInstallValid,
|
|
79
|
+
launchReady: codexProbe.ok,
|
|
80
|
+
probeSummary: codexProbe.summary
|
|
81
|
+
}
|
|
82
|
+
: {})
|
|
83
|
+
},
|
|
84
|
+
gemini: {
|
|
85
|
+
available: gemini.available,
|
|
86
|
+
detail: gemini.detail,
|
|
87
|
+
...(gemini.resolvedPath ? { resolvedPath: gemini.resolvedPath } : {})
|
|
54
88
|
}
|
|
55
89
|
},
|
|
56
90
|
...(input.engine ? { requestedEngine: input.engine } : {}),
|
|
@@ -59,6 +93,7 @@ export async function martinDoctorTool(input) {
|
|
|
59
93
|
loopCount: runStore.loopCount,
|
|
60
94
|
...(runStore.latestRun ? { latestRun: runStore.latestRun } : {})
|
|
61
95
|
},
|
|
96
|
+
readiness,
|
|
62
97
|
warnings
|
|
63
98
|
};
|
|
64
99
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface MartinEvalInput {
|
|
2
|
+
file?: string;
|
|
3
|
+
loopId?: string;
|
|
4
|
+
runsDir?: string;
|
|
5
|
+
latest?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface MartinEvalOutput {
|
|
8
|
+
source: string;
|
|
9
|
+
sourceKind: "file" | "loop_id" | "latest" | "runs_root";
|
|
10
|
+
loopId: string;
|
|
11
|
+
score: number;
|
|
12
|
+
grade: "mergeable" | "mergeable_with_review" | "needs_review" | "blocked" | "insufficient_evidence";
|
|
13
|
+
checks: {
|
|
14
|
+
taskCompletion: "passed" | "warning" | "failed";
|
|
15
|
+
verifier: "passed" | "warning" | "failed";
|
|
16
|
+
diffDiscipline: "passed" | "warning" | "failed";
|
|
17
|
+
regressionRisk: "passed" | "warning" | "failed";
|
|
18
|
+
securityRisk: "passed" | "warning" | "failed";
|
|
19
|
+
reviewability: "passed" | "warning" | "failed";
|
|
20
|
+
};
|
|
21
|
+
warnings: string[];
|
|
22
|
+
summary: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function martinEvalTool(input: MartinEvalInput): Promise<MartinEvalOutput>;
|