@shortcut/mcp 0.11.2 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +62 -0
  2. package/dist/index.js +37 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -125,6 +125,68 @@ Or you can edit the local JSON file directly:
125
125
  }
126
126
  ```
127
127
 
128
+ ## Available Tools
129
+
130
+ ### Stories
131
+
132
+ - **get-story** - Get a single Shortcut story by ID
133
+ - **search-stories** - Find Shortcut stories with filtering and search options
134
+ - **get-story-branch-name** - Get the recommended branch name (based on workspace settings) for a specific story.
135
+ - **create-story** - Create a new Shortcut story
136
+ - **update-story** - Update an existing Shortcut story
137
+ - **upload-file-to-story** - Upload a file and link it to a story
138
+ - **assign-current-user-as-owner** - Assign the current user as the owner of a story
139
+ - **unassign-current-user-as-owner** - Unassign the current user as the owner of a story
140
+ - **create-story-comment** - Create a comment on a story
141
+ - **add-task-to-story** - Add a task to a story
142
+ - **update-task** - Update a task in a story
143
+ - **add-relation-to-story** - Add a story relationship (relates to, blocks, duplicates, etc.)
144
+ - **add-external-link-to-story** - Add an external link to a Shortcut story
145
+ - **remove-external-link-from-story** - Remove an external link from a Shortcut story
146
+ - **get-stories-by-external-link** - Find all stories that contain a specific external link
147
+ - **set-story-external-links** - Replace all external links on a story with a new set of links
148
+
149
+ ### Epics
150
+
151
+ - **get-epic** - Get a Shortcut epic by ID
152
+ - **search-epics** - Find Shortcut epics with filtering and search options
153
+ - **create-epic** - Create a new Shortcut epic
154
+
155
+ ### Iterations
156
+
157
+ - **get-iteration-stories** - Get stories in a specific iteration by iteration ID
158
+ - **get-iteration** - Get a Shortcut iteration by ID
159
+ - **search-iterations** - Find Shortcut iterations with filtering and search options
160
+ - **create-iteration** - Create a new Shortcut iteration with start/end dates
161
+ - **get-active-iterations** - Get active iterations for the current user based on team memberships
162
+ - **get-upcoming-iterations** - Get upcoming iterations for the current user based on team memberships
163
+
164
+ ### Objectives
165
+
166
+ - **get-objective** - Get a Shortcut objective by ID
167
+ - **search-objectives** - Find Shortcut objectives with filtering and search options
168
+
169
+ ### Teams
170
+
171
+ - **get-team** - Get a Shortcut team by ID
172
+ - **list-teams** - List all Shortcut teams
173
+
174
+ ### Workflows
175
+
176
+ - **get-default-workflow** - Get the default workflow for a specific team or the workspace default
177
+ - **get-workflow** - Get a Shortcut workflow by ID
178
+ - **list-workflows** - List all Shortcut workflows
179
+
180
+ ### Users
181
+
182
+ - **get-current-user** - Get the current user information
183
+ - **get-current-user-teams** - Get a list of teams where the current user is a member
184
+ - **list-users** - Get all workspace users
185
+
186
+ ### Documents
187
+
188
+ - **create-document** - Create a new document in Shortcut with HTML content
189
+
128
190
  ## Issues and Troubleshooting
129
191
 
130
192
  Before doing anything else, please make sure you are running the latest version!
package/dist/index.js CHANGED
@@ -2,6 +2,9 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { ShortcutClient } from "@shortcut/client";
5
+ import { File } from "node:buffer";
6
+ import { readFileSync } from "node:fs";
7
+ import { basename } from "node:path";
5
8
  import { z } from "zod";
6
9
 
7
10
  //#region src/client/cache.ts
@@ -422,6 +425,18 @@ var ShortcutClientWrapper = class {
422
425
  if (!doc) throw new Error(`Failed to create the document: ${response.status}`);
423
426
  return doc;
424
427
  }
428
+ async uploadFile(storyId, filePath) {
429
+ const fileContent = readFileSync(filePath);
430
+ const fileName = basename(filePath);
431
+ const file = new File([fileContent], fileName);
432
+ const response = await this.client.uploadFiles({
433
+ story_id: storyId,
434
+ file0: file
435
+ });
436
+ const uploadedFile = response?.data ?? null;
437
+ if (!uploadedFile?.length) throw new Error(`Failed to upload the file: ${response.status}`);
438
+ return uploadedFile[0];
439
+ }
425
440
  async getCustomFieldMap(customFieldIds) {
426
441
  await this.loadCustomFields();
427
442
  return new Map(customFieldIds.map((id) => [id, this.customFieldCache.get(id)]).filter((customField) => customField[1] !== null));
@@ -435,7 +450,7 @@ var ShortcutClientWrapper = class {
435
450
  //#endregion
436
451
  //#region package.json
437
452
  var name = "@shortcut/mcp";
438
- var version = "0.11.2";
453
+ var version = "0.12.1";
439
454
 
440
455
  //#endregion
441
456
  //#region src/tools/base.ts
@@ -1194,6 +1209,10 @@ The story will be added to the default state for the workflow.
1194
1209
  description: z.string().optional().describe("The description of the label")
1195
1210
  })).optional().describe("Labels to assign to the story")
1196
1211
  }, async (params) => await tools.updateStory(params));
1212
+ server$1.tool("upload-file-to-story", "Upload a file and link it to a story.", {
1213
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1214
+ filePath: z.string().describe("The path to the file to upload")
1215
+ }, async ({ storyPublicId, filePath }) => await tools.uploadFileToStory(storyPublicId, filePath));
1197
1216
  server$1.tool("assign-current-user-as-owner", "Assign the current user as the owner of a story", { storyPublicId: z.number().positive().describe("The public ID of the story") }, async ({ storyPublicId }) => await tools.assignCurrentUserAsOwner(storyPublicId));
1198
1217
  server$1.tool("unassign-current-user-as-owner", "Unassign the current user as the owner of a story", { storyPublicId: z.number().positive().describe("The public ID of the story") }, async ({ storyPublicId }) => await tools.unassignCurrentUserAsOwner(storyPublicId));
1199
1218
  server$1.tool("create-story-comment", "Create a comment on a story", {
@@ -1205,6 +1224,13 @@ The story will be added to the default state for the workflow.
1205
1224
  taskDescription: z.string().min(1).describe("The description of the task"),
1206
1225
  taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task")
1207
1226
  }, async (params) => await tools.addTaskToStory(params));
1227
+ server$1.tool("update-task", "Update a task in a story", {
1228
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1229
+ taskPublicId: z.number().positive().describe("The public ID of the task"),
1230
+ taskDescription: z.string().optional().describe("The description of the task"),
1231
+ taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task"),
1232
+ isCompleted: z.boolean().optional().describe("Whether the task is completed or not")
1233
+ }, async (params) => await tools.updateTask(params));
1208
1234
  server$1.tool("add-relation-to-story", "Add a story relationship to a story", {
1209
1235
  storyPublicId: z.number().positive().describe("The public ID of the story"),
1210
1236
  relatedStoryPublicId: z.number().positive().describe("The public ID of the related story"),
@@ -1216,13 +1242,6 @@ The story will be added to the default state for the workflow.
1216
1242
  "duplicated by"
1217
1243
  ]).optional().default("relates to").describe("The type of relationship")
1218
1244
  }, async (params) => await tools.addRelationToStory(params));
1219
- server$1.tool("update-task", "Update a task in a story", {
1220
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1221
- taskPublicId: z.number().positive().describe("The public ID of the task"),
1222
- taskDescription: z.string().optional().describe("The description of the task"),
1223
- taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task"),
1224
- isCompleted: z.boolean().optional().describe("Whether the task is completed or not")
1225
- }, async (params) => await tools.updateTask(params));
1226
1245
  server$1.tool("add-external-link-to-story", "Add an external link to a Shortcut story", {
1227
1246
  storyPublicId: z.number().positive().describe("The public ID of the story"),
1228
1247
  externalLink: z.string().url().max(2048).describe("The external link URL to add")
@@ -1326,6 +1345,15 @@ The story will be added to the default state for the workflow.
1326
1345
  const updatedStory = await this.client.updateStory(storyPublicId, updateParams);
1327
1346
  return this.toResult(`Updated story sc-${storyPublicId}. Story URL: ${updatedStory.app_url}`);
1328
1347
  }
1348
+ async uploadFileToStory(storyPublicId, filePath) {
1349
+ if (!storyPublicId) throw new Error("Story public ID is required");
1350
+ if (!filePath) throw new Error("File path is required");
1351
+ const story = await this.client.getStory(storyPublicId);
1352
+ if (!story) throw new Error(`Failed to retrieve Shortcut story with public ID: ${storyPublicId}`);
1353
+ const uploadedFile = await this.client.uploadFile(storyPublicId, filePath);
1354
+ if (!uploadedFile) throw new Error(`Failed to upload file to story sc-${storyPublicId}`);
1355
+ return this.toResult(`Uploaded file "${uploadedFile.name}" to story sc-${storyPublicId}. File ID is: ${uploadedFile.id}`);
1356
+ }
1329
1357
  async addTaskToStory({ storyPublicId, taskDescription, taskOwnerIds }) {
1330
1358
  if (!storyPublicId) throw new Error("Story public ID is required");
1331
1359
  if (!taskDescription) throw new Error("Task description is required");
@@ -1433,7 +1461,7 @@ var UserTools = class UserTools extends BaseTools {
1433
1461
  const tools = new UserTools(client$1);
1434
1462
  server$1.tool("get-current-user", "Get the current user", async () => await tools.getCurrentUser());
1435
1463
  server$1.tool("get-current-user-teams", "Get a list of teams where the current user is a member", async () => await tools.getCurrentUserTeams());
1436
- server$1.tool("list-members", "Get all members", async () => await tools.listMembers());
1464
+ server$1.tool("list-users", "Get all users", async () => await tools.listMembers());
1437
1465
  return tools;
1438
1466
  }
1439
1467
  async getCurrentUser() {
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "modelcontextprotocol"
13
13
  ],
14
14
  "license": "MIT",
15
- "version": "0.11.2",
15
+ "version": "0.12.1",
16
16
  "type": "module",
17
17
  "main": "dist/index.js",
18
18
  "bin": {