@shortcut/mcp 0.14.0 → 0.15.2
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.
- package/README.md +44 -39
- package/dist/index.js +177 -150
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -129,67 +129,72 @@ Or you can edit the local JSON file directly:
|
|
|
129
129
|
|
|
130
130
|
### Stories
|
|
131
131
|
|
|
132
|
-
- **get-
|
|
133
|
-
- **search
|
|
134
|
-
- **get-
|
|
135
|
-
- **create
|
|
136
|
-
- **update
|
|
137
|
-
- **upload-file
|
|
138
|
-
- **assign-current-user
|
|
139
|
-
- **unassign-current-user
|
|
140
|
-
- **create-
|
|
141
|
-
- **add-task
|
|
142
|
-
- **update-task** - Update a task in a story
|
|
143
|
-
- **add-relation
|
|
144
|
-
- **add-external-link
|
|
145
|
-
- **remove-external-link
|
|
146
|
-
- **set-
|
|
147
|
-
- **get-
|
|
132
|
+
- **stories-get-by-id** - Get a single Shortcut story by ID
|
|
133
|
+
- **stories-search** - Find Shortcut stories with filtering and search options
|
|
134
|
+
- **stories-get-branch-name** - Get the recommended branch name (based on workspace settings) for a specific story.
|
|
135
|
+
- **stories-create** - Create a new Shortcut story
|
|
136
|
+
- **stories-update** - Update an existing Shortcut story
|
|
137
|
+
- **stories-upload-file** - Upload a file and link it to a story
|
|
138
|
+
- **stories-assign-current-user** - Assign the current user as the owner of a story
|
|
139
|
+
- **stories-unassign-current-user** - Unassign the current user as the owner of a story
|
|
140
|
+
- **stories-create-comment** - Create a comment on a story
|
|
141
|
+
- **stories-add-task** - Add a task to a story
|
|
142
|
+
- **stories-update-task** - Update a task in a story
|
|
143
|
+
- **stories-add-relation** - Add a story relationship (relates to, blocks, duplicates, etc.)
|
|
144
|
+
- **stories-add-external-link** - Add an external link to a Shortcut story
|
|
145
|
+
- **stories-remove-external-link** - Remove an external link from a Shortcut story
|
|
146
|
+
- **stories-set-external-links** - Replace all external links on a story with a new set of links
|
|
147
|
+
- **stories-get-by-external-link** - Find all stories that contain a specific external link
|
|
148
148
|
|
|
149
149
|
### Epics
|
|
150
150
|
|
|
151
|
-
- **get-
|
|
152
|
-
- **search
|
|
153
|
-
- **create
|
|
151
|
+
- **epics-get-by-id** - Get a Shortcut epic by ID
|
|
152
|
+
- **epics-search** - Find Shortcut epics with filtering and search options
|
|
153
|
+
- **epics-create** - Create a new Shortcut epic
|
|
154
154
|
|
|
155
155
|
### Iterations
|
|
156
156
|
|
|
157
|
-
- **get-
|
|
158
|
-
- **get-
|
|
159
|
-
- **search
|
|
160
|
-
- **create
|
|
161
|
-
- **get-active
|
|
162
|
-
- **get-upcoming
|
|
157
|
+
- **iterations-get-stories** - Get stories in a specific iteration by iteration ID
|
|
158
|
+
- **iterations-get-by-id** - Get a Shortcut iteration by ID
|
|
159
|
+
- **iterations-search** - Find Shortcut iterations with filtering and search options
|
|
160
|
+
- **iterations-create** - Create a new Shortcut iteration with start/end dates
|
|
161
|
+
- **iterations-get-active** - Get active iterations for the current user based on team memberships
|
|
162
|
+
- **iterations-get-upcoming** - Get upcoming iterations for the current user based on team memberships
|
|
163
163
|
|
|
164
164
|
### Objectives
|
|
165
165
|
|
|
166
|
-
- **get-
|
|
167
|
-
- **search
|
|
166
|
+
- **objectives-get-by-id** - Get a Shortcut objective by ID
|
|
167
|
+
- **objectives-search** - Find Shortcut objectives with filtering and search options
|
|
168
168
|
|
|
169
169
|
### Teams
|
|
170
170
|
|
|
171
|
-
- **get-
|
|
172
|
-
- **list
|
|
171
|
+
- **teams-get-by-id** - Get a Shortcut team by ID
|
|
172
|
+
- **teams-list** - List all Shortcut teams
|
|
173
173
|
|
|
174
174
|
### Workflows
|
|
175
175
|
|
|
176
|
-
- **get-default
|
|
177
|
-
- **get-
|
|
178
|
-
- **list
|
|
176
|
+
- **workflows-get-default** - Get the default workflow for a specific team or the workspace default
|
|
177
|
+
- **workflows-get-by-id** - Get a Shortcut workflow by ID
|
|
178
|
+
- **workflows-list** - List all Shortcut workflows
|
|
179
179
|
|
|
180
180
|
### Users
|
|
181
181
|
|
|
182
|
-
- **get-current
|
|
183
|
-
- **get-current-
|
|
184
|
-
- **list
|
|
182
|
+
- **users-get-current** - Get the current user information
|
|
183
|
+
- **users-get-current-teams** - Get a list of teams where the current user is a member
|
|
184
|
+
- **users-list** - Get all workspace users
|
|
185
185
|
|
|
186
186
|
### Documents
|
|
187
187
|
|
|
188
|
-
- **create
|
|
188
|
+
- **documents-create** - Create a new document in Shortcut with HTML content
|
|
189
189
|
|
|
190
190
|
## Limit tools
|
|
191
191
|
|
|
192
|
-
You can limit the tools available to the LLM by setting the `SHORTCUT_TOOLS` environment variable to a comma-separated list
|
|
192
|
+
You can limit the tools available to the LLM by setting the `SHORTCUT_TOOLS` environment variable to a comma-separated list.
|
|
193
|
+
|
|
194
|
+
- Tools can be limited by entity type by just adding the entity, eg `stories` or `epics`.
|
|
195
|
+
- Individual tools can also be limitied by their full name, eg `stories-get-by-id` or `epics-search`.
|
|
196
|
+
|
|
197
|
+
By default, all tools are enabled.
|
|
193
198
|
|
|
194
199
|
Example:
|
|
195
200
|
|
|
@@ -204,14 +209,14 @@ Example:
|
|
|
204
209
|
],
|
|
205
210
|
"env": {
|
|
206
211
|
"SHORTCUT_API_TOKEN": "<YOUR_SHORTCUT_API_TOKEN>",
|
|
207
|
-
"SHORTCUT_TOOLS": "stories,epics"
|
|
212
|
+
"SHORTCUT_TOOLS": "stories,epics,iterations-create"
|
|
208
213
|
}
|
|
209
214
|
}
|
|
210
215
|
}
|
|
211
216
|
}
|
|
212
217
|
```
|
|
213
218
|
|
|
214
|
-
The following values are accepted:
|
|
219
|
+
The following values are accepted in addition to the full tool names listed above under [Available Tools](#available-tools):
|
|
215
220
|
|
|
216
221
|
- `users`
|
|
217
222
|
- `stories`
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { ShortcutClient } from "@shortcut/client";
|
|
5
4
|
import { File } from "node:buffer";
|
|
6
5
|
import { readFileSync } from "node:fs";
|
|
7
6
|
import { basename } from "node:path";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
//#region src/client/cache.ts
|
|
@@ -450,7 +450,40 @@ var ShortcutClientWrapper = class {
|
|
|
450
450
|
//#endregion
|
|
451
451
|
//#region package.json
|
|
452
452
|
var name = "@shortcut/mcp";
|
|
453
|
-
var version = "0.
|
|
453
|
+
var version = "0.15.2";
|
|
454
|
+
|
|
455
|
+
//#endregion
|
|
456
|
+
//#region src/mcp/CustomMcpServer.ts
|
|
457
|
+
var CustomMcpServer = class extends McpServer {
|
|
458
|
+
readonly;
|
|
459
|
+
tools;
|
|
460
|
+
constructor({ readonly, tools }) {
|
|
461
|
+
super({
|
|
462
|
+
name,
|
|
463
|
+
version
|
|
464
|
+
});
|
|
465
|
+
this.readonly = readonly;
|
|
466
|
+
this.tools = new Set(tools || []);
|
|
467
|
+
}
|
|
468
|
+
shouldAddTool(name$1) {
|
|
469
|
+
if (!this.tools.size) return true;
|
|
470
|
+
const [entityType] = name$1.split("-");
|
|
471
|
+
if (this.tools.has(entityType) || this.tools.has(name$1)) return true;
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
addToolWithWriteAccess(...args) {
|
|
475
|
+
if (this.readonly) return null;
|
|
476
|
+
if (!this.shouldAddTool(args[0])) return null;
|
|
477
|
+
return super.tool(...args);
|
|
478
|
+
}
|
|
479
|
+
addToolWithReadAccess(...args) {
|
|
480
|
+
if (!this.shouldAddTool(args[0])) return null;
|
|
481
|
+
return super.tool(...args);
|
|
482
|
+
}
|
|
483
|
+
tool() {
|
|
484
|
+
throw new Error("Call addToolWithReadAccess or addToolWithWriteAccess instead.");
|
|
485
|
+
}
|
|
486
|
+
};
|
|
454
487
|
|
|
455
488
|
//#endregion
|
|
456
489
|
//#region src/tools/base.ts
|
|
@@ -458,9 +491,8 @@ var version = "0.14.0";
|
|
|
458
491
|
* Base class for all tools.
|
|
459
492
|
*/
|
|
460
493
|
var BaseTools = class {
|
|
461
|
-
constructor(client$1
|
|
494
|
+
constructor(client$1) {
|
|
462
495
|
this.client = client$1;
|
|
463
|
-
this.isReadonly = isReadonly$1;
|
|
464
496
|
}
|
|
465
497
|
renameEntityProps(entity) {
|
|
466
498
|
if (!entity || typeof entity !== "object") return entity;
|
|
@@ -792,9 +824,9 @@ var BaseTools = class {
|
|
|
792
824
|
//#endregion
|
|
793
825
|
//#region src/tools/documents.ts
|
|
794
826
|
var DocumentTools = class DocumentTools extends BaseTools {
|
|
795
|
-
static create(client$1, server$1
|
|
796
|
-
const tools = new DocumentTools(client$1
|
|
797
|
-
|
|
827
|
+
static create(client$1, server$1) {
|
|
828
|
+
const tools = new DocumentTools(client$1);
|
|
829
|
+
server$1.addToolWithWriteAccess("documents-create", "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.", {
|
|
798
830
|
title: z.string().max(256).describe("The title for the new document (max 256 characters)"),
|
|
799
831
|
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>)")
|
|
800
832
|
}, async ({ title, content }) => await tools.createDocument(title, content));
|
|
@@ -876,13 +908,13 @@ const user = (field) => z.string().optional().describe(`Find entities where the
|
|
|
876
908
|
//#endregion
|
|
877
909
|
//#region src/tools/epics.ts
|
|
878
910
|
var EpicTools = class EpicTools extends BaseTools {
|
|
879
|
-
static create(client$1, server$1
|
|
880
|
-
const tools = new EpicTools(client$1
|
|
881
|
-
server$1.
|
|
911
|
+
static create(client$1, server$1) {
|
|
912
|
+
const tools = new EpicTools(client$1);
|
|
913
|
+
server$1.addToolWithReadAccess("epics-get-by-id", "Get a Shortcut epic by public ID", {
|
|
882
914
|
epicPublicId: z.number().positive().describe("The public ID of the epic to get"),
|
|
883
915
|
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")
|
|
884
916
|
}, async ({ epicPublicId, full }) => await tools.getEpic(epicPublicId, full));
|
|
885
|
-
server$1.
|
|
917
|
+
server$1.addToolWithReadAccess("epics-search", "Find Shortcut epics.", {
|
|
886
918
|
nextPageToken: z.string().optional().describe("If a next_page_token was returned from the search result, pass it in to get the next page of results. Should be combined with the original search parameters."),
|
|
887
919
|
id: z.number().optional().describe("Find only epics with the specified public ID"),
|
|
888
920
|
name: z.string().optional().describe("Find only epics matching the specified name"),
|
|
@@ -911,7 +943,7 @@ var EpicTools = class EpicTools extends BaseTools {
|
|
|
911
943
|
completed: date(),
|
|
912
944
|
due: date()
|
|
913
945
|
}, async ({ nextPageToken,...params }) => await tools.searchEpics(params, nextPageToken));
|
|
914
|
-
|
|
946
|
+
server$1.addToolWithWriteAccess("epics-create", "Create a new Shortcut epic.", {
|
|
915
947
|
name: z.string().describe("The name of the epic"),
|
|
916
948
|
owner: z.string().optional().describe("The user ID of the owner of the epic"),
|
|
917
949
|
description: z.string().optional().describe("A description of the epic"),
|
|
@@ -946,17 +978,17 @@ var EpicTools = class EpicTools extends BaseTools {
|
|
|
946
978
|
//#endregion
|
|
947
979
|
//#region src/tools/iterations.ts
|
|
948
980
|
var IterationTools = class IterationTools extends BaseTools {
|
|
949
|
-
static create(client$1, server$1
|
|
950
|
-
const tools = new IterationTools(client$1
|
|
951
|
-
server$1.
|
|
981
|
+
static create(client$1, server$1) {
|
|
982
|
+
const tools = new IterationTools(client$1);
|
|
983
|
+
server$1.addToolWithReadAccess("iterations-get-stories", "Get stories in a specific iteration by iteration public ID", {
|
|
952
984
|
iterationPublicId: z.number().positive().describe("The public ID of the iteration"),
|
|
953
985
|
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.")
|
|
954
986
|
}, async ({ iterationPublicId, includeStoryDescriptions }) => await tools.getIterationStories(iterationPublicId, includeStoryDescriptions));
|
|
955
|
-
server$1.
|
|
987
|
+
server$1.addToolWithReadAccess("iterations-get-by-id", "Get a Shortcut iteration by public ID", {
|
|
956
988
|
iterationPublicId: z.number().positive().describe("The public ID of the iteration to get"),
|
|
957
989
|
full: z.boolean().optional().default(false).describe("True to return all iteration fields from the API. False to return a slim version that excludes uncommon fields")
|
|
958
990
|
}, async ({ iterationPublicId, full }) => await tools.getIteration(iterationPublicId, full));
|
|
959
|
-
server$1.
|
|
991
|
+
server$1.addToolWithReadAccess("iterations-search", "Find Shortcut iterations.", {
|
|
960
992
|
nextPageToken: z.string().optional().describe("If a next_page_token was returned from the search result, pass it in to get the next page of results. Should be combined with the original search parameters."),
|
|
961
993
|
id: z.number().optional().describe("Find only iterations with the specified public ID"),
|
|
962
994
|
name: z.string().optional().describe("Find only iterations matching the specified name"),
|
|
@@ -972,15 +1004,15 @@ var IterationTools = class IterationTools extends BaseTools {
|
|
|
972
1004
|
startDate: date(),
|
|
973
1005
|
endDate: date()
|
|
974
1006
|
}, async ({ nextPageToken,...params }) => await tools.searchIterations(params, nextPageToken));
|
|
975
|
-
|
|
1007
|
+
server$1.addToolWithWriteAccess("iterations-create", "Create a new Shortcut iteration", {
|
|
976
1008
|
name: z.string().describe("The name of the iteration"),
|
|
977
1009
|
startDate: z.string().describe("The start date of the iteration in YYYY-MM-DD format"),
|
|
978
1010
|
endDate: z.string().describe("The end date of the iteration in YYYY-MM-DD format"),
|
|
979
1011
|
teamId: z.string().optional().describe("The ID of a team to assign the iteration to"),
|
|
980
1012
|
description: z.string().optional().describe("A description of the iteration")
|
|
981
1013
|
}, async (params) => await tools.createIteration(params));
|
|
982
|
-
server$1.
|
|
983
|
-
server$1.
|
|
1014
|
+
server$1.addToolWithReadAccess("iterations-get-active", "Get the active Shortcut iterations for the current user based on their team memberships", { teamId: z.string().optional().describe("The ID of a team to filter iterations by") }, async ({ teamId }) => await tools.getActiveIterations(teamId));
|
|
1015
|
+
server$1.addToolWithReadAccess("iterations-get-upcoming", "Get the upcoming Shortcut iterations for the current user based on their team memberships", { teamId: z.string().optional().describe("The ID of a team to filter iterations by") }, async ({ teamId }) => await tools.getUpcomingIterations(teamId));
|
|
984
1016
|
return tools;
|
|
985
1017
|
}
|
|
986
1018
|
async getIterationStories(iterationPublicId, includeDescription) {
|
|
@@ -1057,13 +1089,13 @@ var IterationTools = class IterationTools extends BaseTools {
|
|
|
1057
1089
|
//#endregion
|
|
1058
1090
|
//#region src/tools/objectives.ts
|
|
1059
1091
|
var ObjectiveTools = class ObjectiveTools extends BaseTools {
|
|
1060
|
-
static create(client$1, server$1
|
|
1061
|
-
const tools = new ObjectiveTools(client$1
|
|
1062
|
-
server$1.
|
|
1092
|
+
static create(client$1, server$1) {
|
|
1093
|
+
const tools = new ObjectiveTools(client$1);
|
|
1094
|
+
server$1.addToolWithReadAccess("objectives-get-by-id", "Get a Shortcut objective by public ID", {
|
|
1063
1095
|
objectivePublicId: z.number().positive().describe("The public ID of the objective to get"),
|
|
1064
1096
|
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")
|
|
1065
1097
|
}, async ({ objectivePublicId, full }) => await tools.getObjective(objectivePublicId, full));
|
|
1066
|
-
server$1.
|
|
1098
|
+
server$1.addToolWithReadAccess("objectives-search", "Find Shortcut objectives.", {
|
|
1067
1099
|
nextPageToken: z.string().optional().describe("If a next_page_token was returned from the search result, pass it in to get the next page of results. Should be combined with the original search parameters."),
|
|
1068
1100
|
id: z.number().optional().describe("Find objectives matching the specified id"),
|
|
1069
1101
|
name: z.string().optional().describe("Find objectives matching the specified name"),
|
|
@@ -1105,13 +1137,13 @@ var ObjectiveTools = class ObjectiveTools extends BaseTools {
|
|
|
1105
1137
|
//#endregion
|
|
1106
1138
|
//#region src/tools/stories.ts
|
|
1107
1139
|
var StoryTools = class StoryTools extends BaseTools {
|
|
1108
|
-
static create(client$1, server$1
|
|
1109
|
-
const tools = new StoryTools(client$1
|
|
1110
|
-
server$1.
|
|
1140
|
+
static create(client$1, server$1) {
|
|
1141
|
+
const tools = new StoryTools(client$1);
|
|
1142
|
+
server$1.addToolWithReadAccess("stories-get-by-id", "Get a Shortcut story by public ID", {
|
|
1111
1143
|
storyPublicId: z.number().positive().describe("The public ID of the story to get"),
|
|
1112
1144
|
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")
|
|
1113
1145
|
}, async ({ storyPublicId, full }) => await tools.getStory(storyPublicId, full));
|
|
1114
|
-
server$1.
|
|
1146
|
+
server$1.addToolWithReadAccess("stories-search", "Find Shortcut stories.", {
|
|
1115
1147
|
nextPageToken: z.string().optional().describe("If a next_page_token was returned from the search result, pass it in to get the next page of results. Should be combined with the original search parameters."),
|
|
1116
1148
|
id: z.number().optional().describe("Find only stories with the specified public ID"),
|
|
1117
1149
|
name: z.string().optional().describe("Find only stories matching the specified name"),
|
|
@@ -1162,103 +1194,101 @@ var StoryTools = class StoryTools extends BaseTools {
|
|
|
1162
1194
|
completed: date(),
|
|
1163
1195
|
due: date()
|
|
1164
1196
|
}, async ({ nextPageToken,...params }) => await tools.searchStories(params, nextPageToken));
|
|
1165
|
-
server$1.
|
|
1166
|
-
|
|
1167
|
-
server$1.tool("create-story", `Create a new Shortcut story.
|
|
1197
|
+
server$1.addToolWithReadAccess("stories-get-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));
|
|
1198
|
+
server$1.addToolWithWriteAccess("stories-create", `Create a new Shortcut story.
|
|
1168
1199
|
Name is required, and either a Team or Workflow must be specified:
|
|
1169
1200
|
- If only Team is specified, we will use the default workflow for that team.
|
|
1170
1201
|
- If Workflow is specified, it will be used regardless of Team.
|
|
1171
1202
|
The story will be added to the default state for the workflow.
|
|
1172
1203
|
`, {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
}
|
|
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));
|
|
1204
|
+
name: z.string().min(1).max(512).describe("The name of the story. Required."),
|
|
1205
|
+
description: z.string().max(1e4).optional().describe("The description of the story"),
|
|
1206
|
+
type: z.enum([
|
|
1207
|
+
"feature",
|
|
1208
|
+
"bug",
|
|
1209
|
+
"chore"
|
|
1210
|
+
]).default("feature").describe("The type of the story"),
|
|
1211
|
+
owner: z.string().optional().describe("The user id of the owner of the story"),
|
|
1212
|
+
epic: z.number().optional().describe("The epic id of the epic the story belongs to"),
|
|
1213
|
+
iteration: z.number().optional().describe("The iteration id of the iteration the story belongs to"),
|
|
1214
|
+
team: z.string().optional().describe("The team ID or mention name of the team the story belongs to. Required unless a workflow is specified."),
|
|
1215
|
+
workflow: z.number().optional().describe("The workflow ID to add the story to. Required unless a team is specified.")
|
|
1216
|
+
}, async ({ name: name$1, description, type, owner, epic, iteration, team, workflow }) => await tools.createStory({
|
|
1217
|
+
name: name$1,
|
|
1218
|
+
description,
|
|
1219
|
+
type,
|
|
1220
|
+
owner,
|
|
1221
|
+
epic,
|
|
1222
|
+
iteration,
|
|
1223
|
+
team,
|
|
1224
|
+
workflow
|
|
1225
|
+
}));
|
|
1226
|
+
server$1.addToolWithWriteAccess("stories-update", "Update an existing Shortcut story. Only provide fields you want to update. The story public ID will always be included in updates.", {
|
|
1227
|
+
storyPublicId: z.number().positive().describe("The public ID of the story to update"),
|
|
1228
|
+
name: z.string().max(512).optional().describe("The name of the story"),
|
|
1229
|
+
description: z.string().max(1e4).optional().describe("The description of the story"),
|
|
1230
|
+
type: z.enum([
|
|
1231
|
+
"feature",
|
|
1232
|
+
"bug",
|
|
1233
|
+
"chore"
|
|
1234
|
+
]).optional().describe("The type of the story"),
|
|
1235
|
+
epic: z.number().nullable().optional().describe("The epic id of the epic the story belongs to, or null to unset"),
|
|
1236
|
+
estimate: z.number().nullable().optional().describe("The point estimate of the story, or null to unset"),
|
|
1237
|
+
iteration: z.number().nullable().optional().describe("The iteration id of the iteration the story belongs to, or null to unset"),
|
|
1238
|
+
owner_ids: z.array(z.string()).optional().describe("Array of user UUIDs to assign as owners of the story"),
|
|
1239
|
+
workflow_state_id: z.number().optional().describe("The workflow state ID to move the story to"),
|
|
1240
|
+
labels: z.array(z.object({
|
|
1241
|
+
name: z.string().describe("The name of the label"),
|
|
1242
|
+
color: z.string().optional().describe("The color of the label"),
|
|
1243
|
+
description: z.string().optional().describe("The description of the label")
|
|
1244
|
+
})).optional().describe("Labels to assign to the story")
|
|
1245
|
+
}, async (params) => await tools.updateStory(params));
|
|
1246
|
+
server$1.addToolWithWriteAccess("stories-upload-file", "Upload a file and link it to a story.", {
|
|
1247
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1248
|
+
filePath: z.string().describe("The path to the file to upload")
|
|
1249
|
+
}, async ({ storyPublicId, filePath }) => await tools.uploadFileToStory(storyPublicId, filePath));
|
|
1250
|
+
server$1.addToolWithWriteAccess("stories-assign-current-user", "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));
|
|
1251
|
+
server$1.addToolWithWriteAccess("stories-unassign-current-user", "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));
|
|
1252
|
+
server$1.addToolWithWriteAccess("stories-create-comment", "Create a comment on a story", {
|
|
1253
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1254
|
+
text: z.string().min(1).describe("The text of the comment")
|
|
1255
|
+
}, async (params) => await tools.createStoryComment(params));
|
|
1256
|
+
server$1.addToolWithWriteAccess("stories-add-task", "Add a task to a story", {
|
|
1257
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1258
|
+
taskDescription: z.string().min(1).describe("The description of the task"),
|
|
1259
|
+
taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task")
|
|
1260
|
+
}, async (params) => await tools.addTaskToStory(params));
|
|
1261
|
+
server$1.addToolWithWriteAccess("stories-update-task", "Update a task in a story", {
|
|
1262
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1263
|
+
taskPublicId: z.number().positive().describe("The public ID of the task"),
|
|
1264
|
+
taskDescription: z.string().optional().describe("The description of the task"),
|
|
1265
|
+
taskOwnerIds: z.array(z.string()).optional().describe("Array of user IDs to assign as owners of the task"),
|
|
1266
|
+
isCompleted: z.boolean().optional().describe("Whether the task is completed or not")
|
|
1267
|
+
}, async (params) => await tools.updateTask(params));
|
|
1268
|
+
server$1.addToolWithWriteAccess("stories-add-relation", "Add a story relationship to a story", {
|
|
1269
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1270
|
+
relatedStoryPublicId: z.number().positive().describe("The public ID of the related story"),
|
|
1271
|
+
relationshipType: z.enum([
|
|
1272
|
+
"relates to",
|
|
1273
|
+
"blocks",
|
|
1274
|
+
"blocked by",
|
|
1275
|
+
"duplicates",
|
|
1276
|
+
"duplicated by"
|
|
1277
|
+
]).optional().default("relates to").describe("The type of relationship")
|
|
1278
|
+
}, async (params) => await tools.addRelationToStory(params));
|
|
1279
|
+
server$1.addToolWithWriteAccess("stories-add-external-link", "Add an external link to a Shortcut story", {
|
|
1280
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1281
|
+
externalLink: z.string().url().max(2048).describe("The external link URL to add")
|
|
1282
|
+
}, async ({ storyPublicId, externalLink }) => await tools.addExternalLinkToStory(storyPublicId, externalLink));
|
|
1283
|
+
server$1.addToolWithWriteAccess("stories-remove-external-link", "Remove an external link from a Shortcut story", {
|
|
1284
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1285
|
+
externalLink: z.string().url().max(2048).describe("The external link URL to remove")
|
|
1286
|
+
}, async ({ storyPublicId, externalLink }) => await tools.removeExternalLinkFromStory(storyPublicId, externalLink));
|
|
1287
|
+
server$1.addToolWithWriteAccess("stories-set-external-links", "Replace all external links on a story with a new set of links", {
|
|
1288
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
1289
|
+
externalLinks: z.array(z.string().url().max(2048)).describe("Array of external link URLs to set (replaces all existing links)")
|
|
1290
|
+
}, async ({ storyPublicId, externalLinks }) => await tools.setStoryExternalLinks(storyPublicId, externalLinks));
|
|
1291
|
+
server$1.addToolWithReadAccess("stories-get-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));
|
|
1262
1292
|
return tools;
|
|
1263
1293
|
}
|
|
1264
1294
|
async assignCurrentUserAsOwner(storyPublicId) {
|
|
@@ -1437,13 +1467,13 @@ The story will be added to the default state for the workflow.
|
|
|
1437
1467
|
//#endregion
|
|
1438
1468
|
//#region src/tools/teams.ts
|
|
1439
1469
|
var TeamTools = class TeamTools extends BaseTools {
|
|
1440
|
-
static create(client$1, server$1
|
|
1441
|
-
const tools = new TeamTools(client$1
|
|
1442
|
-
server$1.
|
|
1470
|
+
static create(client$1, server$1) {
|
|
1471
|
+
const tools = new TeamTools(client$1);
|
|
1472
|
+
server$1.addToolWithReadAccess("teams-get-by-id", "Get a Shortcut team by public ID", {
|
|
1443
1473
|
teamPublicId: z.string().describe("The public ID of the team to get"),
|
|
1444
1474
|
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")
|
|
1445
1475
|
}, async ({ teamPublicId, full }) => await tools.getTeam(teamPublicId, full));
|
|
1446
|
-
server$1.
|
|
1476
|
+
server$1.addToolWithReadAccess("teams-list", "List all Shortcut teams", async () => await tools.getTeams());
|
|
1447
1477
|
return tools;
|
|
1448
1478
|
}
|
|
1449
1479
|
async getTeam(teamPublicId, full = false) {
|
|
@@ -1461,11 +1491,11 @@ var TeamTools = class TeamTools extends BaseTools {
|
|
|
1461
1491
|
//#endregion
|
|
1462
1492
|
//#region src/tools/user.ts
|
|
1463
1493
|
var UserTools = class UserTools extends BaseTools {
|
|
1464
|
-
static create(client$1, server$1
|
|
1465
|
-
const tools = new UserTools(client$1
|
|
1466
|
-
server$1.
|
|
1467
|
-
server$1.
|
|
1468
|
-
server$1.
|
|
1494
|
+
static create(client$1, server$1) {
|
|
1495
|
+
const tools = new UserTools(client$1);
|
|
1496
|
+
server$1.addToolWithReadAccess("users-get-current", "Get the current user", async () => await tools.getCurrentUser());
|
|
1497
|
+
server$1.addToolWithReadAccess("users-get-current-teams", "Get a list of teams where the current user is a member", async () => await tools.getCurrentUserTeams());
|
|
1498
|
+
server$1.addToolWithReadAccess("users-list", "Get all users", async () => await tools.listMembers());
|
|
1469
1499
|
return tools;
|
|
1470
1500
|
}
|
|
1471
1501
|
async getCurrentUser() {
|
|
@@ -1494,14 +1524,14 @@ var UserTools = class UserTools extends BaseTools {
|
|
|
1494
1524
|
//#endregion
|
|
1495
1525
|
//#region src/tools/workflows.ts
|
|
1496
1526
|
var WorkflowTools = class WorkflowTools extends BaseTools {
|
|
1497
|
-
static create(client$1, server$1
|
|
1498
|
-
const tools = new WorkflowTools(client$1
|
|
1499
|
-
server$1.
|
|
1500
|
-
server$1.
|
|
1527
|
+
static create(client$1, server$1) {
|
|
1528
|
+
const tools = new WorkflowTools(client$1);
|
|
1529
|
+
server$1.addToolWithReadAccess("workflows-get-default", "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));
|
|
1530
|
+
server$1.addToolWithReadAccess("workflows-get-by-id", "Get a Shortcut workflow by public ID", {
|
|
1501
1531
|
workflowPublicId: z.number().positive().describe("The public ID of the workflow to get"),
|
|
1502
1532
|
full: z.boolean().optional().default(false).describe("True to return all workflow fields from the API. False to return a slim version that excludes uncommon fields")
|
|
1503
1533
|
}, async ({ workflowPublicId, full }) => await tools.getWorkflow(workflowPublicId, full));
|
|
1504
|
-
server$1.
|
|
1534
|
+
server$1.addToolWithReadAccess("workflows-list", "List all Shortcut workflows", async () => await tools.listWorkflows());
|
|
1505
1535
|
return tools;
|
|
1506
1536
|
}
|
|
1507
1537
|
async getDefaultWorkflow(teamPublicId) {
|
|
@@ -1535,32 +1565,29 @@ var WorkflowTools = class WorkflowTools extends BaseTools {
|
|
|
1535
1565
|
//#region src/server.ts
|
|
1536
1566
|
let apiToken = process.env.SHORTCUT_API_TOKEN;
|
|
1537
1567
|
let isReadonly = process.env.SHORTCUT_READONLY === "true";
|
|
1538
|
-
let enabledTools = process.env.SHORTCUT_TOOLS
|
|
1568
|
+
let enabledTools = (process.env.SHORTCUT_TOOLS || "").split(",").map((tool) => tool.trim()).filter(Boolean);
|
|
1539
1569
|
if (process.argv.length >= 3) process.argv.slice(2).map((arg) => arg.split("=")).forEach(([name$1, value]) => {
|
|
1540
1570
|
if (name$1 === "SHORTCUT_API_TOKEN") apiToken = value;
|
|
1541
1571
|
if (name$1 === "SHORTCUT_READONLY") isReadonly = value === "true";
|
|
1542
|
-
if (name$1 === "SHORTCUT_TOOLS") enabledTools = value.split(",").map((tool) => tool.trim());
|
|
1572
|
+
if (name$1 === "SHORTCUT_TOOLS") enabledTools = value.split(",").map((tool) => tool.trim()).filter(Boolean);
|
|
1543
1573
|
});
|
|
1544
1574
|
if (!apiToken) {
|
|
1545
1575
|
console.error("SHORTCUT_API_TOKEN is required");
|
|
1546
1576
|
process.exit(1);
|
|
1547
1577
|
}
|
|
1548
|
-
const server = new
|
|
1549
|
-
|
|
1550
|
-
|
|
1578
|
+
const server = new CustomMcpServer({
|
|
1579
|
+
readonly: isReadonly,
|
|
1580
|
+
tools: enabledTools
|
|
1551
1581
|
});
|
|
1552
1582
|
const client = new ShortcutClientWrapper(new ShortcutClient(apiToken));
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
if (areToolsEnabled("teams")) TeamTools.create(client, server, isReadonly);
|
|
1562
|
-
if (areToolsEnabled("workflows")) WorkflowTools.create(client, server, isReadonly);
|
|
1563
|
-
if (areToolsEnabled("documents")) DocumentTools.create(client, server, isReadonly);
|
|
1583
|
+
UserTools.create(client, server);
|
|
1584
|
+
StoryTools.create(client, server);
|
|
1585
|
+
IterationTools.create(client, server);
|
|
1586
|
+
EpicTools.create(client, server);
|
|
1587
|
+
ObjectiveTools.create(client, server);
|
|
1588
|
+
TeamTools.create(client, server);
|
|
1589
|
+
WorkflowTools.create(client, server);
|
|
1590
|
+
DocumentTools.create(client, server);
|
|
1564
1591
|
async function startServer() {
|
|
1565
1592
|
try {
|
|
1566
1593
|
const transport = new StdioServerTransport();
|