@shortcut/mcp 0.12.0 → 0.13.0

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 +86 -0
  2. package/dist/index.js +127 -122
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -125,6 +125,92 @@ 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
+ - **set-story-external-links** - Replace all external links on a story with a new set of links
147
+ - **get-stories-by-external-link** - Find all stories that contain a specific external link
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
+
190
+ ## Read-only mode
191
+
192
+ You can run the MCP server in read-only mode by setting the `SHORTCUT_READONLY` environment variable to `true`. This will disable all tools that modify data in Shortcut.
193
+
194
+ Example:
195
+
196
+ ```json
197
+ {
198
+ "mcpServers": {
199
+ "shortcut": {
200
+ "command": "npx",
201
+ "args": [
202
+ "-y",
203
+ "@shortcut/mcp@latest"
204
+ ],
205
+ "env": {
206
+ "SHORTCUT_API_TOKEN": "<YOUR_SHORTCUT_API_TOKEN>",
207
+ "SHORTCUT_READONLY": "true"
208
+ }
209
+ }
210
+ }
211
+ }
212
+ ```
213
+
128
214
  ## Issues and Troubleshooting
129
215
 
130
216
  Before doing anything else, please make sure you are running the latest version!
package/dist/index.js CHANGED
@@ -450,7 +450,7 @@ var ShortcutClientWrapper = class {
450
450
  //#endregion
451
451
  //#region package.json
452
452
  var name = "@shortcut/mcp";
453
- var version = "0.12.0";
453
+ var version = "0.13.0";
454
454
 
455
455
  //#endregion
456
456
  //#region src/tools/base.ts
@@ -458,8 +458,9 @@ var version = "0.12.0";
458
458
  * Base class for all tools.
459
459
  */
460
460
  var BaseTools = class {
461
- constructor(client$1) {
461
+ constructor(client$1, isReadonly$1 = false) {
462
462
  this.client = client$1;
463
+ this.isReadonly = isReadonly$1;
463
464
  }
464
465
  renameEntityProps(entity) {
465
466
  if (!entity || typeof entity !== "object") return entity;
@@ -791,12 +792,13 @@ var BaseTools = class {
791
792
  //#endregion
792
793
  //#region src/tools/documents.ts
793
794
  var DocumentTools = class DocumentTools extends BaseTools {
794
- static create(client$1, server$1) {
795
- const tools = new DocumentTools(client$1);
796
- server$1.tool("create-document", "Create a new document in Shortcut with a title and content. Returns the document's id, title, and app_url. Note: Use HTML markup for the content (e.g., <p>, <h1>, <ul>, <strong>) rather than Markdown.", {
795
+ static create(client$1, server$1, isReadonly$1 = false) {
796
+ const tools = new DocumentTools(client$1, isReadonly$1);
797
+ if (!isReadonly$1) server$1.tool("create-document", "Create a new document in Shortcut with a title and content. Returns the document's id, title, and app_url. Note: Use HTML markup for the content (e.g., <p>, <h1>, <ul>, <strong>) rather than Markdown.", {
797
798
  title: z.string().max(256).describe("The title for the new document (max 256 characters)"),
798
799
  content: z.string().describe("The content for the new document in HTML format (e.g., <p>Hello</p>, <h1>Title</h1>, <ul><li>Item</li></ul>)")
799
800
  }, async ({ title, content }) => await tools.createDocument(title, content));
801
+ return tools;
800
802
  }
801
803
  async createDocument(title, content) {
802
804
  try {
@@ -874,8 +876,8 @@ const user = (field) => z.string().optional().describe(`Find entities where the
874
876
  //#endregion
875
877
  //#region src/tools/epics.ts
876
878
  var EpicTools = class EpicTools extends BaseTools {
877
- static create(client$1, server$1) {
878
- const tools = new EpicTools(client$1);
879
+ static create(client$1, server$1, isReadonly$1 = false) {
880
+ const tools = new EpicTools(client$1, isReadonly$1);
879
881
  server$1.tool("get-epic", "Get a Shortcut epic by public ID", {
880
882
  epicPublicId: z.number().positive().describe("The public ID of the epic to get"),
881
883
  full: z.boolean().optional().default(false).describe("True to return all epic fields from the API. False to return a slim version that excludes uncommon fields")
@@ -909,7 +911,7 @@ var EpicTools = class EpicTools extends BaseTools {
909
911
  completed: date(),
910
912
  due: date()
911
913
  }, async ({ nextPageToken,...params }) => await tools.searchEpics(params, nextPageToken));
912
- server$1.tool("create-epic", "Create a new Shortcut epic.", {
914
+ if (!isReadonly$1) server$1.tool("create-epic", "Create a new Shortcut epic.", {
913
915
  name: z.string().describe("The name of the epic"),
914
916
  owner: z.string().optional().describe("The user ID of the owner of the epic"),
915
917
  description: z.string().optional().describe("A description of the epic"),
@@ -944,8 +946,8 @@ var EpicTools = class EpicTools extends BaseTools {
944
946
  //#endregion
945
947
  //#region src/tools/iterations.ts
946
948
  var IterationTools = class IterationTools extends BaseTools {
947
- static create(client$1, server$1) {
948
- const tools = new IterationTools(client$1);
949
+ static create(client$1, server$1, isReadonly$1 = false) {
950
+ const tools = new IterationTools(client$1, isReadonly$1);
949
951
  server$1.tool("get-iteration-stories", "Get stories in a specific iteration by iteration public ID", {
950
952
  iterationPublicId: z.number().positive().describe("The public ID of the iteration"),
951
953
  includeStoryDescriptions: z.boolean().optional().default(false).describe("Indicate whether story descriptions should be included. Including descriptions may take longer and will increase the size of the response.")
@@ -970,7 +972,7 @@ var IterationTools = class IterationTools extends BaseTools {
970
972
  startDate: date(),
971
973
  endDate: date()
972
974
  }, async ({ nextPageToken,...params }) => await tools.searchIterations(params, nextPageToken));
973
- server$1.tool("create-iteration", "Create a new Shortcut iteration", {
975
+ if (!isReadonly$1) server$1.tool("create-iteration", "Create a new Shortcut iteration", {
974
976
  name: z.string().describe("The name of the iteration"),
975
977
  startDate: z.string().describe("The start date of the iteration in YYYY-MM-DD format"),
976
978
  endDate: z.string().describe("The end date of the iteration in YYYY-MM-DD format"),
@@ -1055,8 +1057,8 @@ var IterationTools = class IterationTools extends BaseTools {
1055
1057
  //#endregion
1056
1058
  //#region src/tools/objectives.ts
1057
1059
  var ObjectiveTools = class ObjectiveTools extends BaseTools {
1058
- static create(client$1, server$1) {
1059
- const tools = new ObjectiveTools(client$1);
1060
+ static create(client$1, server$1, isReadonly$1 = false) {
1061
+ const tools = new ObjectiveTools(client$1, isReadonly$1);
1060
1062
  server$1.tool("get-objective", "Get a Shortcut objective by public ID", {
1061
1063
  objectivePublicId: z.number().positive().describe("The public ID of the objective to get"),
1062
1064
  full: z.boolean().optional().default(false).describe("True to return all objective fields from the API. False to return a slim version that excludes uncommon fields")
@@ -1103,8 +1105,8 @@ var ObjectiveTools = class ObjectiveTools extends BaseTools {
1103
1105
  //#endregion
1104
1106
  //#region src/tools/stories.ts
1105
1107
  var StoryTools = class StoryTools extends BaseTools {
1106
- static create(client$1, server$1) {
1107
- const tools = new StoryTools(client$1);
1108
+ static create(client$1, server$1, isReadonly$1 = false) {
1109
+ const tools = new StoryTools(client$1, isReadonly$1);
1108
1110
  server$1.tool("get-story", "Get a Shortcut story by public ID", {
1109
1111
  storyPublicId: z.number().positive().describe("The public ID of the story to get"),
1110
1112
  full: z.boolean().optional().default(false).describe("True to return all story fields from the API. False to return a slim version that excludes uncommon fields")
@@ -1161,100 +1163,102 @@ var StoryTools = class StoryTools extends BaseTools {
1161
1163
  due: date()
1162
1164
  }, async ({ nextPageToken,...params }) => await tools.searchStories(params, nextPageToken));
1163
1165
  server$1.tool("get-story-branch-name", "Get a valid branch name for a specific story.", { storyPublicId: z.number().positive().describe("The public Id of the story") }, async ({ storyPublicId }) => await tools.getStoryBranchName(storyPublicId));
1164
- server$1.tool("create-story", `Create a new Shortcut story.
1166
+ if (!isReadonly$1) {
1167
+ server$1.tool("create-story", `Create a new Shortcut story.
1165
1168
  Name is required, and either a Team or Workflow must be specified:
1166
1169
  - If only Team is specified, we will use the default workflow for that team.
1167
1170
  - If Workflow is specified, it will be used regardless of Team.
1168
1171
  The story will be added to the default state for the workflow.
1169
1172
  `, {
1170
- name: z.string().min(1).max(512).describe("The name of the story. Required."),
1171
- description: z.string().max(1e4).optional().describe("The description of the story"),
1172
- type: z.enum([
1173
- "feature",
1174
- "bug",
1175
- "chore"
1176
- ]).default("feature").describe("The type of the story"),
1177
- owner: z.string().optional().describe("The user id of the owner of the story"),
1178
- epic: z.number().optional().describe("The epic id of the epic the story belongs to"),
1179
- iteration: z.number().optional().describe("The iteration id of the iteration the story belongs to"),
1180
- team: z.string().optional().describe("The team ID or mention name of the team the story belongs to. Required unless a workflow is specified."),
1181
- workflow: z.number().optional().describe("The workflow ID to add the story to. Required unless a team is specified.")
1182
- }, async ({ name: name$1, description, type, owner, epic, iteration, team, workflow }) => await tools.createStory({
1183
- name: name$1,
1184
- description,
1185
- type,
1186
- owner,
1187
- epic,
1188
- iteration,
1189
- team,
1190
- workflow
1191
- }));
1192
- server$1.tool("update-story", "Update an existing Shortcut story. Only provide fields you want to update. The story public ID will always be included in updates.", {
1193
- storyPublicId: z.number().positive().describe("The public ID of the story to update"),
1194
- name: z.string().max(512).optional().describe("The name of the story"),
1195
- description: z.string().max(1e4).optional().describe("The description of the story"),
1196
- type: z.enum([
1197
- "feature",
1198
- "bug",
1199
- "chore"
1200
- ]).optional().describe("The type of the story"),
1201
- epic: z.number().nullable().optional().describe("The epic id of the epic the story belongs to, or null to unset"),
1202
- estimate: z.number().nullable().optional().describe("The point estimate of the story, or null to unset"),
1203
- iteration: z.number().nullable().optional().describe("The iteration id of the iteration the story belongs to, or null to unset"),
1204
- owner_ids: z.array(z.string()).optional().describe("Array of user UUIDs to assign as owners of the story"),
1205
- workflow_state_id: z.number().optional().describe("The workflow state ID to move the story to"),
1206
- labels: z.array(z.object({
1207
- name: z.string().describe("The name of the label"),
1208
- color: z.string().optional().describe("The color of the label"),
1209
- description: z.string().optional().describe("The description of the label")
1210
- })).optional().describe("Labels to assign to the story")
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));
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));
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));
1218
- server$1.tool("create-story-comment", "Create a comment on a story", {
1219
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1220
- text: z.string().min(1).describe("The text of the comment")
1221
- }, async (params) => await tools.createStoryComment(params));
1222
- server$1.tool("add-task-to-story", "Add a task to a story", {
1223
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1224
- taskDescription: z.string().min(1).describe("The description of the task"),
1225
- taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task")
1226
- }, async (params) => await tools.addTaskToStory(params));
1227
- server$1.tool("add-relation-to-story", "Add a story relationship to a story", {
1228
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1229
- relatedStoryPublicId: z.number().positive().describe("The public ID of the related story"),
1230
- relationshipType: z.enum([
1231
- "relates to",
1232
- "blocks",
1233
- "blocked by",
1234
- "duplicates",
1235
- "duplicated by"
1236
- ]).optional().default("relates to").describe("The type of relationship")
1237
- }, async (params) => await tools.addRelationToStory(params));
1238
- server$1.tool("update-task", "Update a task in a story", {
1239
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1240
- taskPublicId: z.number().positive().describe("The public ID of the task"),
1241
- taskDescription: z.string().optional().describe("The description of the task"),
1242
- taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task"),
1243
- isCompleted: z.boolean().optional().describe("Whether the task is completed or not")
1244
- }, async (params) => await tools.updateTask(params));
1245
- server$1.tool("add-external-link-to-story", "Add an external link to a Shortcut story", {
1246
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1247
- externalLink: z.string().url().max(2048).describe("The external link URL to add")
1248
- }, async ({ storyPublicId, externalLink }) => await tools.addExternalLinkToStory(storyPublicId, externalLink));
1249
- server$1.tool("remove-external-link-from-story", "Remove an external link from a Shortcut story", {
1250
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1251
- externalLink: z.string().url().max(2048).describe("The external link URL to remove")
1252
- }, async ({ storyPublicId, externalLink }) => await tools.removeExternalLinkFromStory(storyPublicId, externalLink));
1173
+ name: z.string().min(1).max(512).describe("The name of the story. Required."),
1174
+ description: z.string().max(1e4).optional().describe("The description of the story"),
1175
+ type: z.enum([
1176
+ "feature",
1177
+ "bug",
1178
+ "chore"
1179
+ ]).default("feature").describe("The type of the story"),
1180
+ owner: z.string().optional().describe("The user id of the owner of the story"),
1181
+ epic: z.number().optional().describe("The epic id of the epic the story belongs to"),
1182
+ iteration: z.number().optional().describe("The iteration id of the iteration the story belongs to"),
1183
+ team: z.string().optional().describe("The team ID or mention name of the team the story belongs to. Required unless a workflow is specified."),
1184
+ workflow: z.number().optional().describe("The workflow ID to add the story to. Required unless a team is specified.")
1185
+ }, async ({ name: name$1, description, type, owner, epic, iteration, team, workflow }) => await tools.createStory({
1186
+ name: name$1,
1187
+ description,
1188
+ type,
1189
+ owner,
1190
+ epic,
1191
+ iteration,
1192
+ team,
1193
+ workflow
1194
+ }));
1195
+ server$1.tool("update-story", "Update an existing Shortcut story. Only provide fields you want to update. The story public ID will always be included in updates.", {
1196
+ storyPublicId: z.number().positive().describe("The public ID of the story to update"),
1197
+ name: z.string().max(512).optional().describe("The name of the story"),
1198
+ description: z.string().max(1e4).optional().describe("The description of the story"),
1199
+ type: z.enum([
1200
+ "feature",
1201
+ "bug",
1202
+ "chore"
1203
+ ]).optional().describe("The type of the story"),
1204
+ epic: z.number().nullable().optional().describe("The epic id of the epic the story belongs to, or null to unset"),
1205
+ estimate: z.number().nullable().optional().describe("The point estimate of the story, or null to unset"),
1206
+ iteration: z.number().nullable().optional().describe("The iteration id of the iteration the story belongs to, or null to unset"),
1207
+ owner_ids: z.array(z.string()).optional().describe("Array of user UUIDs to assign as owners of the story"),
1208
+ workflow_state_id: z.number().optional().describe("The workflow state ID to move the story to"),
1209
+ labels: z.array(z.object({
1210
+ name: z.string().describe("The name of the label"),
1211
+ color: z.string().optional().describe("The color of the label"),
1212
+ description: z.string().optional().describe("The description of the label")
1213
+ })).optional().describe("Labels to assign to the story")
1214
+ }, async (params) => await tools.updateStory(params));
1215
+ server$1.tool("upload-file-to-story", "Upload a file and link it to a story.", {
1216
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1217
+ filePath: z.string().describe("The path to the file to upload")
1218
+ }, async ({ storyPublicId, filePath }) => await tools.uploadFileToStory(storyPublicId, filePath));
1219
+ 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));
1220
+ 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));
1221
+ server$1.tool("create-story-comment", "Create a comment on a story", {
1222
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1223
+ text: z.string().min(1).describe("The text of the comment")
1224
+ }, async (params) => await tools.createStoryComment(params));
1225
+ server$1.tool("add-task-to-story", "Add a task to a story", {
1226
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1227
+ taskDescription: z.string().min(1).describe("The description of the task"),
1228
+ taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task")
1229
+ }, async (params) => await tools.addTaskToStory(params));
1230
+ server$1.tool("update-task", "Update a task in a story", {
1231
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1232
+ taskPublicId: z.number().positive().describe("The public ID of the task"),
1233
+ taskDescription: z.string().optional().describe("The description of the task"),
1234
+ taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task"),
1235
+ isCompleted: z.boolean().optional().describe("Whether the task is completed or not")
1236
+ }, async (params) => await tools.updateTask(params));
1237
+ server$1.tool("add-relation-to-story", "Add a story relationship to a story", {
1238
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1239
+ relatedStoryPublicId: z.number().positive().describe("The public ID of the related story"),
1240
+ relationshipType: z.enum([
1241
+ "relates to",
1242
+ "blocks",
1243
+ "blocked by",
1244
+ "duplicates",
1245
+ "duplicated by"
1246
+ ]).optional().default("relates to").describe("The type of relationship")
1247
+ }, async (params) => await tools.addRelationToStory(params));
1248
+ server$1.tool("add-external-link-to-story", "Add an external link to a Shortcut story", {
1249
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1250
+ externalLink: z.string().url().max(2048).describe("The external link URL to add")
1251
+ }, async ({ storyPublicId, externalLink }) => await tools.addExternalLinkToStory(storyPublicId, externalLink));
1252
+ server$1.tool("remove-external-link-from-story", "Remove an external link from a Shortcut story", {
1253
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1254
+ externalLink: z.string().url().max(2048).describe("The external link URL to remove")
1255
+ }, async ({ storyPublicId, externalLink }) => await tools.removeExternalLinkFromStory(storyPublicId, externalLink));
1256
+ server$1.tool("set-story-external-links", "Replace all external links on a story with a new set of links", {
1257
+ storyPublicId: z.number().positive().describe("The public ID of the story"),
1258
+ externalLinks: z.array(z.string().url().max(2048)).describe("Array of external link URLs to set (replaces all existing links)")
1259
+ }, async ({ storyPublicId, externalLinks }) => await tools.setStoryExternalLinks(storyPublicId, externalLinks));
1260
+ }
1253
1261
  server$1.tool("get-stories-by-external-link", "Find all stories that contain a specific external link", { externalLink: z.string().url().max(2048).describe("The external link URL to search for") }, async ({ externalLink }) => await tools.getStoriesByExternalLink(externalLink));
1254
- server$1.tool("set-story-external-links", "Replace all external links on a story with a new set of links", {
1255
- storyPublicId: z.number().positive().describe("The public ID of the story"),
1256
- externalLinks: z.array(z.string().url().max(2048)).describe("Array of external link URLs to set (replaces all existing links)")
1257
- }, async ({ storyPublicId, externalLinks }) => await tools.setStoryExternalLinks(storyPublicId, externalLinks));
1258
1262
  return tools;
1259
1263
  }
1260
1264
  async assignCurrentUserAsOwner(storyPublicId) {
@@ -1433,8 +1437,8 @@ The story will be added to the default state for the workflow.
1433
1437
  //#endregion
1434
1438
  //#region src/tools/teams.ts
1435
1439
  var TeamTools = class TeamTools extends BaseTools {
1436
- static create(client$1, server$1) {
1437
- const tools = new TeamTools(client$1);
1440
+ static create(client$1, server$1, isReadonly$1 = false) {
1441
+ const tools = new TeamTools(client$1, isReadonly$1);
1438
1442
  server$1.tool("get-team", "Get a Shortcut team by public ID", {
1439
1443
  teamPublicId: z.string().describe("The public ID of the team to get"),
1440
1444
  full: z.boolean().optional().default(false).describe("True to return all team fields from the API. False to return a slim version that excludes uncommon fields")
@@ -1457,11 +1461,11 @@ var TeamTools = class TeamTools extends BaseTools {
1457
1461
  //#endregion
1458
1462
  //#region src/tools/user.ts
1459
1463
  var UserTools = class UserTools extends BaseTools {
1460
- static create(client$1, server$1) {
1461
- const tools = new UserTools(client$1);
1464
+ static create(client$1, server$1, isReadonly$1 = false) {
1465
+ const tools = new UserTools(client$1, isReadonly$1);
1462
1466
  server$1.tool("get-current-user", "Get the current user", async () => await tools.getCurrentUser());
1463
1467
  server$1.tool("get-current-user-teams", "Get a list of teams where the current user is a member", async () => await tools.getCurrentUserTeams());
1464
- server$1.tool("list-members", "Get all members", async () => await tools.listMembers());
1468
+ server$1.tool("list-users", "Get all users", async () => await tools.listMembers());
1465
1469
  return tools;
1466
1470
  }
1467
1471
  async getCurrentUser() {
@@ -1490,8 +1494,8 @@ var UserTools = class UserTools extends BaseTools {
1490
1494
  //#endregion
1491
1495
  //#region src/tools/workflows.ts
1492
1496
  var WorkflowTools = class WorkflowTools extends BaseTools {
1493
- static create(client$1, server$1) {
1494
- const tools = new WorkflowTools(client$1);
1497
+ static create(client$1, server$1, isReadonly$1 = false) {
1498
+ const tools = new WorkflowTools(client$1, isReadonly$1);
1495
1499
  server$1.tool("get-default-workflow", "Get the default workflow for a specific team or the global default if no team is specified.", { teamPublicId: z.string().optional().describe("The public ID of the team to get the default workflow for.") }, async ({ teamPublicId }) => await tools.getDefaultWorkflow(teamPublicId));
1496
1500
  server$1.tool("get-workflow", "Get a Shortcut workflow by public ID", {
1497
1501
  workflowPublicId: z.number().positive().describe("The public ID of the workflow to get"),
@@ -1530,10 +1534,11 @@ var WorkflowTools = class WorkflowTools extends BaseTools {
1530
1534
  //#endregion
1531
1535
  //#region src/server.ts
1532
1536
  let apiToken = process.env.SHORTCUT_API_TOKEN;
1533
- if (process.argv.length === 3) {
1534
- const [name$1, token] = String(process.argv[2]).split("=");
1535
- if (name$1 === "SHORTCUT_API_TOKEN") apiToken = token;
1536
- }
1537
+ let isReadonly = process.env.SHORTCUT_READONLY === "true";
1538
+ if (process.argv.length >= 3) process.argv.slice(2).map((arg) => arg.split("=")).forEach(([name$1, value]) => {
1539
+ if (name$1 === "SHORTCUT_API_TOKEN") apiToken = value;
1540
+ if (name$1 === "SHORTCUT_READONLY") isReadonly = value === "true";
1541
+ });
1537
1542
  if (!apiToken) {
1538
1543
  console.error("SHORTCUT_API_TOKEN is required");
1539
1544
  process.exit(1);
@@ -1543,14 +1548,14 @@ const server = new McpServer({
1543
1548
  version
1544
1549
  });
1545
1550
  const client = new ShortcutClientWrapper(new ShortcutClient(apiToken));
1546
- UserTools.create(client, server);
1547
- StoryTools.create(client, server);
1548
- IterationTools.create(client, server);
1549
- EpicTools.create(client, server);
1550
- ObjectiveTools.create(client, server);
1551
- TeamTools.create(client, server);
1552
- WorkflowTools.create(client, server);
1553
- DocumentTools.create(client, server);
1551
+ UserTools.create(client, server, isReadonly);
1552
+ StoryTools.create(client, server, isReadonly);
1553
+ IterationTools.create(client, server, isReadonly);
1554
+ EpicTools.create(client, server, isReadonly);
1555
+ ObjectiveTools.create(client, server, isReadonly);
1556
+ TeamTools.create(client, server, isReadonly);
1557
+ WorkflowTools.create(client, server, isReadonly);
1558
+ DocumentTools.create(client, server, isReadonly);
1554
1559
  async function startServer() {
1555
1560
  try {
1556
1561
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "modelcontextprotocol"
13
13
  ],
14
14
  "license": "MIT",
15
- "version": "0.12.0",
15
+ "version": "0.13.0",
16
16
  "type": "module",
17
17
  "main": "dist/index.js",
18
18
  "bin": {