search-epics
Search and filter Shortcut epics by ID, name, state, owner, due dates, and other criteria to manage project workflows.
Instructions
Find Shortcut epics.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Find only epics with the specified public ID | |
| name | No | Find only epics matching the specified name | |
| description | No | Find only epics matching the specified description | |
| state | No | Find only epics matching the specified state | |
| objective | No | Find only epics matching the specified objective | |
| owner | No | Find entities where the owner match the specified user. This must either be the user's mention name or the keyword "me" for the current user. | |
| requester | No | Find entities where the requester match the specified user. This must either be the user's mention name or the keyword "me" for the current user. | |
| team | No | Find only epics matching the specified team. Should be a team's mention name. | |
| comment | No | Find only epics matching the specified comment | |
| isUnstarted | No | Find only entities that are unstarted when true, or only entities that are not unstarted when false. | |
| isStarted | No | Find only entities that are started when true, or only entities that are not started when false. | |
| isDone | No | Find only entities that are completed when true, or only entities that are not completed when false. | |
| isArchived | No | Find only entities that are archived when true, or only entities that are not archived when false. | |
| isOverdue | No | Find only entities that are overdue when true, or only entities that are not overdue when false. | |
| hasOwner | No | Find only entities that have an owner when true, or only entities that do not have an owner when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasComment | No | Find only entities that have a comment when true, or only entities that do not have a comment when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasDeadline | No | Find only entities that have a deadline when true, or only entities that do not have a deadline when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasLabel | No | Find only entities that have a label when true, or only entities that do not have a label when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| created | No | The date in "YYYY-MM-DD" format, or one of the keywords: "yesterday", "today", "tomorrow", or a date range in the format "YYYY-MM-DD..YYYY-MM-DD". The date range can also be open ended by using "*" for one of the bounds. Examples: "2023-01-01", "today", "2023-01-01..*" (from Jan 1, 2023 to any future date), "*.2023-01-31" (any date up to Jan 31, 2023), "today..*" (from today onwards), "*.yesterday" (any date up to yesterday). The keywords cannot be used to calculate relative dates (e.g. the following are not valid: "today-1" or "tomorrow+1"). | |
| updated | No | The date in "YYYY-MM-DD" format, or one of the keywords: "yesterday", "today", "tomorrow", or a date range in the format "YYYY-MM-DD..YYYY-MM-DD". The date range can also be open ended by using "*" for one of the bounds. Examples: "2023-01-01", "today", "2023-01-01..*" (from Jan 1, 2023 to any future date), "*.2023-01-31" (any date up to Jan 31, 2023), "today..*" (from today onwards), "*.yesterday" (any date up to yesterday). The keywords cannot be used to calculate relative dates (e.g. the following are not valid: "today-1" or "tomorrow+1"). | |
| completed | No | The date in "YYYY-MM-DD" format, or one of the keywords: "yesterday", "today", "tomorrow", or a date range in the format "YYYY-MM-DD..YYYY-MM-DD". The date range can also be open ended by using "*" for one of the bounds. Examples: "2023-01-01", "today", "2023-01-01..*" (from Jan 1, 2023 to any future date), "*.2023-01-31" (any date up to Jan 31, 2023), "today..*" (from today onwards), "*.yesterday" (any date up to yesterday). The keywords cannot be used to calculate relative dates (e.g. the following are not valid: "today-1" or "tomorrow+1"). | |
| due | No | The date in "YYYY-MM-DD" format, or one of the keywords: "yesterday", "today", "tomorrow", or a date range in the format "YYYY-MM-DD..YYYY-MM-DD". The date range can also be open ended by using "*" for one of the bounds. Examples: "2023-01-01", "today", "2023-01-01..*" (from Jan 1, 2023 to any future date), "*.2023-01-31" (any date up to Jan 31, 2023), "today..*" (from today onwards), "*.yesterday" (any date up to yesterday). The keywords cannot be used to calculate relative dates (e.g. the following are not valid: "today-1" or "tomorrow+1"). |
Implementation Reference
- src/tools/epics.ts:79-91 (handler)The handler function that implements the core logic of the 'search-epics' tool. It constructs a search query, fetches epics from the Shortcut client, handles edge cases, and formats the results.async searchEpics(params: QueryParams) { const currentUser = await this.client.getCurrentUser(); const query = await buildSearchQuery(params, currentUser); const { epics, total } = await this.client.searchEpics(query); if (!epics) throw new Error(`Failed to search for epics matching your query: "${query}"`); if (!epics.length) return this.toResult(`Result: No epics found.`); return this.toResult( `Result (first ${epics.length} shown of ${total} total epics found):`, await this.entitiesWithRelatedEntities(epics, "epics"), ); }
- src/tools/epics.ts:23-60 (schema)Zod schema defining all input parameters and validations for the 'search-epics' tool, including optional filters for ID, name, description, state, owner, etc.{ id: z.number().optional().describe("Find only epics with the specified public ID"), name: z.string().optional().describe("Find only epics matching the specified name"), description: z .string() .optional() .describe("Find only epics matching the specified description"), state: z .enum(["unstarted", "started", "done"]) .optional() .describe("Find only epics matching the specified state"), objective: z .number() .optional() .describe("Find only epics matching the specified objective"), owner: user("owner"), requester: user("requester"), team: z .string() .optional() .describe( "Find only epics matching the specified team. Should be a team's mention name.", ), comment: z.string().optional().describe("Find only epics matching the specified comment"), isUnstarted: is("unstarted"), isStarted: is("started"), isDone: is("completed"), isArchived: is("archived").default(false), isOverdue: is("overdue"), hasOwner: has("an owner"), hasComment: has("a comment"), hasDeadline: has("a deadline"), hasLabel: has("a label"), created: date(), updated: date(), completed: date(), due: date(), },
- src/tools/epics.ts:20-62 (registration)Registers the 'search-epics' tool with the MCP server, specifying name, description, input schema, and handler function.server.tool( "search-epics", "Find Shortcut epics.", { id: z.number().optional().describe("Find only epics with the specified public ID"), name: z.string().optional().describe("Find only epics matching the specified name"), description: z .string() .optional() .describe("Find only epics matching the specified description"), state: z .enum(["unstarted", "started", "done"]) .optional() .describe("Find only epics matching the specified state"), objective: z .number() .optional() .describe("Find only epics matching the specified objective"), owner: user("owner"), requester: user("requester"), team: z .string() .optional() .describe( "Find only epics matching the specified team. Should be a team's mention name.", ), comment: z.string().optional().describe("Find only epics matching the specified comment"), isUnstarted: is("unstarted"), isStarted: is("started"), isDone: is("completed"), isArchived: is("archived").default(false), isOverdue: is("overdue"), hasOwner: has("an owner"), hasComment: has("a comment"), hasDeadline: has("a deadline"), hasLabel: has("a label"), created: date(), updated: date(), completed: date(), due: date(), }, async (params) => await tools.searchEpics(params), );
- src/tools/utils/search.ts:19-36 (helper)Helper utility to build the Shortcut search query string from the tool input parameters, handling special cases for owners/requesters, booleans, numbers, and quoted strings.export const buildSearchQuery = async (params: QueryParams, currentUser: MemberInfo | null) => { const query = Object.entries(params) .map(([key, value]) => { const q = getKey(key); if (key === "owner" || key === "requester") { if (value === "me") return `${q}:${currentUser?.mention_name || value}`; return `${q}:${String(value || "").replace(/^@/, "")}`; } if (typeof value === "boolean") return value ? q : `!${q}`; if (typeof value === "number") return `${q}:${value}`; if (typeof value === "string" && value.includes(" ")) return `${q}:"${value}"`; return `${q}:${value}`; }) .join(" "); return query; };