Imported from j0hanz/todokit-mcp-server (
AGENTS.md). Install upstream withnpx skills add j0hanz/todokit-mcp-server. Copyright stays with the author.
AGENTS.md
Purpose: High-signal context and strict guidelines for AI agents working in this repository.
1) Project Context
- Domain: MCP (Model Context Protocol) server for task management — a local, persistent todo list with JSON file storage, cursor pagination, and diagnostics.
- Tech Stack (Verified):
- Language: TypeScript 5.9+ (see
package.jsondevDependencies,tsconfig.json) - Runtime: Node.js ≥ 24 (see
package.jsonengines) - Framework:
@modelcontextprotocol/sdk^1.26.0 — MCP server SDK v1.x (seepackage.jsondependencies) - Key Libraries:
zod^4.3.6 — schema validation withz.strictObject()(seepackage.json,src/schema.ts)tsx^4.21.0 — TypeScript execution for tests (seepackage.jsondevDependencies)eslint^9.39.2 +typescript-eslint^8.54.0 (seeeslint.config.mjs)prettier^3.8.1 +@trivago/prettier-plugin-sort-imports^6.0.2 (see.prettierrc)
- Language: TypeScript 5.9+ (see
- Architecture: Single-package MCP server using stdio transport. Storage layer uses atomic JSON file writes with file-based locking and in-memory caching. Diagnostics via
node:diagnostics_channel. Request context propagated viaAsyncLocalStorage. (seesrc/index.ts,src/storage.ts,src/diagnostics.ts,src/requestContext.ts)
2) Repository Map (High-Level)
src/— Main source code (seetsconfig.jsonrootDir)index.ts— CLI entrypoint with shebang, server creation, transport wiring, shutdown handlingtools.ts— Tool registration, handler wrappers with timeout/abort/diagnosticsstorage.ts— JSON file storage with Port/Adapter interfaces (FileSystemPort,LockPort), atomic writes, file locking, caching,TodoRepositoryschema.ts— Zod schemas for all tool inputs/outputs usingz.strictObject()responses.ts—createToolResponse()/createErrorResponse()helpersdiagnostics.ts—node:diagnostics_channelpublishers for tool, storage, and lifecycle eventsrequestContext.ts—AsyncLocalStorage-based request contextconstants.ts— Error name/code constantsinstructions.md— Server instructions resource (bundled into dist)
tests/— Test files usingnode:test(seetests/setup.ts,tests/*.test.ts)scripts/— Build orchestration (scripts/tasks.mjs)assets/— Server icon (assets/logo.svg).github/workflows/— CI/CD: publish to npm on release (seepublish.yml)
Ignore:
dist/,node_modules/,coverage/,.tsbuildinfo
3) Operational Commands (Verified)
- Environment: Node.js ≥ 24, npm (see
package.jsonengines,package-lock.json) - Install:
npm ci(see.github/workflows/publish.yml) - Dev (watch):
npm run dev→tsc --watch --preserveWatchOutput(seepackage.jsonscripts) - Dev (run):
npm run dev:run→node --env-file=.env --watch dist/index.js(seepackage.jsonscripts) - Build:
npm run build→ clean, compile (tsc -p tsconfig.build.json), validate instructions, copy assets, chmod executable (seescripts/tasks.mjs) - Type-check:
npm run type-check→tsc -p tsconfig.json --noEmit(seescripts/tasks.mjs) - Test:
npm run test→ builds first, thennode --test --import tsx/esm tests/**/*.test.ts(seescripts/tasks.mjs) - Test with coverage:
npm run test:coverage→ adds--experimental-test-coverage(seescripts/tasks.mjs) - Lint:
npm run lint→eslint .(seepackage.jsonscripts) - Lint fix:
npm run lint:fix→eslint . --fix(seepackage.jsonscripts) - Format:
npm run format→prettier --write .(seepackage.jsonscripts) - Duplication check:
npm run dup-check→jscpd --config .jscpd.json(seepackage.jsonscripts) - Dead code check:
npm run knip(seepackage.jsonscripts) - Inspector:
npm run inspector→npx @modelcontextprotocol/inspector(seepackage.jsonscripts) - Full validation sequence:
npm run format; npm run lint; npm run type-check; npm run build; npm run test
4) Coding Standards (Style & Patterns)
- Naming: camelCase for variables/functions, PascalCase for types/classes/enums, UPPER_CASE for constants. Enforced via
@typescript-eslint/naming-convention(seeeslint.config.mjs). - Imports:
- Named exports only — no default exports (see
.github/instructions/typescript-mcp-server.instructions.md). - Type-only imports enforced:
import type { X }orimport { type X }(seeeslint.config.mjsconsistent-type-importsrule). .jsextensions required in local imports (NodeNext module resolution; seetsconfig.json).- Import order enforced by
@trivago/prettier-plugin-sort-imports: node builtins → MCP SDK → zod → third-party → local (see.prettierrc).
- Named exports only — no default exports (see
- TypeScript Strictness:
strict,noUncheckedIndexedAccess,verbatimModuleSyntax,isolatedModules,exactOptionalPropertyTypes,noImplicitOverride,noImplicitReturns,noFallthroughCasesInSwitch— all enabled (seetsconfig.json). - Explicit return types required on exported functions (see
eslint.config.mjsexplicit-function-return-typerule). - Formatting: Prettier — single quotes, trailing commas (es5), 2-space indent, 80 char width, LF line endings (see
.prettierrc). - Schemas: All Zod schemas use
z.strictObject()with.describe()on every parameter and bounds (.min(),.max()) on strings/arrays/numbers (seesrc/schema.ts,.github/instructions/typescript-mcp-server.instructions.md). - Tool output shape: Always return both
content(JSON text) andstructuredContentin tool results. On failure setisError: truewith{ ok: false, error: { code, message } }(seesrc/responses.ts,.github/instructions/typescript-mcp-server.instructions.md). - Patterns Observed:
- Port/Adapter pattern for I/O boundaries:
FileSystemPort,LockPortinterfaces decoupled fromNodeFileSystem,LockFileManagerimplementations (observed insrc/storage.ts). - Coded error domain:
StorageErrorwithcodefield;createErrorResponse()maps to structured output (observed insrc/storage.ts,src/responses.ts). - Diagnostics-first instrumentation: every tool call and storage operation publishes events via
node:diagnostics_channelchannelstodokit:tool,todokit:storage,todokit:lifecycle(observed insrc/diagnostics.ts,src/tools.ts). - Request context via
AsyncLocalStoragefor correlating tool calls with storage events (observed insrc/requestContext.ts,src/diagnostics.ts). - Atomic file writes: write to temp file, then rename with retry on transient OS errors (observed in
src/storage.tsNodeFileSystem.writeTextAtomic()). - File-based locking with exponential backoff and ownership verification via
timingSafeEqual(observed insrc/storage.tsLockFileManager). - In-memory cache with mtime-based invalidation (observed in
src/storage.tsJsonFileStore). - Cursor-based pagination using base64url-encoded JSON payloads (observed in
src/tools.ts). - Tool wrapper with timeout, abort, and diagnostics tracing (observed in
src/tools.tscreateWrappedHandler()). - Shebang required:
src/index.tsmust start with#!/usr/bin/env nodeas the first line (see.github/instructions/typescript-mcp-server.instructions.md). - Logging to stderr only — never write non-MCP output to stdout (see
.github/instructions/typescript-mcp-server.instructions.md).
- Port/Adapter pattern for I/O boundaries:
5) Agent Behavioral Rules (Do Nots)
- Do not introduce new dependencies without updating
package.jsonand runningnpm installto regeneratepackage-lock.json. (seepackage.json,package-lock.json) - Do not edit
package-lock.jsonmanually. (seepackage-lock.json) - Do not commit secrets; never print
.envvalues; useprocess.envfor config. (see.gitignoreexcludes.env*) - Do not use default exports; use named exports only. (see
.github/instructions/typescript-mcp-server.instructions.md) - Do not write non-MCP output to stdout — use
console.error()for logging. (see.github/instructions/typescript-mcp-server.instructions.md) - Do not use
any—@typescript-eslint/no-explicit-anyis set to error. (seeeslint.config.mjs) - Do not omit
.jsextensions on local imports. (seetsconfig.jsonmoduleResolution: "NodeNext") - Do not use
z.object()— always usez.strictObject()for schema definitions. (see.github/instructions/typescript-mcp-server.instructions.md,src/schema.ts) - Do not return tool results without both
contentandstructuredContent. (seesrc/responses.ts,.github/instructions/typescript-mcp-server.instructions.md) - Do not disable or bypass existing lint/type rules without explicit approval. (see
eslint.config.mjs,tsconfig.json) - Do not throw uncaught exceptions from tool handlers; return
isError: trueresponses. (see.github/instructions/typescript-mcp-server.instructions.md) - Do not remove the shebang line (
#!/usr/bin/env node) fromsrc/index.ts. (see.github/instructions/typescript-mcp-server.instructions.md)
6) Testing Strategy (Verified)
- Framework:
node:test(Node.js built-in test runner) withtsxloader for TypeScript (seescripts/tasks.mjs,package.jsondevDependencies) - Where tests live:
tests/*.test.ts(seescripts/tasks.mjsCONFIG.test.patterns) - Setup:
tests/setup.ts—beforeEachcreates a temp directory and setsTODOKIT_TODO_FILEenv var;afterEachcloses DB and cleans up. Each test runs in isolation. (seetests/setup.ts) - Approach:
- Unit tests with a mock
McpServerharness that captures registered tools and calls handlers directly (observed intests/tools.test.ts). - Storage tests use real filesystem via temp directories — no mocks for I/O. (see
tests/setup.ts,tests/storage.test.ts) - Assertions via
node:assert/strict. (observed intests/tools.test.ts) - Tests import directly from
src/(notdist/), run viatsx. (observed in test imports)
- Unit tests with a mock
- Coverage:
npm run test:coverageadds--experimental-test-coverage. (seescripts/tasks.mjs) - Timeout: Test-level timeouts set per test (e.g.,
TEST_TIMEOUT_MS = 5000). (observed intests/tools.test.ts) - No external services required. Tests are fully self-contained with temp file storage.
7) Common Pitfalls (Verified)
- Forgetting
.jsin imports → TypeScript compiles but runtime fails withERR_MODULE_NOT_FOUND. Always use.jsextensions for local imports. (seetsconfig.jsonmoduleResolution: "NodeNext") - Using
z.object()instead ofz.strictObject()→ unknown fields silently pass validation. The codebase exclusively usesz.strictObject(). (seesrc/schema.ts) - Writing to stdout → corrupts JSON-RPC stdio transport. Use
console.error(). (see.github/instructions/typescript-mcp-server.instructions.md) - Forgetting
structuredContent→ tool responses must include bothcontent(text) andstructuredContent. UsecreateToolResponse()/createErrorResponse()helpers. (seesrc/responses.ts) - Build required before test →
npm run testtriggers a full build automatically viascripts/tasks.mjs. Running tests directly withouttsxloader will fail. - Storage file auto-deletion → when all todos are completed, the JSON file is automatically deleted. Tests must account for this behavior. (see
src/storage.tsdeleteFileflag in transactions)
8) Evolution Rules
- If conventions change, include an
AGENTS.mdupdate in the same PR. - If a command is corrected after failures, record the final verified command here.
- If a new critical path or pattern is discovered, add it to the relevant section with evidence.