@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.
Files changed (40) hide show
  1. package/README.md +137 -175
  2. package/dist/arcade/resource.js +20 -20
  3. package/dist/package-version.d.ts +1 -1
  4. package/dist/package-version.js +1 -1
  5. package/dist/resources.js +148 -160
  6. package/dist/server.js +192 -43
  7. package/dist/tools/execution-policy.js +1 -1
  8. package/dist/tools/get-status.d.ts +2 -4
  9. package/dist/tools/get-status.js +6 -3
  10. package/dist/tools/preflight.d.ts +1 -1
  11. package/dist/tools/run-loop.d.ts +1 -1
  12. package/dist/tools/run-loop.js +3 -1
  13. package/dist/tools/run-store.js +1 -1
  14. package/dist/tools/tool-support.d.ts +3 -2
  15. package/dist/tools/tool-support.js +9 -2
  16. package/dist/tools/workflow-governance.d.ts +1 -1
  17. package/dist/tools/workflow-governance.js +1 -1
  18. package/dist/vendor/adapters/openai-compatible.js +6 -6
  19. package/dist/vendor/adapters/verifier-only.js +1 -1
  20. package/dist/vendor/contracts/execution-policy.js +1 -1
  21. package/dist/vendor/contracts/exits.d.ts +1 -1
  22. package/dist/vendor/contracts/governance.d.ts +1 -1
  23. package/dist/vendor/contracts/index.d.ts +1 -1
  24. package/dist/vendor/contracts/index.js +1 -2
  25. package/dist/vendor/contracts/operator.d.ts +1 -1
  26. package/dist/vendor/contracts/operator.js +1 -2
  27. package/dist/vendor/core/compiler.d.ts +2 -2
  28. package/dist/vendor/core/compiler.js +3 -1
  29. package/dist/vendor/core/exits.js +9 -4
  30. package/dist/vendor/core/grounding.d.ts +1 -0
  31. package/dist/vendor/core/grounding.js +28 -1
  32. package/dist/vendor/core/index.d.ts +2 -2
  33. package/dist/vendor/core/index.js +29 -12
  34. package/dist/vendor/core/policy-compiler.js +17 -1
  35. package/dist/vendor/core/policy.d.ts +1 -1
  36. package/dist/vendor/core/policy.js +7 -5
  37. package/dist/vendor/presentation/motion.js +0 -4
  38. package/dist/workflow-state.js +1 -1
  39. package/package.json +95 -81
  40. 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: number;
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: number;
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
- remainingTokens: request.context.remainingTokens
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: { maxUsd: budget.maxUsd, maxTokens: budget.maxTokens },
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
- assertFinitePositive("budget.maxTokens", policy.budget.maxTokens);
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
  });
@@ -9,6 +9,7 @@ export interface RepoGroundingIndex {
9
9
  createdAt: string;
10
10
  fileCount: number;
11
11
  files: RepoGroundingFile[];
12
+ trackedPaths?: string[];
12
13
  repositoryFingerprint?: string;
13
14
  }
14
15
  export interface RepoGroundingHit {
@@ -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(index.files.map((file) => file.path));
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: number;
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: number;
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
- remainingTokens: Math.max(loop.budget.maxTokens - loop.cost.tokensIn - loop.cost.tokensOut, 0)
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
- if (result.execution?.changedFiles?.length) {
1572
- // Build a synthetic diff header from changed file list
1573
- return result.execution.changedFiles
1574
- .map((file) => `--- a/${file}\n+++ b/${file}\n@@ -0,0 +1 @@\n+`)
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
- maxTokens: resolveBudgetField("maxTokens", input, provenance)
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: number;
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 = Math.max(input.budget.maxTokens - input.cost.tokensIn - input.cost.tokensOut, 0);
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;
@@ -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.6",
4
- "mcpName": "io.github.Keesan12/martin-loop",
5
- "private": false,
6
- "type": "module",
7
- "description": "MCP server for AI coding agents — 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
- "model-context-protocol",
22
- "martin-loop",
23
- "ai-agent",
24
- "claude",
25
- "codex",
26
- "martin_doctor",
27
- "martin_triage_runs",
28
- "martin_run_dossier",
29
- "mcp-resources",
30
- "mcp-prompts"
31
- ],
32
- "bin": {
33
- "mcp": "./dist/server.js",
34
- "martin-loop-mcp": "./dist/server.js"
35
- },
36
- "exports": {
37
- "./server.json": "./server.json",
38
- "./package.json": "./package.json"
39
- },
40
- "files": [
41
- "dist",
42
- "README.md",
43
- "server.json"
44
- ],
45
- "engines": {
46
- "node": ">=20.0.0"
47
- },
48
- "publishConfig": {
49
- "access": "public"
50
- },
51
- "scripts": {
52
- "build": "node ./scripts/build-package.mjs",
53
- "prepack": "pnpm build",
54
- "smoke:pack": "node ./scripts/smoke-package.mjs",
55
- "smoke:published": "node ./scripts/smoke-published-package.mjs",
56
- "smoke:published:pack": "node ./scripts/smoke-published-package.mjs --package-spec=pack",
57
- "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",
58
- "test:baseline": "node ./scripts/run-test-lane.mjs baseline",
59
- "test": "vitest run --maxWorkers=1",
60
- "lint": "tsc -p tsconfig.json --noEmit",
61
- "start": "node dist/server.js",
62
- "inspect:live": "node ./scripts/inspect-live.mjs",
63
- "mcpb:build": "node ./mcpb/build-mcpb.mjs",
64
- "mcpb:validate": "mcpb validate ./dist-mcpb/martinloop",
65
- "mcpb:smoke": "node ./mcpb/smoke-mcpb.mjs"
66
- },
67
- "dependencies": {
68
- "@modelcontextprotocol/sdk": "^1.29.0",
69
- "@open-policy-agent/opa-wasm": "^1.10.0",
70
- "@opentelemetry/api-logs": "^0.214.0",
71
- "@opentelemetry/exporter-logs-otlp-http": "^0.214.0",
72
- "@opentelemetry/resources": "^2.6.1",
73
- "@opentelemetry/sdk-logs": "^0.214.0",
74
- "ts-morph": "^21.0.0"
75
- },
76
- "devDependencies": {
77
- "@anthropic-ai/mcpb": "2.1.2",
78
- "@martin/contracts": "workspace:*",
79
- "@martin/presentation": "workspace:*"
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": "MCP server for AI coding agents — verifier gates, stop limits, and Verified Handoffs.",
6
- "repository": {
7
- "url": "https://github.com/Keesan12/martin-loop",
8
- "source": "github"
9
- },
10
- "version": "0.5.6",
11
- "packages": [
12
- {
13
- "registryType": "npm",
14
- "identifier": "@martinloop/mcp",
15
- "version": "0.5.6",
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
+ }