@martinloop/mcp 0.5.7 → 0.6.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/dist/arcade/capabilities.d.ts +84 -1
- package/dist/arcade/capabilities.js +75 -2
- package/dist/discovery-metadata.d.ts +1 -0
- package/dist/discovery-metadata.js +19 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/server.d.ts +9 -1
- package/dist/server.js +62 -42
- package/dist/vendor/core/index.js +26 -11
- package/package.json +13 -8
- package/server.json +2 -2
|
@@ -18,15 +18,98 @@ export declare function buildArcadeToolDefinitions(capabilities: unknown): reado
|
|
|
18
18
|
};
|
|
19
19
|
}, {
|
|
20
20
|
readonly name: "martin_arcade_status";
|
|
21
|
-
readonly description: "Read
|
|
21
|
+
readonly description: "Read the read-only evidence projection used by the Arcade view. Provide loopId for one exact run or omit it to load the latest run. Use only for Arcade rendering; use martin_status for budget pressure or martin_run_dossier for full evidence. This tool reads persisted evidence and never changes run state.";
|
|
22
|
+
readonly annotations: {
|
|
23
|
+
readonly readOnlyHint: true;
|
|
24
|
+
readonly destructiveHint: false;
|
|
25
|
+
readonly idempotentHint: true;
|
|
26
|
+
readonly openWorldHint: false;
|
|
27
|
+
};
|
|
22
28
|
readonly inputSchema: {
|
|
23
29
|
readonly type: "object";
|
|
24
30
|
readonly additionalProperties: false;
|
|
25
31
|
readonly properties: {
|
|
26
32
|
readonly loopId: {
|
|
27
33
|
readonly type: "string";
|
|
34
|
+
readonly description: "Optional MartinLoop run identifier. Omit it to read the latest persisted run.";
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
readonly outputSchema: {
|
|
39
|
+
readonly type: "object";
|
|
40
|
+
readonly additionalProperties: false;
|
|
41
|
+
readonly properties: {
|
|
42
|
+
readonly loopId: {
|
|
43
|
+
readonly type: "string";
|
|
44
|
+
readonly description: "Run identifier represented by this Arcade snapshot.";
|
|
45
|
+
};
|
|
46
|
+
readonly lifecycleState: {
|
|
47
|
+
readonly type: "string";
|
|
48
|
+
readonly description: "Current persisted lifecycle state.";
|
|
49
|
+
};
|
|
50
|
+
readonly completed: {
|
|
51
|
+
readonly type: "boolean";
|
|
52
|
+
readonly description: "Whether the run is outside an active lifecycle state.";
|
|
53
|
+
};
|
|
54
|
+
readonly displayOutcome: {
|
|
55
|
+
readonly anyOf: readonly [{
|
|
56
|
+
readonly type: "string";
|
|
57
|
+
readonly enum: readonly ["VERIFIED", "STOPPED", "NEEDS REVIEW"];
|
|
58
|
+
}, {
|
|
59
|
+
readonly type: "null";
|
|
60
|
+
}];
|
|
61
|
+
readonly description: "Terminal verified-handoff outcome, or null while the run is active.";
|
|
62
|
+
};
|
|
63
|
+
readonly verification: {
|
|
64
|
+
readonly type: "object";
|
|
65
|
+
readonly additionalProperties: true;
|
|
66
|
+
readonly description: "Persisted verifier evidence summary.";
|
|
67
|
+
};
|
|
68
|
+
readonly receiptIntegrity: {
|
|
69
|
+
readonly type: "object";
|
|
70
|
+
readonly additionalProperties: true;
|
|
71
|
+
readonly description: "Persisted receipt-integrity verdict.";
|
|
72
|
+
};
|
|
73
|
+
readonly attempts: {
|
|
74
|
+
readonly type: "integer";
|
|
75
|
+
readonly minimum: 0;
|
|
76
|
+
readonly description: "Number of recorded attempts.";
|
|
77
|
+
};
|
|
78
|
+
readonly cost: {
|
|
79
|
+
readonly type: "object";
|
|
80
|
+
readonly additionalProperties: false;
|
|
81
|
+
readonly properties: {
|
|
82
|
+
readonly actualUsd: {
|
|
83
|
+
readonly type: "number";
|
|
84
|
+
};
|
|
85
|
+
readonly provenance: {
|
|
86
|
+
readonly type: "string";
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
readonly required: readonly ["actualUsd", "provenance"];
|
|
90
|
+
};
|
|
91
|
+
readonly budget: {
|
|
92
|
+
readonly type: "object";
|
|
93
|
+
readonly additionalProperties: false;
|
|
94
|
+
readonly properties: {
|
|
95
|
+
readonly maxUsd: {
|
|
96
|
+
readonly type: "number";
|
|
97
|
+
};
|
|
98
|
+
readonly remainingUsd: {
|
|
99
|
+
readonly type: "number";
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
readonly required: readonly ["maxUsd", "remainingUsd"];
|
|
103
|
+
};
|
|
104
|
+
readonly warnings: {
|
|
105
|
+
readonly type: "array";
|
|
106
|
+
readonly items: {
|
|
107
|
+
readonly type: "string";
|
|
108
|
+
};
|
|
109
|
+
readonly description: "Evidence or interpretation warnings for the Arcade view.";
|
|
28
110
|
};
|
|
29
111
|
};
|
|
112
|
+
readonly required: readonly ["loopId", "lifecycleState", "completed", "displayOutcome", "verification", "receiptIntegrity", "attempts", "cost", "budget", "warnings"];
|
|
30
113
|
};
|
|
31
114
|
readonly _meta: {
|
|
32
115
|
readonly ui: {
|
|
@@ -25,11 +25,84 @@ export function buildArcadeToolDefinitions(capabilities) {
|
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
name: "martin_arcade_status",
|
|
28
|
-
description: "Read
|
|
28
|
+
description: "Read the read-only evidence projection used by the Arcade view. Provide loopId for one exact run or omit it to load the latest run. Use only for Arcade rendering; use martin_status for budget pressure or martin_run_dossier for full evidence. This tool reads persisted evidence and never changes run state.",
|
|
29
|
+
annotations: {
|
|
30
|
+
readOnlyHint: true,
|
|
31
|
+
destructiveHint: false,
|
|
32
|
+
idempotentHint: true,
|
|
33
|
+
openWorldHint: false
|
|
34
|
+
},
|
|
29
35
|
inputSchema: {
|
|
30
36
|
type: "object",
|
|
31
37
|
additionalProperties: false,
|
|
32
|
-
properties: {
|
|
38
|
+
properties: {
|
|
39
|
+
loopId: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Optional MartinLoop run identifier. Omit it to read the latest persisted run."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
outputSchema: {
|
|
46
|
+
type: "object",
|
|
47
|
+
additionalProperties: false,
|
|
48
|
+
properties: {
|
|
49
|
+
loopId: { type: "string", description: "Run identifier represented by this Arcade snapshot." },
|
|
50
|
+
lifecycleState: { type: "string", description: "Current persisted lifecycle state." },
|
|
51
|
+
completed: { type: "boolean", description: "Whether the run is outside an active lifecycle state." },
|
|
52
|
+
displayOutcome: {
|
|
53
|
+
anyOf: [
|
|
54
|
+
{ type: "string", enum: ["VERIFIED", "STOPPED", "NEEDS REVIEW"] },
|
|
55
|
+
{ type: "null" }
|
|
56
|
+
],
|
|
57
|
+
description: "Terminal verified-handoff outcome, or null while the run is active."
|
|
58
|
+
},
|
|
59
|
+
verification: {
|
|
60
|
+
type: "object",
|
|
61
|
+
additionalProperties: true,
|
|
62
|
+
description: "Persisted verifier evidence summary."
|
|
63
|
+
},
|
|
64
|
+
receiptIntegrity: {
|
|
65
|
+
type: "object",
|
|
66
|
+
additionalProperties: true,
|
|
67
|
+
description: "Persisted receipt-integrity verdict."
|
|
68
|
+
},
|
|
69
|
+
attempts: { type: "integer", minimum: 0, description: "Number of recorded attempts." },
|
|
70
|
+
cost: {
|
|
71
|
+
type: "object",
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
properties: {
|
|
74
|
+
actualUsd: { type: "number" },
|
|
75
|
+
provenance: { type: "string" }
|
|
76
|
+
},
|
|
77
|
+
required: ["actualUsd", "provenance"]
|
|
78
|
+
},
|
|
79
|
+
budget: {
|
|
80
|
+
type: "object",
|
|
81
|
+
additionalProperties: false,
|
|
82
|
+
properties: {
|
|
83
|
+
maxUsd: { type: "number" },
|
|
84
|
+
remainingUsd: { type: "number" }
|
|
85
|
+
},
|
|
86
|
+
required: ["maxUsd", "remainingUsd"]
|
|
87
|
+
},
|
|
88
|
+
warnings: {
|
|
89
|
+
type: "array",
|
|
90
|
+
items: { type: "string" },
|
|
91
|
+
description: "Evidence or interpretation warnings for the Arcade view."
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
required: [
|
|
95
|
+
"loopId",
|
|
96
|
+
"lifecycleState",
|
|
97
|
+
"completed",
|
|
98
|
+
"displayOutcome",
|
|
99
|
+
"verification",
|
|
100
|
+
"receiptIntegrity",
|
|
101
|
+
"attempts",
|
|
102
|
+
"cost",
|
|
103
|
+
"budget",
|
|
104
|
+
"warnings"
|
|
105
|
+
]
|
|
33
106
|
},
|
|
34
107
|
_meta: { ui: { visibility: ["app"] } }
|
|
35
108
|
}
|
|
@@ -4,6 +4,7 @@ export declare const MARTIN_MINIMAL_TOOL_NAMES: readonly ["martin_doctor", "mart
|
|
|
4
4
|
export declare const MARTIN_DIAGNOSTIC_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_logs", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_attempt", "martin_get_verification_results", "martin_dossier", "martin_eval"];
|
|
5
5
|
export declare const MARTIN_GITHUB_REVIEW_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_dossier", "martin_eval", "martin_pr_summary", "martin_create_pr", "martin_review_pr"];
|
|
6
6
|
export declare const MARTIN_PAID_REMOTE_TOOL_NAMES: readonly ["martin_doctor", "martin_plan", "martin_preflight", "martin_estimate", "martin_run", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_verification_results", "martin_dossier", "martin_eval"];
|
|
7
|
+
export declare const MARTIN_DIRECTORY_TOOL_NAMES: readonly ["martin_doctor", "martin_estimate", "martin_plan", "martin_preflight", "martin_run", "martin_pause", "martin_cancel", "martin_continue", "martin_list_runs", "martin_triage_runs", "martin_get_run", "martin_get_verification_results", "martin_dossier", "martin_logs", "martin_arcade", "martin_arcade_status"];
|
|
7
8
|
export declare const MARTIN_RESOURCE_URIS: readonly ["martin://server/health", "martin://runs/recent", "martin://runs/triage", "martin://runs/latest", "martin://runs/latest/summary", "martin://runs/latest/proof-card", "martin://runs/latest/budget-status", "martin://runs/latest/verifier-evidence", "martin://runs/latest/rollback-evidence", "martin://policies/current", "martin://repo/risk-map", "martin://verifiers/results", "martin://agent/next-step", "martin://agent/governance-status", "martin://agent/mode-status", "martin://agent/memory-summary", "martin://guides/mcp-usage", "martin://guides/agent-start", "martin://guides/command-map", "martin://guides/ide-onboarding", "martin://guides/operating-rules", "martin://guides/publish-readiness"];
|
|
8
9
|
export declare const MARTIN_RESOURCE_TEMPLATE_URIS: readonly ["martin://runs/{loopId}", "martin://runs/{loopId}/dossier", "martin://runs/{loopId}/attempts/{attemptIndex}", "martin://runs/{loopId}/verification"];
|
|
9
10
|
export declare const MARTIN_PROMPT_NAMES: readonly ["martin_start", "martin_preflight", "martin_triage", "martin_resume", "martin_prove", "martin_release_check", "martin_governed_coding_kickoff", "martin_debug_failed_run", "martin_publish_readiness_review", "martin_triage_run_store", "safe_bug_fix", "write_tests_first", "small_refactor", "security_review", "pr_review", "release_check"];
|
|
@@ -78,6 +78,24 @@ export const MARTIN_PAID_REMOTE_TOOL_NAMES = [
|
|
|
78
78
|
"martin_dossier",
|
|
79
79
|
"martin_eval"
|
|
80
80
|
];
|
|
81
|
+
export const MARTIN_DIRECTORY_TOOL_NAMES = [
|
|
82
|
+
"martin_doctor",
|
|
83
|
+
"martin_estimate",
|
|
84
|
+
"martin_plan",
|
|
85
|
+
"martin_preflight",
|
|
86
|
+
"martin_run",
|
|
87
|
+
"martin_pause",
|
|
88
|
+
"martin_cancel",
|
|
89
|
+
"martin_continue",
|
|
90
|
+
"martin_list_runs",
|
|
91
|
+
"martin_triage_runs",
|
|
92
|
+
"martin_get_run",
|
|
93
|
+
"martin_get_verification_results",
|
|
94
|
+
"martin_dossier",
|
|
95
|
+
"martin_logs",
|
|
96
|
+
"martin_arcade",
|
|
97
|
+
"martin_arcade_status"
|
|
98
|
+
];
|
|
81
99
|
export const MARTIN_RESOURCE_URIS = [
|
|
82
100
|
"martin://server/health",
|
|
83
101
|
"martin://runs/recent",
|
|
@@ -136,6 +154,7 @@ export function buildMartinDiscoveryMetadata(serverVersion) {
|
|
|
136
154
|
profiles: {
|
|
137
155
|
minimal: [...MARTIN_MINIMAL_TOOL_NAMES],
|
|
138
156
|
diagnostic: [...MARTIN_DIAGNOSTIC_TOOL_NAMES],
|
|
157
|
+
directory: [...MARTIN_DIRECTORY_TOOL_NAMES],
|
|
139
158
|
"github-review": [...MARTIN_GITHUB_REVIEW_TOOL_NAMES],
|
|
140
159
|
"full-local": [...MARTIN_TOOL_NAMES],
|
|
141
160
|
starter: [...MARTIN_STARTER_TOOL_NAMES],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MARTIN_MCP_PACKAGE_VERSION = "0.
|
|
1
|
+
export declare const MARTIN_MCP_PACKAGE_VERSION = "0.6.0";
|
package/dist/package-version.js
CHANGED
package/dist/server.d.ts
CHANGED
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { type Server as NodeHttpServer } from "node:http";
|
|
20
20
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
21
|
+
export type MartinMcpToolProfile = "full" | "directory";
|
|
22
|
+
export declare function shouldExposeMartinTool(name: string, profile: MartinMcpToolProfile): boolean;
|
|
21
23
|
export declare function createMartinMcpServer(serverInfo?: {
|
|
22
24
|
name?: string;
|
|
23
25
|
version?: string;
|
|
26
|
+
toolProfile?: MartinMcpToolProfile;
|
|
24
27
|
}): Server<{
|
|
25
28
|
method: string;
|
|
26
29
|
params?: {
|
|
@@ -55,7 +58,9 @@ export declare function createMartinMcpServer(serverInfo?: {
|
|
|
55
58
|
} | undefined;
|
|
56
59
|
} | undefined;
|
|
57
60
|
}>;
|
|
58
|
-
export declare function connectMartinMcpStdioServer(
|
|
61
|
+
export declare function connectMartinMcpStdioServer(options?: {
|
|
62
|
+
toolProfile?: MartinMcpToolProfile;
|
|
63
|
+
}): Promise<Server<{
|
|
59
64
|
method: string;
|
|
60
65
|
params?: {
|
|
61
66
|
[x: string]: unknown;
|
|
@@ -93,6 +98,7 @@ export interface MartinMcpHttpServerOptions {
|
|
|
93
98
|
host?: string;
|
|
94
99
|
port?: number;
|
|
95
100
|
path?: string;
|
|
101
|
+
toolProfile?: MartinMcpToolProfile;
|
|
96
102
|
}
|
|
97
103
|
export interface MartinMcpHttpServerHandle {
|
|
98
104
|
server: NodeHttpServer;
|
|
@@ -104,11 +110,13 @@ export interface MartinMcpHttpServerHandle {
|
|
|
104
110
|
}
|
|
105
111
|
export declare function parseMartinMcpServerArgs(argv: string[]): {
|
|
106
112
|
transport: "stdio";
|
|
113
|
+
toolProfile?: MartinMcpToolProfile;
|
|
107
114
|
} | {
|
|
108
115
|
transport: "http";
|
|
109
116
|
host: string;
|
|
110
117
|
port: number;
|
|
111
118
|
path: string;
|
|
119
|
+
toolProfile?: MartinMcpToolProfile;
|
|
112
120
|
};
|
|
113
121
|
export declare function connectMartinMcpHttpServer(options?: MartinMcpHttpServerOptions): Promise<MartinMcpHttpServerHandle>;
|
|
114
122
|
export declare function isDirectExecutionEntry(entryPath: string | undefined, moduleUrl?: string): boolean;
|
package/dist/server.js
CHANGED
|
@@ -26,6 +26,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
26
26
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
27
27
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
28
28
|
import { MARTIN_MCP_PACKAGE_VERSION } from "./package-version.js";
|
|
29
|
+
import { MARTIN_DIRECTORY_TOOL_NAMES } from "./discovery-metadata.js";
|
|
29
30
|
import { MARTIN_ARCADE_FALLBACK_TEXT, MARTIN_ARCADE_MIME_TYPE, MARTIN_ARCADE_RESOURCE_URI, MARTIN_ARCADE_UI_EXTENSION_ID, buildArcadeToolDefinitions, supportsMartinArcadeApp } from "./arcade/capabilities.js";
|
|
30
31
|
import { listArcadeResources, readArcadeResource } from "./arcade/resource.js";
|
|
31
32
|
import { readArcadeStatus } from "./arcade/status.js";
|
|
@@ -59,6 +60,10 @@ const stringArraySchema = {
|
|
|
59
60
|
type: "array",
|
|
60
61
|
items: { type: "string" }
|
|
61
62
|
};
|
|
63
|
+
const martinDirectoryToolNames = new Set(MARTIN_DIRECTORY_TOOL_NAMES);
|
|
64
|
+
export function shouldExposeMartinTool(name, profile) {
|
|
65
|
+
return profile === "full" || martinDirectoryToolNames.has(name);
|
|
66
|
+
}
|
|
62
67
|
const loopPreviewSchema = {
|
|
63
68
|
type: "object",
|
|
64
69
|
additionalProperties: true,
|
|
@@ -839,6 +844,7 @@ const prReviewOutputSchema = {
|
|
|
839
844
|
required: ["loopId", "verdict", "findings", "summary"]
|
|
840
845
|
};
|
|
841
846
|
export function createMartinMcpServer(serverInfo) {
|
|
847
|
+
const toolProfile = serverInfo?.toolProfile ?? "full";
|
|
842
848
|
const server = new Server({
|
|
843
849
|
name: serverInfo?.name ?? "martin-loop",
|
|
844
850
|
version: serverInfo?.version ?? MARTIN_MCP_PACKAGE_VERSION
|
|
@@ -856,7 +862,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
856
862
|
tools: [
|
|
857
863
|
{
|
|
858
864
|
name: "martin_run",
|
|
859
|
-
description: "Execute a governed
|
|
865
|
+
description: "Execute a governed MartinLoop coding run after MCP workflow admission is satisfied. Use when the user has authorized implementation, bug fixing, tests, or refactoring and doctor/estimate/plan/preflight receipts match this task. Do not use for question-only diagnosis or when policy, budget, credentials, or scope still need consent. Next: read martin_dossier and verifier evidence.",
|
|
860
866
|
annotations: {
|
|
861
867
|
destructiveHint: true,
|
|
862
868
|
idempotentHint: false,
|
|
@@ -961,7 +967,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
961
967
|
},
|
|
962
968
|
{
|
|
963
969
|
name: "martin_status",
|
|
964
|
-
description: "
|
|
970
|
+
description: "Read the current budget, cost, remaining limits, and stop pressure for one MartinLoop run. Provide exactly one selector: loopJson for an inline record, file for a saved record, loopId for a run-store ID, or latest for the newest run; runsDir only changes the run-store root. Use for a compact budget check. Do not use for full events or artifacts; use martin_get_run or martin_run_dossier instead.",
|
|
965
971
|
annotations: {
|
|
966
972
|
readOnlyHint: true,
|
|
967
973
|
idempotentHint: true
|
|
@@ -973,11 +979,11 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
973
979
|
loopJson: { type: "string", description: "JSON-serialized LoopRecord." },
|
|
974
980
|
file: {
|
|
975
981
|
type: "string",
|
|
976
|
-
description: "
|
|
982
|
+
description: "Path under the Martin runs root to a loop-record.json file, a legacy .jsonl file, or a run-store directory. Mutually exclusive with loopJson, loopId, and latest."
|
|
977
983
|
},
|
|
978
984
|
loopId: {
|
|
979
985
|
type: "string",
|
|
980
|
-
description: "Loop ID resolved as <runsDir>/<loopId>/loop-record.json."
|
|
986
|
+
description: "Loop ID resolved as <runsDir>/<loopId>/loop-record.json. Mutually exclusive with loopJson, file, and latest."
|
|
981
987
|
},
|
|
982
988
|
runsDir: {
|
|
983
989
|
type: "string",
|
|
@@ -985,7 +991,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
985
991
|
},
|
|
986
992
|
latest: {
|
|
987
993
|
const: true,
|
|
988
|
-
description: "When true, loads the most recently updated loop record
|
|
994
|
+
description: "When true, loads the most recently updated loop record. Mutually exclusive with loopJson, file, and loopId."
|
|
989
995
|
}
|
|
990
996
|
},
|
|
991
997
|
oneOf: [
|
|
@@ -999,7 +1005,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
999
1005
|
},
|
|
1000
1006
|
{
|
|
1001
1007
|
name: "martin_doctor",
|
|
1002
|
-
description: "Read-only environment and run-store diagnostics for
|
|
1008
|
+
description: "Read-only environment, engine, workspace, and run-store diagnostics for MartinLoop. Use first for software work, fresh installs, suspicious state, or before retries. Do not use as proof that a task is complete. Next: call martin_estimate or martin_triage_runs depending on whether this is new work or failed prior work.",
|
|
1003
1009
|
annotations: {
|
|
1004
1010
|
readOnlyHint: true,
|
|
1005
1011
|
idempotentHint: true
|
|
@@ -1027,7 +1033,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1027
1033
|
},
|
|
1028
1034
|
{
|
|
1029
1035
|
name: "martin_plan",
|
|
1030
|
-
description: "Read-only planning step that turns an objective into
|
|
1036
|
+
description: "Read-only planning step that turns an objective into bounded scope, verifier proposal, policy pack, and risk recommendation. Use for authorized software changes before preflight/run. Do not use to mutate files or replace the verifier. Next: call martin_preflight with the chosen scope, budget, and verifier.",
|
|
1031
1037
|
annotations: {
|
|
1032
1038
|
readOnlyHint: true,
|
|
1033
1039
|
idempotentHint: true
|
|
@@ -1062,7 +1068,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1062
1068
|
},
|
|
1063
1069
|
{
|
|
1064
1070
|
name: "martin_preflight",
|
|
1065
|
-
description: "Read-only validation of
|
|
1071
|
+
description: "Read-only validation of the exact run contract before execution or spend. Use after planning and before martin_run to check engine, verifier, path scope, and budget. Do not use as execution or completion proof. Next: call martin_run if allowed, otherwise resolve the reported blocker.",
|
|
1066
1072
|
annotations: {
|
|
1067
1073
|
readOnlyHint: true,
|
|
1068
1074
|
idempotentHint: true
|
|
@@ -1177,7 +1183,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1177
1183
|
},
|
|
1178
1184
|
{
|
|
1179
1185
|
name: "martin_pause",
|
|
1180
|
-
description: "
|
|
1186
|
+
description: "Write a durable pause receipt for one canonical MartinLoop run. Provide exactly one selector: file, loopId, or latest; runsDir changes the run-store root, while reason and requestedBy add audit context. Use for a temporary hold before risky follow-up work. This records a request and does not kill a process; use martin_cancel to abandon work or martin_continue to resume.",
|
|
1181
1187
|
annotations: {
|
|
1182
1188
|
destructiveHint: true,
|
|
1183
1189
|
idempotentHint: false
|
|
@@ -1186,12 +1192,12 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1186
1192
|
type: "object",
|
|
1187
1193
|
additionalProperties: false,
|
|
1188
1194
|
properties: {
|
|
1189
|
-
file: { type: "string" },
|
|
1190
|
-
loopId: { type: "string" },
|
|
1191
|
-
runsDir: { type: "string" },
|
|
1192
|
-
latest: { const: true },
|
|
1193
|
-
reason: { type: "string" },
|
|
1194
|
-
requestedBy: { type: "string" }
|
|
1195
|
+
file: { type: "string", description: "Path to one canonical run record or directory. Mutually exclusive with loopId and latest." },
|
|
1196
|
+
loopId: { type: "string", description: "Canonical MartinLoop run identifier. Mutually exclusive with file and latest." },
|
|
1197
|
+
runsDir: { type: "string", description: "Optional run-store root override used to resolve loopId or latest." },
|
|
1198
|
+
latest: { const: true, description: "When true, targets the latest canonical run. Mutually exclusive with file and loopId." },
|
|
1199
|
+
reason: { type: "string", description: "Optional non-empty reason recorded in the pause receipt." },
|
|
1200
|
+
requestedBy: { type: "string", description: "Optional human or runtime identity label recorded for audit context." }
|
|
1195
1201
|
},
|
|
1196
1202
|
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1197
1203
|
},
|
|
@@ -1199,7 +1205,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1199
1205
|
},
|
|
1200
1206
|
{
|
|
1201
1207
|
name: "martin_cancel",
|
|
1202
|
-
description: "
|
|
1208
|
+
description: "Write a durable cancellation receipt for one canonical MartinLoop run. Provide exactly one selector: file, loopId, or latest; runsDir changes the run-store root, while reason and requestedBy add audit context. Use when work must be abandoned, not temporarily held. This records a request and does not kill a process; use martin_pause for a reversible hold.",
|
|
1203
1209
|
annotations: {
|
|
1204
1210
|
destructiveHint: true,
|
|
1205
1211
|
idempotentHint: false
|
|
@@ -1208,12 +1214,12 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1208
1214
|
type: "object",
|
|
1209
1215
|
additionalProperties: false,
|
|
1210
1216
|
properties: {
|
|
1211
|
-
file: { type: "string" },
|
|
1212
|
-
loopId: { type: "string" },
|
|
1213
|
-
runsDir: { type: "string" },
|
|
1214
|
-
latest: { const: true },
|
|
1215
|
-
reason: { type: "string" },
|
|
1216
|
-
requestedBy: { type: "string" }
|
|
1217
|
+
file: { type: "string", description: "Path to one canonical run record or directory. Mutually exclusive with loopId and latest." },
|
|
1218
|
+
loopId: { type: "string", description: "Canonical MartinLoop run identifier. Mutually exclusive with file and latest." },
|
|
1219
|
+
runsDir: { type: "string", description: "Optional run-store root override used to resolve loopId or latest." },
|
|
1220
|
+
latest: { const: true, description: "When true, targets the latest canonical run. Mutually exclusive with file and loopId." },
|
|
1221
|
+
reason: { type: "string", description: "Optional non-empty reason recorded in the cancellation receipt." },
|
|
1222
|
+
requestedBy: { type: "string", description: "Optional human or runtime identity label recorded for audit context." }
|
|
1217
1223
|
},
|
|
1218
1224
|
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1219
1225
|
},
|
|
@@ -1272,7 +1278,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1272
1278
|
},
|
|
1273
1279
|
{
|
|
1274
1280
|
name: "martin_triage_runs",
|
|
1275
|
-
description: "
|
|
1281
|
+
description: "Read-only prioritization of saved Martin runs that need attention. Use when the user says a prior attempt failed, asks what to fix next, or resumes an interrupted session. Do not use for a brand-new objective with no relevant run history. Next: inspect the selected run or dossier before retrying.",
|
|
1276
1282
|
annotations: {
|
|
1277
1283
|
readOnlyHint: true,
|
|
1278
1284
|
idempotentHint: true
|
|
@@ -1393,7 +1399,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1393
1399
|
},
|
|
1394
1400
|
{
|
|
1395
1401
|
name: "martin_run_dossier",
|
|
1396
|
-
description: "
|
|
1402
|
+
description: "Read the full structured execution dossier for one MartinLoop run, including attempts, events, artifacts, verification, integrity, cost, and related discovery surfaces. Provide exactly one selector: file, loopId, or latest; runsDir changes the run-store root. Use for comprehensive evidence review. Do not use for a compact state check; use martin_get_run, or use martin_dossier when formatted sharing output is required.",
|
|
1397
1403
|
annotations: {
|
|
1398
1404
|
readOnlyHint: true,
|
|
1399
1405
|
idempotentHint: true
|
|
@@ -1404,13 +1410,13 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1404
1410
|
properties: {
|
|
1405
1411
|
file: {
|
|
1406
1412
|
type: "string",
|
|
1407
|
-
description: "Path to a canonical loop-record.json, legacy file, or run-store directory."
|
|
1413
|
+
description: "Path to a canonical loop-record.json, legacy file, or run-store directory. Mutually exclusive with loopId and latest."
|
|
1408
1414
|
},
|
|
1409
|
-
loopId: { type: "string", description: "Loop ID under the run store." },
|
|
1410
|
-
runsDir: { type: "string", description: "Optional
|
|
1415
|
+
loopId: { type: "string", description: "Loop ID under the run store. Mutually exclusive with file and latest." },
|
|
1416
|
+
runsDir: { type: "string", description: "Optional run-store root override used to resolve loopId or latest." },
|
|
1411
1417
|
latest: {
|
|
1412
1418
|
const: true,
|
|
1413
|
-
description: "When true, loads the most recently updated loop record
|
|
1419
|
+
description: "When true, loads the most recently updated loop record. Mutually exclusive with file and loopId."
|
|
1414
1420
|
}
|
|
1415
1421
|
},
|
|
1416
1422
|
oneOf: [
|
|
@@ -1423,7 +1429,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1423
1429
|
},
|
|
1424
1430
|
{
|
|
1425
1431
|
name: "martin_dossier",
|
|
1426
|
-
description: "
|
|
1432
|
+
description: "Read a formatted evidence summary for one MartinLoop run. Provide exactly one selector: file, loopId, or latest; runsDir changes the run-store root. Set format to json, md, or github-pr; json is the default. Use after martin_run, before merge or release claims, or when sharing what happened. Do not use as a substitute for missing verifier evidence; use martin_run_dossier for the full structured record. Next: review verification results, retry, or hand off the receipt.",
|
|
1427
1433
|
annotations: {
|
|
1428
1434
|
readOnlyHint: true,
|
|
1429
1435
|
idempotentHint: true
|
|
@@ -1432,11 +1438,11 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1432
1438
|
type: "object",
|
|
1433
1439
|
additionalProperties: false,
|
|
1434
1440
|
properties: {
|
|
1435
|
-
file: { type: "string" },
|
|
1436
|
-
loopId: { type: "string" },
|
|
1437
|
-
runsDir: { type: "string" },
|
|
1438
|
-
latest: { const: true },
|
|
1439
|
-
format: { type: "string", enum: ["json", "md", "github-pr"] }
|
|
1441
|
+
file: { type: "string", description: "Path to one run record or directory. Mutually exclusive with loopId and latest." },
|
|
1442
|
+
loopId: { type: "string", description: "MartinLoop run identifier. Mutually exclusive with file and latest." },
|
|
1443
|
+
runsDir: { type: "string", description: "Optional run-store root override used to resolve loopId or latest." },
|
|
1444
|
+
latest: { const: true, description: "When true, loads the latest run. Mutually exclusive with file and loopId." },
|
|
1445
|
+
format: { type: "string", enum: ["json", "md", "github-pr"], description: "Output format. Defaults to json." }
|
|
1440
1446
|
},
|
|
1441
1447
|
oneOf: [{ required: ["file"] }, { required: ["loopId"] }, { required: ["latest"] }]
|
|
1442
1448
|
},
|
|
@@ -1552,7 +1558,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1552
1558
|
},
|
|
1553
1559
|
{
|
|
1554
1560
|
name: "martin_estimate",
|
|
1555
|
-
description: "
|
|
1561
|
+
description: "Read-only cost, route, and pre-work burn estimate for a software objective. Use before planning/preflight when a change may spend agent time or exceed budget. Do not use for casual questions or as permission to execute. Next: call martin_plan if the estimate is acceptable, or ask for consent if budget/risk is high.",
|
|
1556
1562
|
annotations: {
|
|
1557
1563
|
readOnlyHint: true,
|
|
1558
1564
|
idempotentHint: true
|
|
@@ -1608,7 +1614,7 @@ export function createMartinMcpServer(serverInfo) {
|
|
|
1608
1614
|
}
|
|
1609
1615
|
},
|
|
1610
1616
|
...buildArcadeToolDefinitions(server.getClientCapabilities())
|
|
1611
|
-
]
|
|
1617
|
+
].filter((tool) => shouldExposeMartinTool(tool.name, toolProfile))
|
|
1612
1618
|
}));
|
|
1613
1619
|
server.setRequestHandler(ListResourcesRequestSchema, () => {
|
|
1614
1620
|
const listed = listMartinResources();
|
|
@@ -1929,8 +1935,10 @@ function normalizeRunBudget(input) {
|
|
|
1929
1935
|
maxTokens: input.maxTokens
|
|
1930
1936
|
});
|
|
1931
1937
|
}
|
|
1932
|
-
export async function connectMartinMcpStdioServer() {
|
|
1933
|
-
const server = createMartinMcpServer(
|
|
1938
|
+
export async function connectMartinMcpStdioServer(options = {}) {
|
|
1939
|
+
const server = createMartinMcpServer({
|
|
1940
|
+
...(options.toolProfile ? { toolProfile: options.toolProfile } : {})
|
|
1941
|
+
});
|
|
1934
1942
|
const transport = new StdioServerTransport();
|
|
1935
1943
|
await server.connect(transport);
|
|
1936
1944
|
return server;
|
|
@@ -1940,12 +1948,22 @@ export function parseMartinMcpServerArgs(argv) {
|
|
|
1940
1948
|
let host = "127.0.0.1";
|
|
1941
1949
|
let port = 3033;
|
|
1942
1950
|
let path = "/mcp";
|
|
1951
|
+
let toolProfile;
|
|
1943
1952
|
for (let index = 0; index < argv.length; index += 1) {
|
|
1944
1953
|
const token = argv[index];
|
|
1945
1954
|
if (token === "--http") {
|
|
1946
1955
|
transport = "http";
|
|
1947
1956
|
continue;
|
|
1948
1957
|
}
|
|
1958
|
+
if (token === "--tool-profile") {
|
|
1959
|
+
const next = argv[index + 1];
|
|
1960
|
+
if (next !== "full" && next !== "directory") {
|
|
1961
|
+
throw new Error(`Invalid --tool-profile value '${next ?? ""}'.`);
|
|
1962
|
+
}
|
|
1963
|
+
toolProfile = next;
|
|
1964
|
+
index += 1;
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1949
1967
|
if (token === "--port") {
|
|
1950
1968
|
const next = argv[index + 1];
|
|
1951
1969
|
const parsed = Number(next);
|
|
@@ -1975,14 +1993,16 @@ export function parseMartinMcpServerArgs(argv) {
|
|
|
1975
1993
|
}
|
|
1976
1994
|
}
|
|
1977
1995
|
return transport === "http"
|
|
1978
|
-
? { transport, host, port, path }
|
|
1979
|
-
: { transport };
|
|
1996
|
+
? { transport, host, port, path, ...(toolProfile ? { toolProfile } : {}) }
|
|
1997
|
+
: { transport, ...(toolProfile ? { toolProfile } : {}) };
|
|
1980
1998
|
}
|
|
1981
1999
|
export async function connectMartinMcpHttpServer(options = {}) {
|
|
1982
2000
|
const host = options.host ?? "127.0.0.1";
|
|
1983
2001
|
const port = options.port ?? 3033;
|
|
1984
2002
|
const path = options.path ?? "/mcp";
|
|
1985
|
-
const mcpServer = createMartinMcpServer(
|
|
2003
|
+
const mcpServer = createMartinMcpServer({
|
|
2004
|
+
...(options.toolProfile ? { toolProfile: options.toolProfile } : {})
|
|
2005
|
+
});
|
|
1986
2006
|
const transport = new StreamableHTTPServerTransport({
|
|
1987
2007
|
sessionIdGenerator: undefined
|
|
1988
2008
|
});
|
|
@@ -2068,7 +2088,7 @@ if (isDirectExecution()) {
|
|
|
2068
2088
|
await connectMartinMcpHttpServer(startup);
|
|
2069
2089
|
}
|
|
2070
2090
|
else {
|
|
2071
|
-
await connectMartinMcpStdioServer();
|
|
2091
|
+
await connectMartinMcpStdioServer({ toolProfile: startup.toolProfile });
|
|
2072
2092
|
}
|
|
2073
2093
|
}
|
|
2074
2094
|
async function readJsonBody(req, res) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
1
2
|
import { DEFAULT_AGENT_EXECUTION_INTENT, normalizeProviderExecutionTimeoutMs, appendLoopEvent, createLoopRecord } from "../contracts/index.js";
|
|
2
3
|
import { classifyFailure, computeEvidenceVector, evaluatePatchDecision, evaluateCostGovernor, evaluateBudgetPreflight, inferExit, nextPolicyPhase, policyPhaseToLifecycleState, scorePatchDecision, selectRecoveryRecipe } from "./policy.js";
|
|
3
4
|
import { evaluateChangeApprovalLeash, evaluateFilesystemLeash, evaluateSecretLeash, redactSecretsFromText, resolveExecutionProfile, evaluateVerificationLeash } from "./leash.js";
|
|
@@ -1135,7 +1136,7 @@ export async function runMartin(input) {
|
|
|
1135
1136
|
// VERIFY: Run grounding scan on patch diff if available
|
|
1136
1137
|
// Uses the task's repoRoot to build/load the grounding index, then scans any diff
|
|
1137
1138
|
let groundingScanResult;
|
|
1138
|
-
const patchDiff = buildPatchDiff(result, changedFiles);
|
|
1139
|
+
const patchDiff = buildPatchDiff(result, changedFiles, input.task.repoRoot);
|
|
1139
1140
|
if (patchDiff && input.task.repoRoot) {
|
|
1140
1141
|
try {
|
|
1141
1142
|
const groundingIndex = await loadOrBuildRepoGroundingIndex(input.task.repoRoot);
|
|
@@ -1568,21 +1569,35 @@ function resolveChangedFiles(result, repoRoot, rollbackBoundary) {
|
|
|
1568
1569
|
}
|
|
1569
1570
|
return listAttemptChangedFilesSinceBoundary({ repoRoot, boundary: rollbackBoundary });
|
|
1570
1571
|
}
|
|
1571
|
-
function buildPatchDiff(result, changedFiles) {
|
|
1572
|
+
function buildPatchDiff(result, changedFiles, repoRoot) {
|
|
1572
1573
|
// Use structured diff stats to build a minimal diff header if no raw diff is available
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
return
|
|
1576
|
-
.map((file) =>
|
|
1577
|
-
.join("\n");
|
|
1578
|
-
}
|
|
1579
|
-
if (changedFiles.length > 0) {
|
|
1580
|
-
return changedFiles
|
|
1581
|
-
.map((file) => `--- a/${file}\n+++ b/${file}\n@@ -0,0 +1 @@\n+`)
|
|
1574
|
+
const files = result.execution?.changedFiles?.length ? result.execution.changedFiles : changedFiles;
|
|
1575
|
+
if (files.length > 0) {
|
|
1576
|
+
return files
|
|
1577
|
+
.map((file) => buildSyntheticDiffHeader(file, repoRoot))
|
|
1582
1578
|
.join("\n");
|
|
1583
1579
|
}
|
|
1584
1580
|
return undefined;
|
|
1585
1581
|
}
|
|
1582
|
+
function buildSyntheticDiffHeader(file, repoRoot) {
|
|
1583
|
+
const normalizedFile = file.replace(/\\/gu, "/");
|
|
1584
|
+
const isNewWorkspaceFile = repoRoot ? isUntrackedWorkspaceFile(repoRoot, normalizedFile) : false;
|
|
1585
|
+
const before = isNewWorkspaceFile ? "/dev/null" : `a/${normalizedFile}`;
|
|
1586
|
+
return `--- ${before}\n+++ b/${normalizedFile}\n@@ -0,0 +1 @@\n+`;
|
|
1587
|
+
}
|
|
1588
|
+
function isUntrackedWorkspaceFile(repoRoot, file) {
|
|
1589
|
+
const result = spawnSync("git", ["ls-files", "--others", "--exclude-standard", "--", file], {
|
|
1590
|
+
cwd: repoRoot,
|
|
1591
|
+
encoding: "utf8"
|
|
1592
|
+
});
|
|
1593
|
+
if (result.status !== 0 || typeof result.stdout !== "string") {
|
|
1594
|
+
return false;
|
|
1595
|
+
}
|
|
1596
|
+
return result.stdout
|
|
1597
|
+
.split(/\r?\n/u)
|
|
1598
|
+
.map((line) => line.trim().replace(/\\/gu, "/"))
|
|
1599
|
+
.includes(file);
|
|
1600
|
+
}
|
|
1586
1601
|
function createBudgetSettlement(input) {
|
|
1587
1602
|
const totalActualUsd = getUsageUsd(input.usage);
|
|
1588
1603
|
return {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@martinloop/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"mcpName": "io.github.Keesan12/martin-loop",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
|
-
"description": "MCP server for AI coding agents
|
|
7
|
+
"description": "MCP server for AI coding agents — verifier gates, stop limits, and Verified Handoffs.",
|
|
8
8
|
"license": "Apache-2.0",
|
|
9
9
|
"author": "MartinLoop contributors",
|
|
10
10
|
"homepage": "https://martinloop.com/",
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
"martin-loop-mcp": "./dist/server.js"
|
|
42
42
|
},
|
|
43
43
|
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./dist/server.d.ts",
|
|
46
|
+
"import": "./dist/server.js",
|
|
47
|
+
"default": "./dist/server.js"
|
|
48
|
+
},
|
|
44
49
|
"./server.json": "./server.json",
|
|
45
50
|
"./package.json": "./package.json"
|
|
46
51
|
},
|
|
@@ -74,12 +79,12 @@
|
|
|
74
79
|
"mcpb:smoke": "node ./mcpb/smoke-mcpb.mjs"
|
|
75
80
|
},
|
|
76
81
|
"dependencies": {
|
|
77
|
-
"@modelcontextprotocol/sdk": "
|
|
82
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
78
83
|
"@open-policy-agent/opa-wasm": "^1.10.0",
|
|
79
|
-
"@opentelemetry/api-logs": "^0.
|
|
80
|
-
"@opentelemetry/exporter-logs-otlp-http": "^0.
|
|
81
|
-
"@opentelemetry/resources": "^2.
|
|
82
|
-
"@opentelemetry/sdk-logs": "^0.
|
|
84
|
+
"@opentelemetry/api-logs": "^0.221.0",
|
|
85
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.221.0",
|
|
86
|
+
"@opentelemetry/resources": "^2.10.0",
|
|
87
|
+
"@opentelemetry/sdk-logs": "^0.221.0",
|
|
83
88
|
"ts-morph": "^21.0.0"
|
|
84
89
|
},
|
|
85
90
|
"devDependencies": {
|
|
@@ -87,4 +92,4 @@
|
|
|
87
92
|
"@martin/contracts": "workspace:*",
|
|
88
93
|
"@martin/presentation": "workspace:*"
|
|
89
94
|
}
|
|
90
|
-
}
|
|
95
|
+
}
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/Keesan12/martin-loop",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.6.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@martinloop/mcp",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.6.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|