search-stories
Search and filter Shortcut stories by ID, name, description, type, status, owner, project, labels, dates, and other criteria to find specific work items.
Instructions
Find Shortcut stories.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | Find only stories with the specified public ID | |
| name | No | Find only stories matching the specified name | |
| description | No | Find only stories matching the specified description | |
| comment | No | Find only stories matching the specified comment | |
| type | No | Find only stories of the specified type | |
| estimate | No | Find only stories matching the specified estimate | |
| branch | No | Find only stories matching the specified branch | |
| commit | No | Find only stories matching the specified commit | |
| pr | No | Find only stories matching the specified pull request | |
| project | No | Find only stories matching the specified project | |
| epic | No | Find only stories matching the specified epic | |
| objective | No | Find only stories matching the specified objective | |
| state | No | Find only stories matching the specified state | |
| label | No | Find only stories matching the specified label | |
| 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 stories matching the specified team. This can be a team mention name or team name. | |
| skillSet | No | Find only stories matching the specified skill set | |
| productArea | No | Find only stories matching the specified product area | |
| technicalArea | No | Find only stories matching the specified technical area | |
| priority | No | Find only stories matching the specified priority | |
| severity | No | Find only stories matching the specified severity | |
| isDone | No | Find only entities that are completed when true, or only entities that are not completed when false. | |
| isStarted | No | Find only entities that are started when true, or only entities that are not started when false. | |
| isUnstarted | No | Find only entities that are unstarted when true, or only entities that are not unstarted when false. | |
| isUnestimated | No | Find only entities that are unestimated when true, or only entities that are not unestimated when false. | |
| isOverdue | No | Find only entities that are overdue when true, or only entities that are not overdue when false. | |
| isArchived | No | Find only entities that are archived when true, or only entities that are not archived when false. | |
| isBlocker | No | Find only entities that are blocking when true, or only entities that are not blocking when false. | |
| isBlocked | No | Find only entities that are blocked when true, or only entities that are not blocked when false. | |
| 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. | |
| 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. | |
| 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. | |
| 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. | |
| hasPr | No | Find only entities that have a pr when true, or only entities that do not have a pr when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasCommit | No | Find only entities that have a commit when true, or only entities that do not have a commit when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasBranch | No | Find only entities that have a branch when true, or only entities that do not have a branch when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasEpic | No | Find only entities that have an epic when true, or only entities that do not have an epic when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasTask | No | Find only entities that have a task when true, or only entities that do not have a task when false. Example: hasOwner: true will find stories with an owner, hasOwner: false will find stories without an owner. | |
| hasAttachment | No | Find only entities that have an attachment when true, or only entities that do not have an attachment 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/stories.ts:432-444 (handler)Main handler function for the 'search-stories' tool. Builds a search query from input parameters, performs the search using the Shortcut client, handles results or errors, and formats the output.async searchStories(params: QueryParams) { const currentUser = await this.client.getCurrentUser(); const query = await buildSearchQuery(params, currentUser); const { stories, total } = await this.client.searchStories(query); if (!stories) throw new Error(`Failed to search for stories matching your query: "${query}".`); if (!stories.length) return this.toResult(`Result: No stories found.`); return this.toResult( `Result (first ${stories.length} shown of ${total} total stories found):`, await this.entitiesWithRelatedEntities(stories, "stories"), ); }
- src/tools/stories.ts:31-113 (registration)Registers the 'search-stories' MCP tool on the server, including name, description, input schema, and handler reference.server.tool( "search-stories", "Find Shortcut stories.", { id: z.number().optional().describe("Find only stories with the specified public ID"), name: z.string().optional().describe("Find only stories matching the specified name"), description: z .string() .optional() .describe("Find only stories matching the specified description"), comment: z.string().optional().describe("Find only stories matching the specified comment"), type: z .enum(["feature", "bug", "chore"]) .optional() .describe("Find only stories of the specified type"), estimate: z .number() .optional() .describe("Find only stories matching the specified estimate"), branch: z.string().optional().describe("Find only stories matching the specified branch"), commit: z.string().optional().describe("Find only stories matching the specified commit"), pr: z.number().optional().describe("Find only stories matching the specified pull request"), project: z.number().optional().describe("Find only stories matching the specified project"), epic: z.number().optional().describe("Find only stories matching the specified epic"), objective: z .number() .optional() .describe("Find only stories matching the specified objective"), state: z.string().optional().describe("Find only stories matching the specified state"), label: z.string().optional().describe("Find only stories matching the specified label"), owner: user("owner"), requester: user("requester"), team: z .string() .optional() .describe( "Find only stories matching the specified team. This can be a team mention name or team name.", ), skillSet: z .string() .optional() .describe("Find only stories matching the specified skill set"), productArea: z .string() .optional() .describe("Find only stories matching the specified product area"), technicalArea: z .string() .optional() .describe("Find only stories matching the specified technical area"), priority: z .string() .optional() .describe("Find only stories matching the specified priority"), severity: z .string() .optional() .describe("Find only stories matching the specified severity"), isDone: is("completed"), isStarted: is("started"), isUnstarted: is("unstarted"), isUnestimated: is("unestimated"), isOverdue: is("overdue"), isArchived: is("archived").default(false), isBlocker: is("blocking"), isBlocked: is("blocked"), hasComment: has("a comment"), hasLabel: has("a label"), hasDeadline: has("a deadline"), hasOwner: has("an owner"), hasPr: has("a pr"), hasCommit: has("a commit"), hasBranch: has("a branch"), hasEpic: has("an epic"), hasTask: has("a task"), hasAttachment: has("an attachment"), created: date(), updated: date(), completed: date(), due: date(), }, async (params) => await tools.searchStories(params), );
- src/tools/stories.ts:34-111 (schema)Zod input schema defining all parameters for searching stories, including filters for ID, name, type, state, owners, dates, etc.{ id: z.number().optional().describe("Find only stories with the specified public ID"), name: z.string().optional().describe("Find only stories matching the specified name"), description: z .string() .optional() .describe("Find only stories matching the specified description"), comment: z.string().optional().describe("Find only stories matching the specified comment"), type: z .enum(["feature", "bug", "chore"]) .optional() .describe("Find only stories of the specified type"), estimate: z .number() .optional() .describe("Find only stories matching the specified estimate"), branch: z.string().optional().describe("Find only stories matching the specified branch"), commit: z.string().optional().describe("Find only stories matching the specified commit"), pr: z.number().optional().describe("Find only stories matching the specified pull request"), project: z.number().optional().describe("Find only stories matching the specified project"), epic: z.number().optional().describe("Find only stories matching the specified epic"), objective: z .number() .optional() .describe("Find only stories matching the specified objective"), state: z.string().optional().describe("Find only stories matching the specified state"), label: z.string().optional().describe("Find only stories matching the specified label"), owner: user("owner"), requester: user("requester"), team: z .string() .optional() .describe( "Find only stories matching the specified team. This can be a team mention name or team name.", ), skillSet: z .string() .optional() .describe("Find only stories matching the specified skill set"), productArea: z .string() .optional() .describe("Find only stories matching the specified product area"), technicalArea: z .string() .optional() .describe("Find only stories matching the specified technical area"), priority: z .string() .optional() .describe("Find only stories matching the specified priority"), severity: z .string() .optional() .describe("Find only stories matching the specified severity"), isDone: is("completed"), isStarted: is("started"), isUnstarted: is("unstarted"), isUnestimated: is("unestimated"), isOverdue: is("overdue"), isArchived: is("archived").default(false), isBlocker: is("blocking"), isBlocked: is("blocked"), hasComment: has("a comment"), hasLabel: has("a label"), hasDeadline: has("a deadline"), hasOwner: has("an owner"), hasPr: has("a pr"), hasCommit: has("a commit"), hasBranch: has("a branch"), hasEpic: has("an epic"), hasTask: has("a task"), hasAttachment: has("an attachment"), created: date(), updated: date(), completed: date(), due: date(), },
- src/tools/utils/search.ts:19-36 (helper)Helper function that constructs the search query string from the input parameters, handling special cases for users, 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; };