Skip to content

Commit 58ceadb

Browse files
committed
Validate repo status responses against their public schema
1 parent 6d7ee62 commit 58ceadb

3 files changed

Lines changed: 40 additions & 2 deletions

File tree

script.ps1

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
$sdlScriptSucceeded = $?
3+
$sdlNativeExitCode = Get-Variable -Name LASTEXITCODE -ValueOnly -ErrorAction SilentlyContinue
4+
if (-not $sdlScriptSucceeded) {
5+
if ($null -ne $sdlNativeExitCode -and $sdlNativeExitCode -ne 0) { exit $sdlNativeExitCode }
6+
exit 1
7+
}

src/mcp/tools/repo.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
RepoUnregisterResponse,
99
type RepoStatusRequest,
1010
RepoStatusResponse,
11+
RepoStatusResponseSchema,
1112
type IndexRefreshRequest,
1213
IndexRefreshResponse,
1314
type RepoOverviewRequest,
@@ -912,7 +913,7 @@ export async function handleRepoStatus(
912913
}
913914
}
914915

915-
return {
916+
return RepoStatusResponseSchema.parse({
916917
repoId,
917918
...(includeTelemetry ? { rootPath: repo.rootPath } : {}),
918919
rootAvailability,
@@ -983,7 +984,7 @@ export async function handleRepoStatus(
983984
: {}),
984985
...(memories !== undefined ? { memories } : {}),
985986
derivedState: statusDerivedState,
986-
};
987+
});
987988
};
988989

989990
if (isTracingEnabled()) {

tests/unit/repo-status-health.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { after, before, describe, it } from "node:test";
1414
import {
1515
RepoStatusRequestSchema,
1616
RepoStatusResponseSchema,
17+
withProjectionSuccessOutputSchema,
1718
} from "../../dist/mcp/tools.js";
1819

1920
describe("repo status health fields", () => {
@@ -554,6 +555,35 @@ describe("repo status root availability", { concurrency: 1 }, () => {
554555
}
555556
});
556557

558+
it("keeps active watcher internals out of full public status", async () => {
559+
const { handleRepoStatus } = await import("../../dist/mcp/tools/repo.js");
560+
const watcher = await import("../../dist/indexer/watcher.js");
561+
watcher._setWatcherHealthForTesting("available", {
562+
provider: "watchman",
563+
filesWatched: 7,
564+
watchmanWatchRoot: availableRoot,
565+
});
566+
567+
try {
568+
const status = await handleRepoStatus({
569+
repoId: "available",
570+
detail: "full",
571+
});
572+
573+
assert.ok(status.watcherHealth);
574+
assert.equal("filesWatched" in status.watcherHealth, false);
575+
assert.equal("watchmanWatchRoot" in status.watcherHealth, false);
576+
assert.doesNotThrow(() =>
577+
withProjectionSuccessOutputSchema(
578+
"repo.status",
579+
RepoStatusResponseSchema,
580+
).parse(status),
581+
);
582+
} finally {
583+
watcher._clearWatcherHealthForTesting("available");
584+
}
585+
});
586+
557587
it("keeps semantic-only staleness non-blocking", async () => {
558588
const { handleRepoStatus } = await import("../../dist/mcp/tools/repo.js");
559589
const status = await handleRepoStatus({

0 commit comments

Comments
 (0)