For repo structure, architecture, shared utilities, and where things live, read
CODEBASE.mdfirst. This file covers the rules; CODEBASE.md is the map.
structuredContent is sanitised with removeNullFields before it leaves the server, so a null can never reach a client. A nullable output field therefore declares a value the tool cannot deliver: the MCP SDK validates the sanitised payload against the declared outputSchema and fails the whole call with Output validation error the moment that field is stripped.
Declare the field .optional() instead and leave the key out of structuredContent (foo: value ?? undefined) when there is nothing to report. src/tools/output-schema-nullability.test.ts enforces this across every registered tool.
When you need to support clearing an optional field:
-
Use a special string value (not
null- avoids LLM provider compatibility issues, with Gemini in particular)- For assignments: use
"unassign" - For other fields: use
"remove"or similar descriptive string
- For assignments: use
-
Handle both legacy and new patterns in runtime logic for backward compatibility:
if (fieldValue === null || fieldValue === 'remove') { // Convert to null for API call updateArgs = { ...updateArgs, fieldName: null } }
-
Update schema description to document the special string value
- PR #181: Fixed
responsibleUserfield - changed from.nullable()to using"unassign"string - Latest commit: Fixed
deadlineDatefield - changed from.nullable()to using"remove"string
- Ensures compatibility with all LLM providers (OpenAI, Anthropic, Gemini, etc.)
- Maintains backward compatibility through dual handling
- Creates self-documenting APIs with explicit action strings
When adding new tool parameters:
- Add comprehensive test coverage for new fields
- Test setting values
- Test clearing values (if applicable)
- Verify build and type checking pass
- Run full test suite (all 333+ tests must pass)
When adding new tool features:
- Update tool schema descriptions in the source file
- Update
src/mcp-server.tstool usage guidelines - Add tests demonstrating the feature
- Include examples in descriptions where helpful
CODEBASE.md is a structural map, not a file index. Update it when structure changes — not on every new file. Triggers:
- new top-level dir under
src/, or a new tool-file category - a new broadly-reusable helper in
src/utils/(the "don't reimplement" list) - changes to the tool contract, registration flow, auth, or build/test/release tooling
Adding a single tool in an existing category or a narrowly-scoped helper used by one caller does not require an update.
Use scripts/run-tool.ts to execute any tool without the MCP server:
npx tsx scripts/run-tool.ts <tool-name> '<json-args>'
npx tsx scripts/run-tool.ts --list # list all toolsExamples:
npx tsx scripts/run-tool.ts add-tasks '{"tasks":[{"content":"Test task"}]}'
npx tsx scripts/run-tool.ts find-tasks '{"searchText":"meeting"}'
npx tsx scripts/run-tool.ts get-overview '{}'Requires TODOIST_API_KEY in .env (and optionally TODOIST_BASE_URL).