Files
dbmole-mcp/AGENTS.md
T
admin baecc24211 docs(agents): monorepo layout + inputSchema gotcha
Add an Architecture section: the three-package workspace, core being
private and bundled, and the root-level workspace commands. New sessions
had no map of the repo.

Rewrite the MCP SDK note to warn that the advertised inputSchema must
expose .shape: a .superRefine/.refine (ZodEffects) serializes to an empty
{ properties: {} }. Document the fullSchema/parseSchema split so the
add_connection bug fixed this cycle cannot recur. Fix the stale
test/unit/helpers.ts path to its per-package location.
2026-06-16 23:34:40 +05:00

3.5 KiB

Agent rules for dbmole-mcp

Applies to every change in this repo, by any agent or human.

Architecture

  • npm workspace monorepo, three packages under packages/:
    • @dbmole/core — shared Manager/registry/SSH-tunnel/MCP plumbing. private, never published; each leaf bundles it via tsup (noExternal).
    • dbmole-mcp — PostgreSQL + MySQL server
    • esmole-mcp — Elasticsearch server
  • Backends spread baseConnectionShape, add engine fields + type, call .strict().
  • Run workspace commands from the root, they fan out across packages: npm run typecheck / build / test:unit / test:int / coverage / lint.

Code style

  • 4-space indentation

  • No semicolons where possible (semicolons: asNeeded)

  • Single quotes where possible

  • Functions as const fn = () => {} where possible

  • No trailing commas before a closing }, ], ) (trailingCommas: none)

  • Always use braces for if/else/loop bodies — never single-line bodies like if (x) return y. Write:

    if (typeof value === 'bigint') {
        return value.toString()
    }
    

    (Biome useBlockStatements enforces this)

  • No template-literal interpolation for building strings: use concatenation — '[' + engine + '] ' + message, NOT `[${engine}] ${message}`. Plain multi-line template literals WITHOUT ${} (e.g. SQL text blocks) are fine.

  • Biome enforces all of the above — run npm run lint:fix before every commit

  • Line width 100

TypeScript discipline

  • No as unknown as X double-casts anywhere
  • No @ts-ignore / @ts-expect-error / any in src/
  • In tests prefer typed fakes (see packages/dbmole-mcp/test/unit/helpers.ts); a single as cast is a last resort, only when there is genuinely no typed way
  • Types must line up by construction — if they don't, fix the design, not the cast

Testing

  • TDD: write the failing test first, watch it fail, implement, watch it pass
  • Full coverage: unit tests (mocked IO) + integration tests via testcontainers
  • Coverage thresholds ≥90% lines/functions on the unit project — never lower them
  • npm run test:unit (fast), npm run test:int (needs Docker)

Commits

  • Conventional Commits: <type>(<scope>): <imperative summary>
  • Subject ≤50 chars (hard cap 72), no trailing period
  • Body only when the why is not obvious from the diff; wrap at 72
  • NO AI attribution: no Co-Authored-By: Claude ..., no "Generated with ..."
  • No emoji

Project constraints

  • stdout is the MCP protocol channel — diagnostics go to console.error ONLY, never console.log
  • ESM with NodeNext resolution: relative imports need the .js extension
  • MCP SDK v1 (@modelcontextprotocol/sdk): registerTool accepts a ZodRawShape (plain object of zod fields) OR a full Zod object (verified on 1.29: AnySchema, .strict() honored at runtime). The advertised inputSchema MUST expose .shape — pass a ZodRawShape or a plain z.object/.strict(), NEVER a .superRefine/.refine (ZodEffects has no .shape, so the SDK publishes an empty { properties: {} } and clients see no fields). Keep cross-field refinements on a separate parse schema: registerConnectionTools takes fullSchema (strict object, advertised) plus optional parseSchema (refined, applied in the handler) — see esmole add_connection. Use a strict z.object where unknown-key rejection matters; raw shapes are fine elsewhere. Subpath imports (server/mcp.js, server/stdio.js, client/index.js, inMemory.js, types.js)
  • zod 3 (import * as z from 'zod')