Skip to content

refactor: extract shared CLI utils, types, tool handlers, and constants#139

Merged
rsdouglas merged 3 commits intomainfrom
refactor/reduce-duplication
Mar 16, 2026
Merged

refactor: extract shared CLI utils, types, tool handlers, and constants#139
rsdouglas merged 3 commits intomainfrom
refactor/reduce-duplication

Conversation

@rsdouglas
Copy link
Copy Markdown
Owner

Summary

  • Extract shared CLI utilities into src/cli/cli-utils.tscliError, requireConfig, resolveEnvVar, parseEnvMap, handleCommandError, getErrorMessage removed from 14 command files, replaced with imports. Adds cli-utils.test.ts.
  • Centralize shared types and constants in src/core/types.tsAPIRequest, APIResponse, DenialError/DenialDetails, DEFAULT_TIMEOUT_MS, REDACTED, MIN_SCRUB_LENGTH. Adds SerializedSession to core/sessions.ts.
  • Extract MCP tool handlers into src/core/tool-handlers.ts — 6 handlers (execute, janee_exec, manage_credential, test_service, explain_access, whoami) pulled out of the monolithic createMCPServer switch statement (~600 lines).
  • Deduplicate Authority REST routes — new mountAuthorityRoutes() in authority.ts replaces ~80 lines of copy-pasted Express routes in startMCPServerHTTP.

Net result: -444 lines (874 added, 1,318 removed). All 528 tests pass.

Test plan

  • All 528 existing tests pass (npx vitest run)
  • New cli-utils.test.ts covers extracted utility functions
  • No lint errors introduced

Made with Cursor

Reduces ~1,100 lines of duplicated code across the codebase:

- Extract cliError, requireConfig, resolveEnvVar, parseEnvMap,
  handleCommandError into src/cli/cli-utils.ts (14 consumers updated)
- Centralize APIRequest, APIResponse, DenialError, DenialDetails,
  and shared constants in src/core/types.ts
- Extract 6 MCP tool handlers into src/core/tool-handlers.ts,
  shrinking createMCPServer by ~600 lines
- Add mountAuthorityRoutes() to deduplicate Express route definitions
  between authority.ts and mcp-server.ts
- Replace magic values (30000, '[REDACTED]', 8) with named constants
- Add SerializedSession to core/sessions.ts (replaces 3 duplicates)
- Add cli-utils.test.ts for the new shared module

Made-with: Cursor
The consolidated reloadConfig helper was placed after the try/catch,
putting it outside the scope of the `let currentServices` declaration.

Made-with: Cursor
Copy link
Copy Markdown
Collaborator

@lucamorettibuilds lucamorettibuilds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thorough refactor. The -444 lines are real — this isn't just shuffling code, it's genuine deduplication. A few things I noticed:

🔴 Behavioral change in parseTTL

The original in mcp-server.ts threw on invalid TTL:

if (!match) throw new Error(`Invalid TTL format: ${ttl}`);

The new version in tool-handlers.ts silently falls back:

if (!match) return 3600;

This swallows config errors. If someone typos ttl: "1x" in their config, the old code would surface it immediately; the new code silently defaults to 1 hour. I'd keep the throw — it's a config error that should be loud.

🟡 resolveEnvVar error path changed

The old add.ts version called console.error + process.exit(1) directly, producing:

❌ Environment variable FOO is not set (needed for bar)

The new centralized version throws an Error, which bubbles to handleCommandError, which produces:

❌ Error: Environment variable FOO is not set (needed for bar)

Minor format difference (extra "Error:"), but if anyone's parsing CLI output programmatically, it's a break. Probably fine, but worth noting.

✅ Authority route dedup is clean

The mountAuthorityRoutes extraction is well done. The old code used a global middleware that skipped /v1/health; the new code applies authMiddleware per-route. Functionally identical, cleaner to read.

✅ Re-exports preserve the public API

mcp-server.ts re-exports DenialError, APIRequest, etc. from types.ts — anything importing from mcp-server still works. Good.

ToolHandlerContext interface is the right abstraction

The context bag gives tool handlers everything they need without coupling them to the closure. Makes the handlers independently testable, which was the main win here.

Overall: solid refactor. The parseTTL regression is the only thing I'd flag as must-fix before merge.

Comment thread src/core/tool-handlers.ts
}

function parseTTL(ttl: string): number {
const match = ttl.match(/^(\d+)(s|m|h|d)$/);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This silently defaults to 1h on invalid TTL. The original in mcp-server.ts threw new Error('Invalid TTL format: ${ttl}') — I'd keep that behavior so config typos surface early.

Copy link
Copy Markdown
Owner Author

@rsdouglas rsdouglas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the thorough review. Addressing each point:

🔴 parseTTL regression — fixed in a2e8803

Good catch, this was a genuine bug I introduced. Restored the throw new Error(...) behavior so invalid TTL formats (like "1x") are surfaced immediately instead of silently defaulting to 1 hour.

🟡 resolveEnvVar error path format change

Acknowledged. The old path was console.error("❌ ...") + process.exit(1) inline, the new path throws which lands in handleCommandError producing "❌ Error: ...". The extra "Error:" prefix is a minor cosmetic difference. I think the consistency win (all commands use the same error pipeline) outweighs the format change, and anyone parsing CLI stderr is already in fragile territory. Happy to strip the "Error:" prefix from handleCommandError if you feel strongly about it.

✅ Authority route dedup / re-exports / ToolHandlerContext

Glad these landed well. The re-exports from mcp-server.ts were specifically to avoid breaking downstream imports.

@rsdouglas rsdouglas merged commit 96ba731 into main Mar 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants