run_typecheck
Verify TypeScript code correctness by running type-checking on a specified service path, receiving structured error details for pre-commit validation.
Instructions
Run TypeScript type-checking (tsc --noEmit) for a service. Returns structured errors with file, line, column, and message. Call after writing code to verify correctness before committing.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| servicePath | Yes | Relative path from repo root to the service to typecheck. E.g. "ops/control-panel/server", "SeID/seid-core", "packages/overlay-contracts". |
Implementation Reference
- src/tools/dev-lifecycle.tool.ts:42-52 (registration)Registers the 'run_typecheck' tool on the MCP server with its schema and handler.
server.tool( 'run_typecheck', 'Run TypeScript type-checking (tsc --noEmit) for a service. Returns structured errors with file, line, column, and message. Call after writing code to verify correctness before committing.', RunTypecheckSchema.shape, async (args) => { const result = await manager.runTypecheck(args.servicePath); return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }], }; }, ); - src/tools/dev-lifecycle.tool.ts:5-11 (schema)Zod schema for run_typecheck input, requiring 'servicePath' (string).
export const RunTypecheckSchema = z.object({ servicePath: z .string() .describe( 'Relative path from repo root to the service to typecheck. E.g. "ops/control-panel/server", "SeID/seid-core", "packages/overlay-contracts".', ), }); - Core handler logic: resolves path, finds tsc binary, runs 'tsc --noEmit', parses structured errors from output, returns TypecheckResult.
async function runTypecheck(servicePath: string): Promise<TypecheckResult> { const absPath = resolveSafe(repoDir, servicePath); // Prefer local tsc, fall back to workspace root, then PATH const localTsc = join(absPath, 'node_modules', '.bin', 'tsc'); const rootTsc = join(repoDir, 'node_modules', '.bin', 'tsc'); const tscBin = existsSync(localTsc) ? localTsc : existsSync(rootTsc) ? rootTsc : 'tsc'; const hasTsConfig = existsSync(join(absPath, 'tsconfig.json')); const args = ['--noEmit']; if (hasTsConfig) args.push('-p', 'tsconfig.json'); const result = await proc.run(tscBin, args, { cwd: absPath, timeout: 90_000 }); const combined = (result.stdout + result.stderr).trim(); const errors = parseTscOutput(combined, repoDir); return { success: result.success && errors.length === 0, errorCount: errors.length, errors, raw: combined.slice(0, 5_000), }; } - Helper that parses raw tsc stdout/stderr into structured TypecheckError[] using regex.
const TSC_ERROR_RE = /^(.+?)\((\d+),(\d+)\):\s+error\s+(TS\d+):\s+(.+)$/gm; function parseTscOutput(output: string, repoDir: string): TypecheckError[] { const errors: TypecheckError[] = []; let match: RegExpExecArray | null; TSC_ERROR_RE.lastIndex = 0; while ((match = TSC_ERROR_RE.exec(output)) !== null) { const rawFile = match[1] ?? ''; const line = match[2] ?? '0'; const col = match[3] ?? '0'; const code = match[4] ?? ''; const message = match[5] ?? ''; const file = rawFile.startsWith(repoDir) ? rawFile.slice(repoDir.length + 1) : rawFile.trim(); errors.push({ file, line: parseInt(line, 10), col: parseInt(col, 10), code: code.trim(), message: message.trim(), }); } return errors; } - src/access/ProcessAccess.ts:21-48 (helper)Factory that creates the process runner (execFileAsync) used by runTypecheck to invoke tsc.
export function createProcessAccess(): ProcessAccess { async function run( cmd: string, args: string[], options: { cwd: string; timeout?: number; env?: Record<string, string> }, ): Promise<ProcessResult> { const timeout = options.timeout ?? 120_000; try { const { stdout, stderr } = await execFileAsync(cmd, args, { cwd: options.cwd, timeout, env: { ...process.env, ...(options.env ?? {}) }, maxBuffer: 10 * 1024 * 1024, // 10MB }); return { stdout, stderr, exitCode: 0, success: true }; } catch (err: unknown) { const e = err as { stdout?: string; stderr?: string; code?: number }; return { stdout: e.stdout ?? '', stderr: e.stderr ?? '', exitCode: typeof e.code === 'number' ? e.code : 1, success: false, }; } } return { run }; }