Skip to content

Commit d48651a

Browse files
committed
chore(mcp): delete orphaned changelog config and dead schema literals, lock the package against unused locals
cliff.mcp.toml was referenced by nothing -- the MCP changelog is generated by scripts/generate-mcp-changelog.ts via cliff.toml, and check-changelog.ts --mcp still passes without it. It carried a stale commit_preprocessors rewrite rule that read as live config. Five hand-written output-schema literals (repoContext, preflight, decisionPack, localStatus, agentPlan) sat unreferenced since #291. Enabling noUnusedLocals / noUnusedParameters is what keeps the next one from accumulating; it immediately surfaced two more dead items, both removed: isValidationStatus, superseded by isValidationStatusLike with callers reaching normalizeValidationStatus directly, and an unused node:path join import in local-branch.ts. Comment fixes for counts that had already rotted: the registration wrapper said 37 call sites, and the tools-search example cited get_subnet_stake_quote, a tool that does not exist in this repo. Both now describe the surface without pinning a number that goes stale. Refs #9516
1 parent adc0936 commit d48651a

4 files changed

Lines changed: 16 additions & 122 deletions

File tree

cliff.mcp.toml

Lines changed: 0 additions & 48 deletions
This file was deleted.

packages/loopover-mcp/bin/loopover-mcp.ts

Lines changed: 10 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,8 +1732,8 @@ export const server = new McpServer({
17321732
version: packageVersion,
17331733
});
17341734

1735-
// #4777: register a stdio tool under its loopover_ name. Thin wrapper kept so all 37 call sites
1736-
// stay uniform with the rest of this file's registration style.
1735+
// #4777: register a stdio tool under its loopover_ name. Every tool registers through this wrapper,
1736+
// so registration style stays uniform across the file.
17371737
// Telemetry await/flush lives in wrapStdioToolHandler (lib/telemetry.ts, unit-tested) — #6238 / #8690.
17381738
// Reads telemetryState() HERE on purpose: registerStdioTool's second parameter is the TOOL's config and
17391739
// shadows the module-level `config`, so a read inside a nested function would silently see the wrong object.
@@ -2954,68 +2954,9 @@ registerStdioTool(
29542954
async (input: any) => toolResult("LoopOver base-agent public-safe PR packet.", await agentPreparePrPacket(await withClientWorkspaceRoots(input))),
29552955
);
29562956

2957-
// ── Output schemas for structured tool responses (#291) ──────────────────────
2958-
2959-
const repoContextOutputSchema = {
2960-
type: "object",
2961-
properties: {
2962-
repoFullName: { type: "string" },
2963-
lane: { type: "string" },
2964-
primaryLanguage: { type: ["string", "null"] },
2965-
openIssueCount: { type: "number" },
2966-
openPrCount: { type: "number" },
2967-
},
2968-
additionalProperties: true,
2969-
};
2970-
2971-
const preflightOutputSchema = {
2972-
type: "object",
2973-
properties: {
2974-
status: { type: "string", enum: ["pass", "warn", "fail", "unknown"] },
2975-
signals: { type: "array", items: { type: "object" } },
2976-
summary: { type: "string" },
2977-
},
2978-
additionalProperties: true,
2979-
};
2980-
2981-
const decisionPackOutputSchema = {
2982-
type: "object",
2983-
properties: {
2984-
login: { type: "string" },
2985-
decisions: { type: "array", items: { type: "object" } },
2986-
cachedAt: { type: ["string", "null"] },
2987-
},
2988-
additionalProperties: true,
2989-
};
2990-
2991-
const localStatusOutputSchema = {
2992-
type: "object",
2993-
properties: {
2994-
apiUrl: { type: "string" },
2995-
package: { type: "object", properties: { name: { type: "string" }, version: { type: "string" } }, additionalProperties: true },
2996-
hasToken: { type: "boolean" },
2997-
profile: { type: "object", additionalProperties: true },
2998-
authLogin: { type: ["string", "null"] },
2999-
sessionExpiresAt: { type: ["string", "null"] },
3000-
sourceUploadDefault: { type: "boolean" },
3001-
sourceUploadSupported: { type: "boolean" },
3002-
git: { type: "object", additionalProperties: true },
3003-
},
3004-
additionalProperties: true,
3005-
};
3006-
3007-
const agentPlanOutputSchema = {
3008-
type: "object",
3009-
properties: {
3010-
login: { type: "string" },
3011-
actions: { type: "array", items: { type: "object" } },
3012-
topAction: { type: ["object", "null"] },
3013-
},
3014-
additionalProperties: true,
3015-
};
3016-
3017-
// Attach outputSchema to key tools via registerTool with zod output schemas.
3018-
// All other tools continue to return unschematized text+structured content.
2957+
// Only this tool declares an outputSchema today; every other tool returns text + unschematized
2958+
// structured content. #9518 finishes the job by registering all of them from @loopover/contract,
2959+
// where each tool's output schema lives beside its input schema and is enforced by validate:mcp.
30192960

