4. Coding Rules
File Naming
- File names: Use
kebab-case(e.g.,readme-docs-plugin.ts,case-convert.ts) - Interface/type files: Use
kebab-case - Test files: Use the
*.test.tsconvention, placed next to the file under test (e.g.,src/exchange/parse.test.ts) - Build scripts: Located under
scripts/(readme-docs-plugin.ts)
TypeScript Strong Typing Rules
- No
any: Avoid usingany. Thetool.tshandler type allowsanyfor MCP SDK interface compatibility only. For new code, preferunknownwith type guards. - Zod schemas: All tool input schemas are typed as
z.ZodRawShape - Generics: Use
toolDef<const TSchema extends z.ZodRawShape>()pattern for type-safe schema definitions consttype parameters: Preserve literal types when defining Zod schemas
Tool Writing Pattern
All tools must follow this structure:
toolDef(): Helper function with type inference (simple pass-through)defineTool(): Casts toAnyToolDeftype (used when converting to arrays in server.ts)text(): MCP ToolResult helper producing{ content: [{ type: "text", text: content }] }
AnyToolDef also carries the optional examples, guidelines, typeLabels,
typeDefs, returnType, and returnDescription fields. Nothing reads them any
more — the documentation is hand-written — so leave them unset. The fields stay on
the type because the toolDef() / defineTool() signature is frozen
(see 09-safe-change-rules).
Input Schema Rules (OpenAI Tool Guide)
Tool schemas are consumed by OpenAI-compatible endpoints (the agent kit wires MCP
tools into ChatOpenAI), so they follow the
OpenAI function calling guide.
Strict mode rejects the whole request when an unsupported keyword survives, so the
following are banned in inputSchema:
Consequences to keep in mind:
- Every field is
required. The guide states "All fields inpropertiesmust be marked asrequired." Callers cannot omit a field; they passnull. nullmeans "use the default." Handlers mapnulltoundefinedso the domain layer (src/exchange/,src/tmdb/,src/system/) applies the default.- Defaults and ranges live in
.describe(), which is the only place the model reads them, and are repeated inREADME.mdandSKILL.md. src/tools/openai-schema.test.tsenforces all of this against the JSON Schema the MCP SDK actually emits. Add no tool that fails it.
Maximum File Length
- Tool definition files: Maximum 200 lines recommended (split into separate files when too many tools)
- Handler logic: Inline within tool definition files when possible. Extract shared logic into separate utility functions.
Documentation
README.md and skills/<bin>/SKILL.md are hand-written. There is no generator and
no pnpm readme command — adding or changing a tool means editing both files by hand,
including the defaults and ranges that no longer live in the schema.
Import/Export Rules
- Named exports only (no default exports)
- Path alias:
@/*→src/*. Defined in the roottsconfig.jsonpaths;rstest.config.tsmirrors the same alias for tests.- Import the shared kit as
@/commonand the agent kit as@/common/agent(@/commondoes not re-exportagent/).
- Relative vs alias: Use
@/for cross-directory imports (e.g.,server.ts→@/tools/index). Keep relative paths for same-directory siblings (e.g.,./exchange). - File extensions: Omit them (
moduleResolution: "Bundler"— tsc, tsup/esbuild, and rstest all resolve the.tsfile). Everything is bundled, so no runtime extension is required. - Re-export: Use
export { tools } from "./exchange"to selectively re-export only what's needed
Comment Guidelines
- Tool descriptions: Write concisely in the
descriptionfield - Zod describe: Add
.describe()for every parameter. It is mandatory — withdefaultand range keywords banned from the schema,.describe()is the only place the model learns them - Code comments: Only use when explaining essential reasoning (always include a reason for
eslint-disablecomments) - Usage notes: Write them in
README.mdandSKILL.md, not in the tool definition
Async Handling
- All tool handlers must be
asyncfunctions - Handle errors with
try/catchinside handlers, or usehandleCliError()for Zod error auto-formatting in CLI - The MCP SDK handles async errors internally for server mode
Global Error Handling
- CLI:
handleCliError()catchesZodErrorand converts to user-friendly messages - MCP Server: Top-level
catchinserver.tsterminates the process - Exceptions inside tool handlers are automatically converted to
CallToolResulterror responses by the MCP SDK