Problem
When developing the MCP server locally, it's easy to modify source files in packages/mcp/src/ and forget to rebuild before starting a new Claude Code session. The server runs from packages/mcp/dist/esm/cli.js, so stale compiled output means tool behavior doesn't match the source — and nothing alerts the user.
This caused a real issue in #157 where init_run returned a run ID format ({projectSlug}.{timestamp}) that didn't match the current source code's format ({timestamp} only), because the running server was built from an older version.
Proposed solution
Add a staleness check that warns once per session when the compiled output is older than the source files.
Detection logic
Compare the most recent mtime across src/**/*.ts against the mtime of dist/esm/cli.js. If any source file is newer than the compiled output, the build is stale.
Dev vs production: Resolve src/ relative to the server's own file location (../../src/ from dist/esm/). If src/ doesn't exist (published package), skip silently — the feature activates only in local development.
Warning delivery
Prepend a warning to the first tool call response in each session. Module-level let hasWarned = false persists for the session's lifetime (stdio process = one session).
⚠️ MCP server build is stale — source files are newer than compiled output. Run `pnpm run ws compile` in packages/mcp/ to rebuild.
Implementation approach
packages/mcp/src/staleness.ts (new) — isBuildStale(compiledFileUrl?) with recursive mtime comparison. Optional parameter enables testing without mocking import.meta.url.
packages/mcp/src/server.ts (modify) — wrapHandler() factory applied to all 5 tool registrations. prependWarning() helper mutates the first text content entry.
Key design decisions
- Module-level boolean, not protocol-level session tracking — stdio transport means one process = one session.
- Warning in tool response, not MCP logging — tool responses are guaranteed visible in Claude's context.
- Silent in production — the
src/ directory check means the feature is a no-op from a published package.
- Fail-safe — any staleness detection error returns
false, never blocks tool execution.
Acceptance criteria
Problem
When developing the MCP server locally, it's easy to modify source files in
packages/mcp/src/and forget to rebuild before starting a new Claude Code session. The server runs frompackages/mcp/dist/esm/cli.js, so stale compiled output means tool behavior doesn't match the source — and nothing alerts the user.This caused a real issue in #157 where
init_runreturned a run ID format ({projectSlug}.{timestamp}) that didn't match the current source code's format ({timestamp}only), because the running server was built from an older version.Proposed solution
Add a staleness check that warns once per session when the compiled output is older than the source files.
Detection logic
Compare the most recent mtime across
src/**/*.tsagainst the mtime ofdist/esm/cli.js. If any source file is newer than the compiled output, the build is stale.Dev vs production: Resolve
src/relative to the server's own file location (../../src/fromdist/esm/). Ifsrc/doesn't exist (published package), skip silently — the feature activates only in local development.Warning delivery
Prepend a warning to the first tool call response in each session. Module-level
let hasWarned = falsepersists for the session's lifetime (stdio process = one session).Implementation approach
packages/mcp/src/staleness.ts(new) —isBuildStale(compiledFileUrl?)with recursive mtime comparison. Optional parameter enables testing without mockingimport.meta.url.packages/mcp/src/server.ts(modify) —wrapHandler()factory applied to all 5 tool registrations.prependWarning()helper mutates the first text content entry.Key design decisions
src/directory check means the feature is a no-op from a published package.false, never blocks tool execution.Acceptance criteria
src/directory), no warning appears