get-projects
Retrieve and filter projects from Things 3 with options to include items, filter by status or area, search text, and control response detail.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| include_items | No | Include headings and todos within projects | |
| status | No | Optional status filter | |
| area | No | Optional area UUID or title filter | |
| query | No | Optional text search | |
| detail | No | Response detail level. Defaults to compact. | |
| limit | No | Maximum number of projects to return |
Implementation Reference
- src/index.ts:902-968 (handler)Implementation of the get-projects MCP tool handler within src/index.ts.
server.tool( "get-projects", { include_items: z.boolean().optional().describe("Include headings and todos within projects"), status: z.string().optional().describe("Optional status filter"), area: z.string().optional().describe("Optional area UUID or title filter"), query: z.string().optional().describe("Optional text search"), detail: z.enum(["compact", "full"]).optional().describe("Response detail level. Defaults to compact."), limit: z.number().int().positive().optional().describe("Maximum number of projects to return"), }, async ({ include_items, status, area, query, detail, limit }) => { const requestedDetail = detail ?? "compact"; const projects = await withDatabase((db) => { const tasks = getAllTasks(db); let result = tasks.filter((task) => task.type === "project" && !task.trashed); if (status) { result = result.filter((task) => task.status === status.toLowerCase()); } if (area) { const lower = area.toLowerCase(); result = result.filter( (task) => task.areaId?.toLowerCase() === lower || task.areaTitle?.toLowerCase() === lower ); } if (query) { result = result.filter((task) => matchesQuery(task, query)); } result = applyLimit(result, limit); if (!include_items) { return result.map((project) => toTaskView(project, requestedDetail)); } return result.map((project) => ({ ...toTaskView(project, requestedDetail), headings: tasks .filter( (task) => task.type === "heading" && task.projectId === project.id && !task.trashed ) .map((task) => toTaskView(task, requestedDetail)), items: tasks .filter( (task) => task.type === "to-do" && task.projectId === project.id && !task.trashed ) .map((task) => toTaskView(task, requestedDetail)), })); }); return buildTextResponse(`Found ${projects.length} projects`, { projects, detail: requestedDetail, limit: limit ?? null, }); } );