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
| 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"). |
Input Schema (JSON Schema)
{
"properties": {
"branch": {
"description": "Find only stories matching the specified branch",
"type": "string"
},
"comment": {
"description": "Find only stories matching the specified comment",
"type": "string"
},
"commit": {
"description": "Find only stories matching the specified commit",
"type": "string"
},
"completed": {
"description": "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\").",
"pattern": "^(today|tomorrow|yesterday|\\d{4}-\\d{2}-\\d{2}|today\\.\\.\\*|\\*\\.\\.today|yesterday\\.\\.\\*|\\*\\.\\.yesterday|tomorrow\\.\\.\\*|\\*\\.\\.tomorrow|\\d{4}-\\d{2}-\\d{2}\\.\\.\\*|\\*\\.\\.\\d{4}-\\d{2}-\\d{2}|\\d{4}-\\d{2}-\\d{2}\\.\\.\\d{4}-\\d{2}-\\d{2})$",
"type": "string"
},
"created": {
"description": "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\").",
"pattern": "^(today|tomorrow|yesterday|\\d{4}-\\d{2}-\\d{2}|today\\.\\.\\*|\\*\\.\\.today|yesterday\\.\\.\\*|\\*\\.\\.yesterday|tomorrow\\.\\.\\*|\\*\\.\\.tomorrow|\\d{4}-\\d{2}-\\d{2}\\.\\.\\*|\\*\\.\\.\\d{4}-\\d{2}-\\d{2}|\\d{4}-\\d{2}-\\d{2}\\.\\.\\d{4}-\\d{2}-\\d{2})$",
"type": "string"
},
"description": {
"description": "Find only stories matching the specified description",
"type": "string"
},
"due": {
"description": "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\").",
"pattern": "^(today|tomorrow|yesterday|\\d{4}-\\d{2}-\\d{2}|today\\.\\.\\*|\\*\\.\\.today|yesterday\\.\\.\\*|\\*\\.\\.yesterday|tomorrow\\.\\.\\*|\\*\\.\\.tomorrow|\\d{4}-\\d{2}-\\d{2}\\.\\.\\*|\\*\\.\\.\\d{4}-\\d{2}-\\d{2}|\\d{4}-\\d{2}-\\d{2}\\.\\.\\d{4}-\\d{2}-\\d{2})$",
"type": "string"
},
"epic": {
"description": "Find only stories matching the specified epic",
"type": "number"
},
"estimate": {
"description": "Find only stories matching the specified estimate",
"type": "number"
},
"hasAttachment": {
"description": "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.",
"type": "boolean"
},
"hasBranch": {
"description": "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.",
"type": "boolean"
},
"hasComment": {
"description": "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.",
"type": "boolean"
},
"hasCommit": {
"description": "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.",
"type": "boolean"
},
"hasDeadline": {
"description": "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.",
"type": "boolean"
},
"hasEpic": {
"description": "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.",
"type": "boolean"
},
"hasLabel": {
"description": "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.",
"type": "boolean"
},
"hasOwner": {
"description": "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.",
"type": "boolean"
},
"hasPr": {
"description": "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.",
"type": "boolean"
},
"hasTask": {
"description": "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.",
"type": "boolean"
},
"id": {
"description": "Find only stories with the specified public ID",
"type": "number"
},
"isArchived": {
"default": false,
"description": "Find only entities that are archived when true, or only entities that are not archived when false.",
"type": "boolean"
},
"isBlocked": {
"description": "Find only entities that are blocked when true, or only entities that are not blocked when false.",
"type": "boolean"
},
"isBlocker": {
"description": "Find only entities that are blocking when true, or only entities that are not blocking when false.",
"type": "boolean"
},
"isDone": {
"description": "Find only entities that are completed when true, or only entities that are not completed when false.",
"type": "boolean"
},
"isOverdue": {
"description": "Find only entities that are overdue when true, or only entities that are not overdue when false.",
"type": "boolean"
},
"isStarted": {
"description": "Find only entities that are started when true, or only entities that are not started when false.",
"type": "boolean"
},
"isUnestimated": {
"description": "Find only entities that are unestimated when true, or only entities that are not unestimated when false.",
"type": "boolean"
},
"isUnstarted": {
"description": "Find only entities that are unstarted when true, or only entities that are not unstarted when false.",
"type": "boolean"
},
"label": {
"description": "Find only stories matching the specified label",
"type": "string"
},
"name": {
"description": "Find only stories matching the specified name",
"type": "string"
},
"objective": {
"description": "Find only stories matching the specified objective",
"type": "number"
},
"owner": {
"description": "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.",
"type": "string"
},
"pr": {
"description": "Find only stories matching the specified pull request",
"type": "number"
},
"priority": {
"description": "Find only stories matching the specified priority",
"type": "string"
},
"productArea": {
"description": "Find only stories matching the specified product area",
"type": "string"
},
"project": {
"description": "Find only stories matching the specified project",
"type": "number"
},
"requester": {
"description": "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.",
"type": "string"
},
"severity": {
"description": "Find only stories matching the specified severity",
"type": "string"
},
"skillSet": {
"description": "Find only stories matching the specified skill set",
"type": "string"
},
"state": {
"description": "Find only stories matching the specified state",
"type": "string"
},
"team": {
"description": "Find only stories matching the specified team. This can be a team mention name or team name.",
"type": "string"
},
"technicalArea": {
"description": "Find only stories matching the specified technical area",
"type": "string"
},
"type": {
"description": "Find only stories of the specified type",
"enum": [
"feature",
"bug",
"chore"
],
"type": "string"
},
"updated": {
"description": "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\").",
"pattern": "^(today|tomorrow|yesterday|\\d{4}-\\d{2}-\\d{2}|today\\.\\.\\*|\\*\\.\\.today|yesterday\\.\\.\\*|\\*\\.\\.yesterday|tomorrow\\.\\.\\*|\\*\\.\\.tomorrow|\\d{4}-\\d{2}-\\d{2}\\.\\.\\*|\\*\\.\\.\\d{4}-\\d{2}-\\d{2}|\\d{4}-\\d{2}-\\d{2}\\.\\.\\d{4}-\\d{2}-\\d{2})$",
"type": "string"
}
},
"type": "object"
}
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; };