@shortcut/mcp 0.2.1 → 0.4.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.
- package/README.md +30 -0
- package/dist/index.js +114 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,8 +51,38 @@ See the [official Cursor docs](https://docs.cursor.com/context/model-context-pro
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
### Claude Code
|
|
55
|
+
|
|
56
|
+
See the [official Claude Code docs](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/tutorials#set-up-model-context-protocol-mcp) for more information.
|
|
57
|
+
|
|
58
|
+
_You can add a new MCP server from the Claude Code CLI. But modifying the json file directly is simpler!_
|
|
59
|
+
|
|
60
|
+
1. Open the Claude Code configuration file (it should be in `~/.claude.json`).
|
|
61
|
+
2. Find the `projects` > `mcpServers` section and add the following details and save the file:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"projects": {
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"shortcut": {
|
|
68
|
+
"command": "npx",
|
|
69
|
+
"args": [
|
|
70
|
+
"-y",
|
|
71
|
+
"@shortcut/mcp"
|
|
72
|
+
],
|
|
73
|
+
"env": {
|
|
74
|
+
"SHORTCUT_API_TOKEN": "<YOUR_SHORTCUT_API_TOKEN>"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
54
82
|
## Issues and Troubleshooting
|
|
55
83
|
|
|
84
|
+
Before doing anything else, please make sure you are running the latest version!
|
|
85
|
+
|
|
56
86
|
If you run into problems using this MCP server, you have a couple of options:
|
|
57
87
|
|
|
58
88
|
- Open an issue on [GitHub](https://github.com/useshortcut/mcp-server-shortcut/issues)
|
package/dist/index.js
CHANGED
|
@@ -14761,6 +14761,27 @@ class ShortcutClientWrapper {
|
|
|
14761
14761
|
return { stories: null, total: null };
|
|
14762
14762
|
return { stories, total: stories.length };
|
|
14763
14763
|
}
|
|
14764
|
+
async createStoryComment(storyPublicId, params) {
|
|
14765
|
+
const response = await this.client.createStoryComment(storyPublicId, params);
|
|
14766
|
+
const storyComment = response?.data ?? null;
|
|
14767
|
+
if (!storyComment)
|
|
14768
|
+
throw new Error(`Failed to create the comment: ${response.status}`);
|
|
14769
|
+
return storyComment;
|
|
14770
|
+
}
|
|
14771
|
+
async createIteration(params) {
|
|
14772
|
+
const response = await this.client.createIteration(params);
|
|
14773
|
+
const iteration = response?.data ?? null;
|
|
14774
|
+
if (!iteration)
|
|
14775
|
+
throw new Error(`Failed to create the iteration: ${response.status}`);
|
|
14776
|
+
return iteration;
|
|
14777
|
+
}
|
|
14778
|
+
async createEpic(params) {
|
|
14779
|
+
const response = await this.client.createEpic(params);
|
|
14780
|
+
const epic = response?.data ?? null;
|
|
14781
|
+
if (!epic)
|
|
14782
|
+
throw new Error(`Failed to create the epic: ${response.status}`);
|
|
14783
|
+
return epic;
|
|
14784
|
+
}
|
|
14764
14785
|
}
|
|
14765
14786
|
|
|
14766
14787
|
// node_modules/zod/lib/index.mjs
|
|
@@ -21304,7 +21325,7 @@ var import_client = __toESM(require_lib(), 1);
|
|
|
21304
21325
|
|
|
21305
21326
|
// package.json
|
|
21306
21327
|
var name = "@shortcut/mcp";
|
|
21307
|
-
var version = "0.
|
|
21328
|
+
var version = "0.4.0";
|
|
21308
21329
|
|
|
21309
21330
|
// src/tools/base.ts
|
|
21310
21331
|
class BaseTools {
|
|
@@ -21344,6 +21365,23 @@ var formatPullRequestList = (branches) => {
|
|
|
21344
21365
|
var formatTaskList = (tasks) => {
|
|
21345
21366
|
return formatAsUnorderedList((tasks || []).map((task) => `[${task.complete ? "X" : " "}] ${task.description}`), "Tasks");
|
|
21346
21367
|
};
|
|
21368
|
+
var formatStats = (stats, showPoints) => {
|
|
21369
|
+
const { num_stories_backlog, num_stories_unstarted, num_stories_started, num_stories_done } = stats;
|
|
21370
|
+
const { num_points_backlog, num_points_unstarted, num_points_started, num_points_done } = stats;
|
|
21371
|
+
const totalCount = num_stories_backlog + num_stories_unstarted + num_stories_started + num_stories_done;
|
|
21372
|
+
const totalUnstarted = num_stories_backlog + num_stories_unstarted;
|
|
21373
|
+
const totalPoints = (num_points_backlog || 0) + (num_points_unstarted || 0) + (num_points_started || 0) + (num_points_done || 0);
|
|
21374
|
+
const totalUnstartedPoints = (num_points_backlog || 0) + (num_points_unstarted || 0);
|
|
21375
|
+
const statsString = `Stats:
|
|
21376
|
+
- Total stories: ${totalCount}${showPoints ? ` (${totalPoints} points)` : ""}
|
|
21377
|
+
- Unstarted stories: ${totalUnstarted}${showPoints ? ` (${totalUnstartedPoints} points)` : ""}
|
|
21378
|
+
- Stories in progress: ${num_stories_started}${showPoints ? ` (${num_points_started || 0} points)` : ""}
|
|
21379
|
+
- Completed stories: ${num_stories_done}${showPoints ? ` (${num_points_done || 0} points)` : ""}`;
|
|
21380
|
+
if (showPoints && stats.num_stories_unestimated)
|
|
21381
|
+
return `${statsString}
|
|
21382
|
+
- (${stats.num_stories_unestimated} of the stories are unestimated)`;
|
|
21383
|
+
return statsString;
|
|
21384
|
+
};
|
|
21347
21385
|
|
|
21348
21386
|
// src/tools/utils/search.ts
|
|
21349
21387
|
var getKey = (prop) => {
|
|
@@ -21425,6 +21463,11 @@ class EpicTools extends BaseTools {
|
|
|
21425
21463
|
completed: date2,
|
|
21426
21464
|
due: date2
|
|
21427
21465
|
}, async (params) => await tools.searchEpics(params));
|
|
21466
|
+
server.tool("create-epic", "Create a new Shortcut epic.", {
|
|
21467
|
+
name: z.string().describe("The name of the epic"),
|
|
21468
|
+
teamId: z.string().optional().describe("The ID of the team to assign the epic to"),
|
|
21469
|
+
description: z.string().optional().describe("The description of the epic")
|
|
21470
|
+
}, async (params) => await tools.createEpic(params));
|
|
21428
21471
|
return tools;
|
|
21429
21472
|
}
|
|
21430
21473
|
async searchEpics(params) {
|
|
@@ -21442,6 +21485,8 @@ ${formatAsUnorderedList(epics.map((epic) => `${epic.id}: ${epic.name}`))}`);
|
|
|
21442
21485
|
const epic = await this.client.getEpic(epicPublicId);
|
|
21443
21486
|
if (!epic)
|
|
21444
21487
|
throw new Error(`Failed to retrieve Shortcut epic with public ID: ${epicPublicId}`);
|
|
21488
|
+
const currentUser = await this.client.getCurrentUser();
|
|
21489
|
+
const showPoints = !!currentUser?.workspace2?.estimate_scale?.length;
|
|
21445
21490
|
return this.toResult(`Epic: ${epicPublicId}
|
|
21446
21491
|
URL: ${epic.app_url}
|
|
21447
21492
|
Name: ${epic.name}
|
|
@@ -21452,9 +21497,24 @@ Due date: ${epic.deadline ? epic.deadline : "[Not set]"}
|
|
|
21452
21497
|
Team: ${epic.group_id ? `${epic.group_id}` : "[None]"}
|
|
21453
21498
|
Objective: ${epic.milestone_id ? `${epic.milestone_id}` : "[None]"}
|
|
21454
21499
|
|
|
21500
|
+
${formatStats(epic.stats, showPoints)}
|
|
21501
|
+
|
|
21455
21502
|
Description:
|
|
21456
21503
|
${epic.description}`);
|
|
21457
21504
|
}
|
|
21505
|
+
async createEpic({
|
|
21506
|
+
name: name2,
|
|
21507
|
+
teamId,
|
|
21508
|
+
description
|
|
21509
|
+
}) {
|
|
21510
|
+
if (teamId) {
|
|
21511
|
+
const team = await this.client.getTeam(teamId);
|
|
21512
|
+
if (!team)
|
|
21513
|
+
throw new Error(`Team with ID ${teamId} not found`);
|
|
21514
|
+
}
|
|
21515
|
+
const epic = await this.client.createEpic({ name: name2, group_id: teamId, description });
|
|
21516
|
+
return this.toResult(`Epic created with ID: ${epic.id}.`);
|
|
21517
|
+
}
|
|
21458
21518
|
}
|
|
21459
21519
|
|
|
21460
21520
|
// src/tools/iterations.ts
|
|
@@ -21476,6 +21536,13 @@ class IterationTools extends BaseTools {
|
|
|
21476
21536
|
startDate: date2,
|
|
21477
21537
|
endDate: date2
|
|
21478
21538
|
}, async (params) => await tools.searchIterations(params));
|
|
21539
|
+
server.tool("create-iteration", "Create a new Shortcut iteration", {
|
|
21540
|
+
name: z.string().describe("The name of the iteration"),
|
|
21541
|
+
description: z.string().optional().describe("The description of the iteration"),
|
|
21542
|
+
startDate: z.string().describe("The start date of the iteration"),
|
|
21543
|
+
endDate: z.string().describe("The end date of the iteration"),
|
|
21544
|
+
teamId: z.string().optional().describe("The ID of the team to assign the iteration to")
|
|
21545
|
+
}, async (params) => await tools.createIteration(params));
|
|
21479
21546
|
return tools;
|
|
21480
21547
|
}
|
|
21481
21548
|
async getIterationStories(iterationPublicId) {
|
|
@@ -21501,6 +21568,8 @@ ${formatAsUnorderedList(iterations.map((iteration) => `${iteration.id}: ${iterat
|
|
|
21501
21568
|
const iteration = await this.client.getIteration(iterationPublicId);
|
|
21502
21569
|
if (!iteration)
|
|
21503
21570
|
throw new Error(`Failed to retrieve Shortcut iteration with public ID: ${iterationPublicId}.`);
|
|
21571
|
+
const currentUser = await this.client.getCurrentUser();
|
|
21572
|
+
const showPoints = !!currentUser?.workspace2?.estimate_scale?.length;
|
|
21504
21573
|
return this.toResult(`Iteration: ${iterationPublicId}
|
|
21505
21574
|
Url: ${iteration.app_url}
|
|
21506
21575
|
Name: ${iteration.name}
|
|
@@ -21510,9 +21579,34 @@ Completed: ${iteration.status === "completed" ? "Yes" : "No"}
|
|
|
21510
21579
|
Started: ${iteration.status === "started" ? "Yes" : "No"}
|
|
21511
21580
|
Team: ${iteration.group_ids?.length ? `${iteration.group_ids.join(", ")}` : "[None]"}
|
|
21512
21581
|
|
|
21582
|
+
${formatStats(iteration.stats, showPoints)}
|
|
21583
|
+
|
|
21513
21584
|
Description:
|
|
21514
21585
|
${iteration.description}`);
|
|
21515
21586
|
}
|
|
21587
|
+
async createIteration({
|
|
21588
|
+
name: name2,
|
|
21589
|
+
startDate,
|
|
21590
|
+
endDate,
|
|
21591
|
+
teamId,
|
|
21592
|
+
description
|
|
21593
|
+
}) {
|
|
21594
|
+
if (teamId) {
|
|
21595
|
+
const team = await this.client.getTeam(teamId);
|
|
21596
|
+
if (!team)
|
|
21597
|
+
throw new Error(`Team with ID ${teamId} not found`);
|
|
21598
|
+
}
|
|
21599
|
+
const iteration = await this.client.createIteration({
|
|
21600
|
+
name: name2,
|
|
21601
|
+
start_date: startDate,
|
|
21602
|
+
end_date: endDate,
|
|
21603
|
+
group_ids: teamId ? [teamId] : undefined,
|
|
21604
|
+
description
|
|
21605
|
+
});
|
|
21606
|
+
if (!iteration)
|
|
21607
|
+
throw new Error(`Failed to create the iteration.`);
|
|
21608
|
+
return this.toResult(`Iteration created with ID: ${iteration.id}.`);
|
|
21609
|
+
}
|
|
21516
21610
|
}
|
|
21517
21611
|
|
|
21518
21612
|
// src/tools/objectives.ts
|
|
@@ -21650,6 +21744,10 @@ The story will be added to the default state for the workflow.
|
|
|
21650
21744
|
server.tool("unassign-current-user-as-owner", "Unassign the current user as the owner of a story", {
|
|
21651
21745
|
storyPublicId: z.number().positive().describe("The public ID of the story")
|
|
21652
21746
|
}, async ({ storyPublicId }) => await tools.unassignCurrentUserAsOwner(storyPublicId));
|
|
21747
|
+
server.tool("create-story-comment", "Create a comment on a story", {
|
|
21748
|
+
storyPublicId: z.number().positive().describe("The public ID of the story"),
|
|
21749
|
+
text: z.string().min(1).describe("The text of the comment")
|
|
21750
|
+
}, async (params) => await tools.createStoryComment(params));
|
|
21653
21751
|
return tools;
|
|
21654
21752
|
}
|
|
21655
21753
|
async assignCurrentUserAsOwner(storyPublicId) {
|
|
@@ -21778,6 +21876,20 @@ ${comment.text || ""}`;
|
|
|
21778
21876
|
|
|
21779
21877
|
`)}`);
|
|
21780
21878
|
}
|
|
21879
|
+
async createStoryComment({
|
|
21880
|
+
storyPublicId,
|
|
21881
|
+
text
|
|
21882
|
+
}) {
|
|
21883
|
+
if (!storyPublicId)
|
|
21884
|
+
throw new Error("Story public ID is required");
|
|
21885
|
+
if (!text)
|
|
21886
|
+
throw new Error("Story comment text is required");
|
|
21887
|
+
const story = await this.client.getStory(storyPublicId);
|
|
21888
|
+
if (!story)
|
|
21889
|
+
throw new Error(`Failed to retrieve Shortcut story with public ID: ${storyPublicId}`);
|
|
21890
|
+
const storyComment = await this.client.createStoryComment(storyPublicId, { text });
|
|
21891
|
+
return this.toResult(`Created comment on story sc-${storyPublicId}. Comment URL: ${storyComment.app_url}.`);
|
|
21892
|
+
}
|
|
21781
21893
|
}
|
|
21782
21894
|
|
|
21783
21895
|
// src/tools/teams.ts
|
|
@@ -21869,7 +21981,7 @@ Default State: ${workflow.states.find((state) => state.id === workflow.default_s
|
|
|
21869
21981
|
|
|
21870
21982
|
// src/server.ts
|
|
21871
21983
|
var apiToken = process.env.SHORTCUT_API_TOKEN;
|
|
21872
|
-
if (process.argv.length
|
|
21984
|
+
if (process.argv.length === 3) {
|
|
21873
21985
|
const [name2, token] = String(process.argv[2]).split("=");
|
|
21874
21986
|
if (name2 === "SHORTCUT_API_TOKEN")
|
|
21875
21987
|
apiToken = token;
|