@martinloop/mcp 0.5.6 → 0.5.8
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 +137 -175
- package/dist/arcade/resource.js +20 -20
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resources.js +148 -160
- package/dist/server.js +192 -43
- package/dist/tools/execution-policy.js +1 -1
- package/dist/tools/get-status.d.ts +2 -4
- package/dist/tools/get-status.js +6 -3
- package/dist/tools/preflight.d.ts +1 -1
- package/dist/tools/run-loop.d.ts +1 -1
- package/dist/tools/run-loop.js +3 -1
- package/dist/tools/run-store.js +1 -1
- package/dist/tools/tool-support.d.ts +3 -2
- package/dist/tools/tool-support.js +9 -2
- package/dist/tools/workflow-governance.d.ts +1 -1
- package/dist/tools/workflow-governance.js +1 -1
- package/dist/vendor/adapters/openai-compatible.js +6 -6
- package/dist/vendor/adapters/verifier-only.js +1 -1
- package/dist/vendor/contracts/execution-policy.js +1 -1
- package/dist/vendor/contracts/exits.d.ts +1 -1
- package/dist/vendor/contracts/governance.d.ts +1 -1
- package/dist/vendor/contracts/index.d.ts +1 -1
- package/dist/vendor/contracts/index.js +1 -2
- package/dist/vendor/contracts/operator.d.ts +1 -1
- package/dist/vendor/contracts/operator.js +1 -2
- package/dist/vendor/core/compiler.d.ts +2 -2
- package/dist/vendor/core/compiler.js +3 -1
- package/dist/vendor/core/exits.js +9 -4
- package/dist/vendor/core/grounding.d.ts +1 -0
- package/dist/vendor/core/grounding.js +28 -1
- package/dist/vendor/core/index.d.ts +2 -2
- package/dist/vendor/core/index.js +29 -12
- package/dist/vendor/core/policy-compiler.js +17 -1
- package/dist/vendor/core/policy.d.ts +1 -1
- package/dist/vendor/core/policy.js +7 -5
- package/dist/vendor/presentation/motion.js +0 -4
- package/dist/workflow-state.js +1 -1
- package/package.json +95 -81
- package/server.json +21 -21
|
@@ -20,7 +20,7 @@ export interface CompilerAdapterRequest {
|
|
|
20
20
|
focus: string;
|
|
21
21
|
remainingBudgetUsd: number;
|
|
22
22
|
remainingIterations: number;
|
|
23
|
-
remainingTokens
|
|
23
|
+
remainingTokens?: number;
|
|
24
24
|
};
|
|
25
25
|
previousAttempts: LoopAttempt[];
|
|
26
26
|
}
|
|
@@ -41,7 +41,7 @@ export interface PromptPacket {
|
|
|
41
41
|
budgetEnvelope: {
|
|
42
42
|
remainingBudgetUsd: number;
|
|
43
43
|
remainingIterations: number;
|
|
44
|
-
remainingTokens
|
|
44
|
+
remainingTokens?: number;
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
@@ -41,7 +41,9 @@ export function compilePromptPacket(request) {
|
|
|
41
41
|
budgetEnvelope: {
|
|
42
42
|
remainingBudgetUsd: request.context.remainingBudgetUsd,
|
|
43
43
|
remainingIterations: request.context.remainingIterations,
|
|
44
|
-
|
|
44
|
+
...(request.context.remainingTokens !== undefined
|
|
45
|
+
? { remainingTokens: request.context.remainingTokens }
|
|
46
|
+
: {})
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -26,7 +26,10 @@ export function createDefaultExitPolicy(budget, overrides = {}) {
|
|
|
26
26
|
schemaVersion: "exit-policy/1",
|
|
27
27
|
goal: { verifierRequired: true, minimumScore: 1 },
|
|
28
28
|
turns: { max: budget.maxIterations },
|
|
29
|
-
budget: {
|
|
29
|
+
budget: {
|
|
30
|
+
maxUsd: budget.maxUsd,
|
|
31
|
+
...(budget.maxTokens !== undefined ? { maxTokens: budget.maxTokens } : {})
|
|
32
|
+
},
|
|
30
33
|
wallClock: { maxElapsedMs: 30 * 60 * 1000 },
|
|
31
34
|
progress: { windowSize: 3, unchangedStateLimit: 3 },
|
|
32
35
|
errors: { maxConsecutive: 3 },
|
|
@@ -49,7 +52,9 @@ export function createDefaultExitPolicy(budget, overrides = {}) {
|
|
|
49
52
|
export function validateExitPolicy(policy) {
|
|
50
53
|
assertFinitePositive("turns.max", policy.turns.max);
|
|
51
54
|
assertFinitePositive("budget.maxUsd", policy.budget.maxUsd);
|
|
52
|
-
|
|
55
|
+
if (policy.budget.maxTokens !== undefined) {
|
|
56
|
+
assertFinitePositive("budget.maxTokens", policy.budget.maxTokens);
|
|
57
|
+
}
|
|
53
58
|
assertFinitePositive("wallClock.maxElapsedMs", policy.wallClock.maxElapsedMs);
|
|
54
59
|
assertFinitePositive("progress.windowSize", policy.progress.windowSize);
|
|
55
60
|
assertFinitePositive("progress.unchangedStateLimit", policy.progress.unchangedStateLimit);
|
|
@@ -117,13 +122,13 @@ export function evaluateExitPolicy(policy, snapshot) {
|
|
|
117
122
|
}
|
|
118
123
|
// 5. Budget cap
|
|
119
124
|
const usdExceeded = snapshot.actualUsd >= policy.budget.maxUsd;
|
|
120
|
-
const tokensExceeded = snapshot.tokensUsed >= policy.budget.maxTokens;
|
|
125
|
+
const tokensExceeded = policy.budget.maxTokens !== undefined && snapshot.tokensUsed >= policy.budget.maxTokens;
|
|
121
126
|
if (usdExceeded || tokensExceeded) {
|
|
122
127
|
push("budget_cap", "Run token or dollar budget reached.", {
|
|
123
128
|
actualUsd: snapshot.actualUsd,
|
|
124
129
|
maxUsd: policy.budget.maxUsd,
|
|
125
130
|
tokensUsed: snapshot.tokensUsed,
|
|
126
|
-
maxTokens: policy.budget.maxTokens,
|
|
131
|
+
...(policy.budget.maxTokens !== undefined ? { maxTokens: policy.budget.maxTokens } : {}),
|
|
127
132
|
usdExceeded,
|
|
128
133
|
tokensExceeded
|
|
129
134
|
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
2
3
|
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
3
4
|
import { homedir } from "node:os";
|
|
4
5
|
import { extname, join, relative } from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
5
8
|
const TEXT_EXTENSIONS = new Set([
|
|
6
9
|
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs",
|
|
7
10
|
".json", ".md", ".yaml", ".yml", ".py", ".go",
|
|
@@ -42,21 +45,42 @@ export async function loadOrBuildRepoGroundingIndex(repoRoot) {
|
|
|
42
45
|
export async function buildRepoGroundingIndex(repoRoot, repositoryFingerprint) {
|
|
43
46
|
const files = [];
|
|
44
47
|
const discovered = await walk(repoRoot, repoRoot, files, { count: 0 });
|
|
48
|
+
const trackedPaths = await loadTrackedPaths(repoRoot);
|
|
45
49
|
return {
|
|
46
50
|
schemaVersion: "martin.grounding.v1",
|
|
47
51
|
repoRoot,
|
|
48
52
|
createdAt: new Date().toISOString(),
|
|
49
53
|
fileCount: discovered.count,
|
|
50
54
|
files,
|
|
55
|
+
...(trackedPaths.length > 0 ? { trackedPaths } : {}),
|
|
51
56
|
repositoryFingerprint: repositoryFingerprint ?? await computeRepositoryFingerprint(repoRoot)
|
|
52
57
|
};
|
|
53
58
|
}
|
|
54
59
|
async function computeRepositoryFingerprint(repoRoot) {
|
|
55
60
|
const entries = [];
|
|
56
61
|
await collectFingerprintEntries(repoRoot, repoRoot, entries, { count: 0 });
|
|
62
|
+
for (const trackedPath of await loadTrackedPaths(repoRoot)) {
|
|
63
|
+
entries.push(`tracked:${trackedPath}`);
|
|
64
|
+
}
|
|
57
65
|
entries.sort();
|
|
58
66
|
return createHash("sha256").update(entries.join("\n")).digest("hex");
|
|
59
67
|
}
|
|
68
|
+
async function loadTrackedPaths(repoRoot) {
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = await execFileAsync("git", ["-C", repoRoot, "ls-files", "-z"], {
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
windowsHide: true
|
|
73
|
+
});
|
|
74
|
+
return stdout
|
|
75
|
+
.split("\0")
|
|
76
|
+
.map((filePath) => filePath.trim().replace(/\\/g, "/"))
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
.sort();
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
60
84
|
async function collectFingerprintEntries(repoRoot, currentDir, entries, state) {
|
|
61
85
|
if (state.count >= MAX_FILES)
|
|
62
86
|
return;
|
|
@@ -193,7 +217,10 @@ function tokenize(input) {
|
|
|
193
217
|
export function scanPatchForGroundingViolations(diff, index, options = {}) {
|
|
194
218
|
const violations = [];
|
|
195
219
|
const resolvedFiles = [];
|
|
196
|
-
const indexedPaths = new Set(
|
|
220
|
+
const indexedPaths = new Set([
|
|
221
|
+
...index.files.map((file) => file.path),
|
|
222
|
+
...(index.trackedPaths ?? [])
|
|
223
|
+
]);
|
|
197
224
|
const indexedSymbols = new Set(index.files.flatMap((file) => file.symbols.map((symbol) => symbol.toLowerCase())));
|
|
198
225
|
const referencedFiles = new Set();
|
|
199
226
|
const addedFiles = new Set();
|
|
@@ -75,7 +75,7 @@ export interface MartinAdapterRequest {
|
|
|
75
75
|
focus: string;
|
|
76
76
|
remainingBudgetUsd: number;
|
|
77
77
|
remainingIterations: number;
|
|
78
|
-
remainingTokens
|
|
78
|
+
remainingTokens?: number;
|
|
79
79
|
};
|
|
80
80
|
previousAttempts: LoopAttempt[];
|
|
81
81
|
/** Abort signal propagated from the harness — adapters should honour it. */
|
|
@@ -165,7 +165,7 @@ export interface DistilledContext {
|
|
|
165
165
|
constraints: {
|
|
166
166
|
remainingBudgetUsd: number;
|
|
167
167
|
remainingIterations: number;
|
|
168
|
-
remainingTokens
|
|
168
|
+
remainingTokens?: number;
|
|
169
169
|
};
|
|
170
170
|
}
|
|
171
171
|
export interface AttemptPolicyDecision {
|
|
@@ -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";
|
|
@@ -152,7 +153,9 @@ export function distillContext(loop, options = {}) {
|
|
|
152
153
|
constraints: {
|
|
153
154
|
remainingBudgetUsd: roundUsd(loop.budget.maxUsd - loop.cost.actualUsd),
|
|
154
155
|
remainingIterations: Math.max(loop.budget.maxIterations - loop.attempts.length, 0),
|
|
155
|
-
|
|
156
|
+
...(loop.budget.maxTokens !== undefined
|
|
157
|
+
? { remainingTokens: Math.max(loop.budget.maxTokens - loop.cost.tokensIn - loop.cost.tokensOut, 0) }
|
|
158
|
+
: {})
|
|
156
159
|
}
|
|
157
160
|
};
|
|
158
161
|
}
|
|
@@ -1133,7 +1136,7 @@ export async function runMartin(input) {
|
|
|
1133
1136
|
// VERIFY: Run grounding scan on patch diff if available
|
|
1134
1137
|
// Uses the task's repoRoot to build/load the grounding index, then scans any diff
|
|
1135
1138
|
let groundingScanResult;
|
|
1136
|
-
const patchDiff = buildPatchDiff(result, changedFiles);
|
|
1139
|
+
const patchDiff = buildPatchDiff(result, changedFiles, input.task.repoRoot);
|
|
1137
1140
|
if (patchDiff && input.task.repoRoot) {
|
|
1138
1141
|
try {
|
|
1139
1142
|
const groundingIndex = await loadOrBuildRepoGroundingIndex(input.task.repoRoot);
|
|
@@ -1566,21 +1569,35 @@ function resolveChangedFiles(result, repoRoot, rollbackBoundary) {
|
|
|
1566
1569
|
}
|
|
1567
1570
|
return listAttemptChangedFilesSinceBoundary({ repoRoot, boundary: rollbackBoundary });
|
|
1568
1571
|
}
|
|
1569
|
-
function buildPatchDiff(result, changedFiles) {
|
|
1572
|
+
function buildPatchDiff(result, changedFiles, repoRoot) {
|
|
1570
1573
|
// Use structured diff stats to build a minimal diff header if no raw diff is available
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
return
|
|
1574
|
-
.map((file) =>
|
|
1575
|
-
.join("\n");
|
|
1576
|
-
}
|
|
1577
|
-
if (changedFiles.length > 0) {
|
|
1578
|
-
return changedFiles
|
|
1579
|
-
.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))
|
|
1580
1578
|
.join("\n");
|
|
1581
1579
|
}
|
|
1582
1580
|
return undefined;
|
|
1583
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
|
+
}
|
|
1584
1601
|
function createBudgetSettlement(input) {
|
|
1585
1602
|
const totalActualUsd = getUsageUsd(input.usage);
|
|
1586
1603
|
return {
|
|
@@ -5,7 +5,7 @@ export function compileExecutionPolicy(input) {
|
|
|
5
5
|
maxUsd: resolveBudgetField("maxUsd", input, provenance),
|
|
6
6
|
softLimitUsd: resolveBudgetField("softLimitUsd", input, provenance),
|
|
7
7
|
maxIterations: resolveBudgetField("maxIterations", input, provenance),
|
|
8
|
-
|
|
8
|
+
...resolveOptionalTokenBudget(input, provenance)
|
|
9
9
|
};
|
|
10
10
|
if (budget.softLimitUsd >= budget.maxUsd) {
|
|
11
11
|
budget.softLimitUsd = Math.round(budget.maxUsd * 0.75 * 100) / 100;
|
|
@@ -103,6 +103,22 @@ export function compileExecutionPolicy(input) {
|
|
|
103
103
|
provenance
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
|
+
function resolveOptionalTokenBudget(input, provenance) {
|
|
107
|
+
if (input.request.budgetOverrides?.maxTokens && input.request.budget.maxTokens !== undefined) {
|
|
108
|
+
provenance.push({ field: "budget.maxTokens", source: "request", value: String(input.request.budget.maxTokens) });
|
|
109
|
+
return { maxTokens: input.request.budget.maxTokens };
|
|
110
|
+
}
|
|
111
|
+
const configValue = input.config?.budget?.maxTokens;
|
|
112
|
+
if (typeof configValue === "number" && Number.isFinite(configValue)) {
|
|
113
|
+
provenance.push({ field: "budget.maxTokens", source: "config", value: String(configValue) });
|
|
114
|
+
return { maxTokens: configValue };
|
|
115
|
+
}
|
|
116
|
+
if (input.defaults.budget.maxTokens !== undefined) {
|
|
117
|
+
provenance.push({ field: "budget.maxTokens", source: "default", value: String(input.defaults.budget.maxTokens) });
|
|
118
|
+
return { maxTokens: input.defaults.budget.maxTokens };
|
|
119
|
+
}
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
106
122
|
function resolveBudgetField(field, input, provenance) {
|
|
107
123
|
if (input.request.budgetOverrides?.[field]) {
|
|
108
124
|
const value = input.request.budget[field];
|
|
@@ -12,7 +12,7 @@ export interface CostGovernorState {
|
|
|
12
12
|
shouldStop: boolean;
|
|
13
13
|
remainingBudgetUsd: number;
|
|
14
14
|
remainingIterations: number;
|
|
15
|
-
remainingTokens
|
|
15
|
+
remainingTokens?: number;
|
|
16
16
|
recommendedIntervention?: InterventionType;
|
|
17
17
|
}
|
|
18
18
|
export interface ExitDecision {
|
|
@@ -165,16 +165,18 @@ export function classifyFailure(input) {
|
|
|
165
165
|
export function evaluateCostGovernor(input) {
|
|
166
166
|
const remainingBudgetUsd = roundUsd(input.budget.maxUsd - input.cost.actualUsd);
|
|
167
167
|
const remainingIterations = Math.max(input.budget.maxIterations - input.attemptsUsed, 0);
|
|
168
|
-
const remainingTokens =
|
|
168
|
+
const remainingTokens = input.budget.maxTokens === undefined
|
|
169
|
+
? undefined
|
|
170
|
+
: Math.max(input.budget.maxTokens - input.cost.tokensIn - input.cost.tokensOut, 0);
|
|
169
171
|
if (input.cost.actualUsd >= input.budget.maxUsd ||
|
|
170
172
|
input.attemptsUsed >= input.budget.maxIterations ||
|
|
171
|
-
remainingTokens <= 0) {
|
|
173
|
+
(remainingTokens !== undefined && remainingTokens <= 0)) {
|
|
172
174
|
return {
|
|
173
175
|
pressure: "hard_limit",
|
|
174
176
|
shouldStop: true,
|
|
175
177
|
remainingBudgetUsd,
|
|
176
178
|
remainingIterations,
|
|
177
|
-
remainingTokens,
|
|
179
|
+
...(remainingTokens !== undefined ? { remainingTokens } : {}),
|
|
178
180
|
recommendedIntervention: "stop_loop"
|
|
179
181
|
};
|
|
180
182
|
}
|
|
@@ -184,7 +186,7 @@ export function evaluateCostGovernor(input) {
|
|
|
184
186
|
shouldStop: false,
|
|
185
187
|
remainingBudgetUsd,
|
|
186
188
|
remainingIterations,
|
|
187
|
-
remainingTokens,
|
|
189
|
+
...(remainingTokens !== undefined ? { remainingTokens } : {}),
|
|
188
190
|
recommendedIntervention: "compress_context"
|
|
189
191
|
};
|
|
190
192
|
}
|
|
@@ -193,7 +195,7 @@ export function evaluateCostGovernor(input) {
|
|
|
193
195
|
shouldStop: false,
|
|
194
196
|
remainingBudgetUsd,
|
|
195
197
|
remainingIterations,
|
|
196
|
-
remainingTokens
|
|
198
|
+
...(remainingTokens !== undefined ? { remainingTokens } : {})
|
|
197
199
|
};
|
|
198
200
|
}
|
|
199
201
|
export function calculateCostPerOutcome(input) {
|
|
@@ -10,10 +10,6 @@ export function canAnimate(env = {}) {
|
|
|
10
10
|
if (mode !== "human") {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
|
-
const noColor = env.noColor ?? Object.prototype.hasOwnProperty.call(process.env, "NO_COLOR");
|
|
14
|
-
if (noColor) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
13
|
const stdoutTty = env.stdoutIsTty ?? process.stdout.isTTY === true;
|
|
18
14
|
if (!stdoutTty) {
|
|
19
15
|
return false;
|
package/dist/workflow-state.js
CHANGED
|
@@ -145,7 +145,7 @@ function hashBudget(budget) {
|
|
|
145
145
|
maxUsd: Number(budget.maxUsd.toFixed(4)),
|
|
146
146
|
softLimitUsd: Number(budget.softLimitUsd.toFixed(4)),
|
|
147
147
|
maxIterations: budget.maxIterations,
|
|
148
|
-
maxTokens: budget.maxTokens
|
|
148
|
+
...(budget.maxTokens !== undefined ? { maxTokens: budget.maxTokens } : {})
|
|
149
149
|
};
|
|
150
150
|
return createHash("sha256").update(JSON.stringify(normalized)).digest("hex").slice(0, 12);
|
|
151
151
|
}
|
package/package.json
CHANGED
|
@@ -1,81 +1,95 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@martinloop/mcp",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"mcpName": "io.github.Keesan12/martin-loop",
|
|
5
|
-
"private": false,
|
|
6
|
-
"type": "module",
|
|
7
|
-
"description": "MCP server for AI coding agents
|
|
8
|
-
"license": "Apache-2.0",
|
|
9
|
-
"author": "MartinLoop contributors",
|
|
10
|
-
"homepage": "https://martinloop.com/",
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/Keesan12/martin-loop.git",
|
|
14
|
-
"directory": "packages/mcp"
|
|
15
|
-
},
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/Keesan12/martin-loop/issues"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"mcp",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"dist"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
}
|
|
81
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@martinloop/mcp",
|
|
3
|
+
"version": "0.5.8",
|
|
4
|
+
"mcpName": "io.github.Keesan12/martin-loop",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"description": "MCP server for AI coding agents \u00e2\u20ac\u201d verifier gates, stop limits, and Verified Handoffs.",
|
|
8
|
+
"license": "Apache-2.0",
|
|
9
|
+
"author": "MartinLoop contributors",
|
|
10
|
+
"homepage": "https://martinloop.com/",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/Keesan12/martin-loop.git",
|
|
14
|
+
"directory": "packages/mcp"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/Keesan12/martin-loop/issues"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"mcp-server",
|
|
22
|
+
"model-context-protocol",
|
|
23
|
+
"coding-agent",
|
|
24
|
+
"coding-agents",
|
|
25
|
+
"ai-coding-agent",
|
|
26
|
+
"ai-coding-agents",
|
|
27
|
+
"autonomous-coding",
|
|
28
|
+
"claude-code",
|
|
29
|
+
"codex",
|
|
30
|
+
"gemini-cli",
|
|
31
|
+
"cursor",
|
|
32
|
+
"vscode",
|
|
33
|
+
"agent-runtime",
|
|
34
|
+
"agent-verification",
|
|
35
|
+
"developer-tools",
|
|
36
|
+
"software-engineering",
|
|
37
|
+
"martin-loop"
|
|
38
|
+
],
|
|
39
|
+
"bin": {
|
|
40
|
+
"mcp": "./dist/server.js",
|
|
41
|
+
"martin-loop-mcp": "./dist/server.js"
|
|
42
|
+
},
|
|
43
|
+
"exports": {
|
|
44
|
+
".": {
|
|
45
|
+
"types": "./dist/server.d.ts",
|
|
46
|
+
"import": "./dist/server.js",
|
|
47
|
+
"default": "./dist/server.js"
|
|
48
|
+
},
|
|
49
|
+
"./server.json": "./server.json",
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist",
|
|
54
|
+
"README.md",
|
|
55
|
+
"server.json"
|
|
56
|
+
],
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=20.0.0"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "node ./scripts/build-package.mjs",
|
|
65
|
+
"version:sync": "node ./scripts/version-sync.mjs",
|
|
66
|
+
"version:check": "node ./scripts/verify-version-consistency.mjs",
|
|
67
|
+
"prepack": "pnpm build",
|
|
68
|
+
"smoke:pack": "node ./scripts/smoke-package.mjs",
|
|
69
|
+
"smoke:published": "node ./scripts/smoke-published-package.mjs",
|
|
70
|
+
"smoke:published:pack": "node ./scripts/smoke-published-package.mjs --package-spec=pack",
|
|
71
|
+
"verify:release": "node --test ../../scripts/tests/publish-mcp-workflow.test.mjs ../../scripts/tests/mcp-publish-reliability.test.mjs ../../scripts/tests/mcp-release-docs.test.mjs",
|
|
72
|
+
"test:baseline": "node ./scripts/run-test-lane.mjs baseline",
|
|
73
|
+
"test": "vitest run --maxWorkers=1",
|
|
74
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
75
|
+
"start": "node dist/server.js",
|
|
76
|
+
"inspect:live": "node ./scripts/inspect-live.mjs",
|
|
77
|
+
"mcpb:build": "node ./mcpb/build-mcpb.mjs",
|
|
78
|
+
"mcpb:validate": "mcpb validate ./dist-mcpb/martinloop",
|
|
79
|
+
"mcpb:smoke": "node ./mcpb/smoke-mcpb.mjs"
|
|
80
|
+
},
|
|
81
|
+
"dependencies": {
|
|
82
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
83
|
+
"@open-policy-agent/opa-wasm": "^1.10.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",
|
|
88
|
+
"ts-morph": "^21.0.0"
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"@anthropic-ai/mcpb": "2.1.2",
|
|
92
|
+
"@martin/contracts": "workspace:*",
|
|
93
|
+
"@martin/presentation": "workspace:*"
|
|
94
|
+
}
|
|
95
|
+
}
|
package/server.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
-
"name": "io.github.Keesan12/martin-loop",
|
|
4
|
-
"title": "Martin Loop",
|
|
5
|
-
"description": "
|
|
6
|
-
"repository": {
|
|
7
|
-
"url": "https://github.com/Keesan12/martin-loop",
|
|
8
|
-
"source": "github"
|
|
9
|
-
},
|
|
10
|
-
"version": "0.5.
|
|
11
|
-
"packages": [
|
|
12
|
-
{
|
|
13
|
-
"registryType": "npm",
|
|
14
|
-
"identifier": "@martinloop/mcp",
|
|
15
|
-
"version": "0.5.
|
|
16
|
-
"transport": {
|
|
17
|
-
"type": "stdio"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.Keesan12/martin-loop",
|
|
4
|
+
"title": "Martin Loop",
|
|
5
|
+
"description": "One system around coding agents to run, verify, recover, and hand off software work.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"url": "https://github.com/Keesan12/martin-loop",
|
|
8
|
+
"source": "github"
|
|
9
|
+
},
|
|
10
|
+
"version": "0.5.8",
|
|
11
|
+
"packages": [
|
|
12
|
+
{
|
|
13
|
+
"registryType": "npm",
|
|
14
|
+
"identifier": "@martinloop/mcp",
|
|
15
|
+
"version": "0.5.8",
|
|
16
|
+
"transport": {
|
|
17
|
+
"type": "stdio"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|