30202961
registerStdioTool(
30212962
"loopover_local_status_structured",
@@ -5192,10 +5133,11 @@ function toolsCommand(args: any) {
51925133
});
51935134
}
51945135

5195-
// `tools search <query>` — fuzzy discovery across the ~150-tool combined surface (#6300). Matches the
5196-
// query against each registered tool's name AND description (not name-only), so "stake" surfaces
5197-
// get_subnet_stake_quote even though "stake" is only in its description. Reuses this CLI's existing
5198-
// levenshteinDistance for typo tolerance rather than pulling in a fuzzy-match dependency.
5136+
// `tools search <query>` — fuzzy discovery across the whole registered tool surface (#6300). Matches
5137+
// the query against each registered tool's name AND description (not name-only), so "duplicate"
5138+
// surfaces loopover_check_before_start even though "duplicate" is only in its description. Reuses
5139+
// this CLI's existing levenshteinDistance for typo tolerance rather than pulling in a fuzzy-match
5140+
// dependency.
51995141
function toolsSearchCommand(args: any) {
52005142
const options = parseOptions(args);
52015143
const query = args.find((arg: any) => !arg.startsWith("--"));
@@ -6427,10 +6369,6 @@ function optionalNumber(value: any) {
64276369
return Number.isFinite(parsed) ? parsed : undefined;
64286370
}
64296371

6430-
function isValidationStatus(value: any) {
6431-
return Boolean(normalizeValidationStatus(value));
6432-
}
6433-
64346372
function normalizeValidationStatus(value: any) {
64356373
const text = String(value ?? "").trim().toLowerCase().replace(/[-\s]+/g, "_");
64366374
if (["passed", "pass", "success", "ok", "exit_0", "0"].includes(text)) return "passed";

packages/loopover-mcp/lib/local-branch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { execFileSync } from "node:child_process";
22
import { realpathSync } from "node:fs";
3-
import { isAbsolute, join, relative, resolve } from "node:path";
3+
import { isAbsolute, relative, resolve } from "node:path";
44
import { fileURLToPath } from "node:url";
55
import { assertScenarioLocalBranchInputSafe } from "@loopover/engine";
66
import { isCodeFile, isTestPath as isTestFile } from "@loopover/engine/signals/test-evidence";

packages/loopover-mcp/tsconfig.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
// Without this, the inherited root value ("./.tsbuildinfo") resolves relative to the ROOT config's
2626
// location, not this one -- both packages would then read/write the exact same cache file at the
2727
// repo root and corrupt each other's incremental state.
28-
"tsBuildInfoFile": "./.tsbuildinfo"
28+
"tsBuildInfoFile": "./.tsbuildinfo",
29+
// #9516: five hand-written output-schema literals sat here unreferenced because nothing rejected
30+
// dead locals. tsc is the gate now, so the next one fails the build instead of accumulating.
31+
"noUnusedLocals": true,
32+
"noUnusedParameters": true
2933
},
3034
// Every bin/lib runtime module is real TypeScript (#7291, phased across #7328/#7329/#7330): tsc owns
3135
// the dist/ .js emit, which is gitignored -- contributors and tests never touch it. The glob stays

0 commit comments

Comments
 (0)