Skip to content
This repository was archived by the owner on Jul 8, 2026. It is now read-only.

Commit 1b1b7e3

Browse files
Merge pull request #136 from useshortcut/kschrader/exclude-archived-teams
Exclude archived teams from teams-list results
2 parents 8d60dfe + 2a5c481 commit 1b1b7e3

3 files changed

Lines changed: 132 additions & 33 deletions

File tree

src/tools/base.ts

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ export type SimplifiedWorkflow = {
5050
export type SimplifiedTeam = {
5151
id: string;
5252
name: string;
53-
archived: boolean;
5453
mention_name: string;
55-
member_ids: string[];
56-
workflow_ids: number[];
54+
archived: boolean;
5755
default_workflow_id: number | null;
56+
member_ids?: string[];
57+
workflow_ids?: number[];
5858
};
5959
export type SimplifiedObjective = {
6060
id: number;
@@ -262,18 +262,28 @@ export class BaseTools {
262262
};
263263
}
264264

265-
private getSimplifiedTeam(entity: Group | null | undefined): SimplifiedTeam | null {
265+
private getSimplifiedTeam(
266+
entity: Group | null | undefined,
267+
kind: SimplifiedKind,
268+
): SimplifiedTeam | null {
266269
if (!entity) return null;
267270
const { archived, id, name, mention_name, member_ids, workflow_ids, default_workflow_id } =
268271
entity;
272+
273+
const additionalFields: Partial<SimplifiedTeam> = {};
274+
275+
if (kind === "simple") {
276+
additionalFields.member_ids = member_ids;
277+
additionalFields.workflow_ids = workflow_ids;
278+
}
279+
269280
return {
270281
id,
271282
name,
272-
archived,
273283
mention_name,
274-
member_ids,
275-
workflow_ids,
284+
archived,
276285
default_workflow_id: default_workflow_id ?? null,
286+
...additionalFields,
277287
};
278288
}
279289

@@ -336,12 +346,30 @@ export class BaseTools {
336346
return { id, name, app_url, team_ids: group_ids, status, start_date, end_date };
337347
}
338348

339-
private async getRelatedEntitiesForTeam(entity: Group | null | undefined): Promise<{
349+
private async getRelatedEntitiesForTeam(
350+
entity: Group | null | undefined,
351+
kind: SimplifiedKind,
352+
): Promise<{
340353
users: Record<string, SimplifiedMember>;
341354
workflows: Record<string, SimplifiedWorkflow>;
342355
}> {
343356
if (!entity) return { users: {}, workflows: {} };
344-
const { member_ids, workflow_ids } = entity;
357+
358+
const { member_ids, workflow_ids, default_workflow_id } = entity;
359+
360+
if (kind === "list") {
361+
if (!default_workflow_id) return { users: {}, workflows: {} };
362+
363+
const workflows = await this.client.getWorkflowMap([default_workflow_id]);
364+
const simplifiedWorkflow = this.getSimplifiedWorkflow(workflows.get(default_workflow_id));
365+
366+
if (!simplifiedWorkflow) return { users: {}, workflows: {} };
367+
368+
return {
369+
users: {},
370+
workflows: simplifiedWorkflow ? { [default_workflow_id]: simplifiedWorkflow } : {},
371+
};
372+
}
345373

346374
const [users, workflows] = await Promise.all([
347375
this.client.getUserMap(member_ids),
@@ -364,7 +392,10 @@ export class BaseTools {
364392
};
365393
}
366394

367-
private async getRelatedEntitiesForIteration(entity: Iteration | null | undefined): Promise<{
395+
private async getRelatedEntitiesForIteration(
396+
entity: Iteration | null | undefined,
397+
kind: SimplifiedKind,
398+
): Promise<{
368399
teams: Record<string, SimplifiedTeam>;
369400
users: Record<string, SimplifiedMember>;
370401
workflows: Record<string, SimplifiedWorkflow>;
@@ -374,22 +405,25 @@ export class BaseTools {
374405

375406
const teams = await this.client.getTeamMap(group_ids || []);
376407
const relatedEntitiesForTeams = await Promise.all(
377-
Array.from(teams.values()).map((team) => this.getRelatedEntitiesForTeam(team)),
408+
Array.from(teams.values()).map((team) => this.getRelatedEntitiesForTeam(team, kind)),
378409
);
379410
const { users, workflows } = this.mergeRelatedEntities(relatedEntitiesForTeams);
380411

381412
return {
382413
teams: Object.fromEntries(
383414
[...teams.entries()]
384-
.map(([id, team]) => [id, this.getSimplifiedTeam(team)])
415+
.map(([id, team]) => [id, this.getSimplifiedTeam(team, kind)])
385416
.filter(([_, team]) => !!team),
386417
) as Record<string, SimplifiedTeam>,
387418
users,
388419
workflows,
389420
};
390421
}
391422

392-
private async getRelatedEntitiesForEpic(entity: Epic | null | undefined): Promise<{
423+
private async getRelatedEntitiesForEpic(
424+
entity: Epic | null | undefined,
425+
kind: SimplifiedKind,
426+
): Promise<{
393427
users: Record<string, SimplifiedMember>;
394428
workflows: Record<string, SimplifiedWorkflow>;
395429
teams: Record<string, SimplifiedTeam>;
@@ -418,8 +452,11 @@ export class BaseTools {
418452
.filter(([_, user]) => !!user)
419453
.map(([id, user]) => [id, this.getSimplifiedMember(user)]),
420454
) as Record<string, SimplifiedMember>;
421-
const team = this.getSimplifiedTeam(teams.get(group_id || ""));
422-
const { users, workflows } = await this.getRelatedEntitiesForTeam(teams.get(group_id || ""));
455+
const team = this.getSimplifiedTeam(teams.get(group_id || ""), kind);
456+
const { users, workflows } = await this.getRelatedEntitiesForTeam(
457+
teams.get(group_id || ""),
458+
kind,
459+
);
423460
const milestone = this.getSimplifiedObjective(fullMilestone);
424461

425462
return {
@@ -484,18 +521,18 @@ export class BaseTools {
484521
const workflowForStory = this.getSimplifiedWorkflow(workflowsForStory.get(workflow_id));
485522

486523
const { users: usersForTeam, workflows: workflowsForTeam } =
487-
await this.getRelatedEntitiesForTeam(teamForStory);
524+
await this.getRelatedEntitiesForTeam(teamForStory, kind);
488525
const {
489526
users: usersForIteration,
490527
workflows: workflowsForIteration,
491528
teams: teamsForIteration,
492-
} = await this.getRelatedEntitiesForIteration(iteration);
529+
} = await this.getRelatedEntitiesForIteration(iteration, kind);
493530
const {
494531
users: usersForEpic,
495532
workflows: workflowsForEpic,
496533
teams: teamsForEpic,
497534
objectives,
498-
} = await this.getRelatedEntitiesForEpic(epic);
535+
} = await this.getRelatedEntitiesForEpic(epic, kind);
499536

500537
const users = this.mergeRelatedEntities([
501538
usersForTeam,
@@ -509,7 +546,7 @@ export class BaseTools {
509546
workflowsForEpic,
510547
workflowForStory ? { [workflowForStory.id]: workflowForStory } : {},
511548
]);
512-
const simplifiedStoryTeam = this.getSimplifiedTeam(teamForStory);
549+
const simplifiedStoryTeam = this.getSimplifiedTeam(teamForStory, kind);
513550
const teams = this.mergeRelatedEntities([
514551
teamsForIteration,
515552
teamsForEpic,
@@ -563,10 +600,11 @@ export class BaseTools {
563600
| Milestone,
564601
kind: SimplifiedKind,
565602
) {
566-
if (entity.entity_type === "group") return this.getRelatedEntitiesForTeam(entity as Group);
603+
if (entity.entity_type === "group")
604+
return this.getRelatedEntitiesForTeam(entity as Group, kind);
567605
if (entity.entity_type === "iteration")
568-
return this.getRelatedEntitiesForIteration(entity as Iteration);
569-
if (entity.entity_type === "epic") return this.getRelatedEntitiesForEpic(entity as Epic);
606+
return this.getRelatedEntitiesForIteration(entity as Iteration, kind);
607+
if (entity.entity_type === "epic") return this.getRelatedEntitiesForEpic(entity as Epic, kind);
570608
if (entity.entity_type === "story")
571609
return this.getRelatedEntitiesForStory(entity as Story, kind);
572610

@@ -588,7 +626,7 @@ export class BaseTools {
588626
| Milestone,
589627
kind: SimplifiedKind,
590628
) {
591-
if (entity.entity_type === "group") return this.getSimplifiedTeam(entity as Group);
629+
if (entity.entity_type === "group") return this.getSimplifiedTeam(entity as Group, kind);
592630
if (entity.entity_type === "iteration") return this.getSimplifiedIteration(entity as Iteration);
593631
if (entity.entity_type === "epic") return this.getSimplifiedEpic(entity as Epic, kind);
594632
if (entity.entity_type === "story") return this.getSimplifiedStory(entity as Story, kind);

src/tools/teams.test.ts

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ describe("TeamTools", () => {
105105
spyOn(tools, "getTeams").mockImplementation(async () => ({
106106
content: [{ text: "", type: "text" }],
107107
}));
108-
await mockTool.mock.calls?.[1]?.[2]();
109-
expect(tools.getTeams).toHaveBeenCalled();
108+
await mockTool.mock.calls?.[1]?.[3]({ includeArchived: true });
109+
expect(tools.getTeams).toHaveBeenCalledWith(true);
110110
});
111111
});
112112

@@ -180,6 +180,19 @@ describe("TeamTools", () => {
180180
});
181181

182182
describe("getTeams method", () => {
183+
const mockArchivedTeam = {
184+
entity_type: "group",
185+
id: "team3",
186+
name: "Archived Team",
187+
mention_name: "archived-team",
188+
description: "An archived team",
189+
archived: true,
190+
app_url: "https://app.shortcut.com/test/team/team3",
191+
global_id: "team3",
192+
member_ids: [] as string[],
193+
workflow_ids: [] as number[],
194+
} as Group;
195+
183196
const getTeamsMock = mock(async () => mockTeams);
184197
const getWorkflowMapMock = mock(async (ids: number[]) => {
185198
const map = new Map<number, Workflow>();
@@ -199,7 +212,7 @@ describe("TeamTools", () => {
199212

200213
test("should return formatted list of teams when teams are found", async () => {
201214
const teamTools = new TeamTools(mockClient);
202-
const result = await teamTools.getTeams();
215+
const result = await teamTools.getTeams(false);
203216

204217
expect(result.content[0].type).toBe("text");
205218
const textContent = getTextContent(result);
@@ -215,7 +228,7 @@ describe("TeamTools", () => {
215228
getTeams: mock(async () => []),
216229
} as unknown as ShortcutClientWrapper);
217230

218-
const result = await teamTools.getTeams();
231+
const result = await teamTools.getTeams(false);
219232

220233
expect(result.content[0].type).toBe("text");
221234
expect(getTextContent(result)).toBe("No teams found.");
@@ -234,7 +247,7 @@ describe("TeamTools", () => {
234247
getTeamMap: mock(async () => new Map()),
235248
} as unknown as ShortcutClientWrapper);
236249

237-
const result = await teamTools.getTeams();
250+
const result = await teamTools.getTeams(false);
238251

239252
expect(result.content[0].type).toBe("text");
240253
const textContent = getTextContent(result);
@@ -243,5 +256,43 @@ describe("TeamTools", () => {
243256
expect(textContent).toContain('"name": "Team 1"');
244257
expect(textContent).toContain('"workflow_ids": []');
245258
});
259+
260+
test("should filter out archived teams when includeArchived is false", async () => {
261+
const teamTools = new TeamTools({
262+
getTeams: mock(async () => [...mockTeams, mockArchivedTeam]),
263+
getWorkflowMap: getWorkflowMapMock,
264+
getUserMap: mock(async () => new Map()),
265+
getTeamMap: mock(async () => new Map()),
266+
} as unknown as ShortcutClientWrapper);
267+
268+
const result = await teamTools.getTeams(false);
269+
270+
expect(result.content[0].type).toBe("text");
271+
const textContent = getTextContent(result);
272+
expect(textContent).toContain("Result (first 2 shown of 2 total teams found):");
273+
expect(textContent).toContain('"id": "team1"');
274+
expect(textContent).toContain('"id": "team2"');
275+
expect(textContent).not.toContain('"id": "team3"');
276+
expect(textContent).not.toContain("Archived Team");
277+
});
278+
279+
test("should include archived teams when includeArchived is true", async () => {
280+
const teamTools = new TeamTools({
281+
getTeams: mock(async () => [...mockTeams, mockArchivedTeam]),
282+
getWorkflowMap: getWorkflowMapMock,
283+
getUserMap: mock(async () => new Map()),
284+
getTeamMap: mock(async () => new Map()),
285+
} as unknown as ShortcutClientWrapper);
286+
287+
const result = await teamTools.getTeams(true);
288+
289+
expect(result.content[0].type).toBe("text");
290+
const textContent = getTextContent(result);
291+
expect(textContent).toContain("Result (first 3 shown of 3 total teams found):");
292+
expect(textContent).toContain('"id": "team1"');
293+
expect(textContent).toContain('"id": "team2"');
294+
expect(textContent).toContain('"id": "team3"');
295+
expect(textContent).toContain("Archived Team");
296+
});
246297
});
247298
});

src/tools/teams.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,16 @@ export class TeamTools extends BaseTools {
2525

2626
server.addToolWithReadAccess(
2727
"teams-list",
28-
"List all Shortcut teams",
29-
async () => await tools.getTeams(),
28+
"List Shortcut teams",
29+
{
30+
includeArchived: z
31+
.boolean()
32+
.describe("True to include archived teams.")
33+
.optional()
34+
.default(false),
35+
},
36+
async ({ includeArchived }: { includeArchived: boolean }) =>
37+
await tools.getTeams(includeArchived),
3038
);
3139

3240
return tools;
@@ -43,14 +51,16 @@ export class TeamTools extends BaseTools {
4351
);
4452
}
4553

46-
async getTeams() {
54+
async getTeams(includeArchived: boolean) {
4755
const teams = await this.client.getTeams();
4856

4957
if (!teams.length) return this.toResult(`No teams found.`);
5058

59+
const filteredTeams = includeArchived ? teams : teams.filter((team) => !team.archived);
60+
5161
return this.toResult(
52-
`Result (first ${teams.length} shown of ${teams.length} total teams found):`,
53-
await this.entitiesWithRelatedEntities(teams, "teams"),
62+
`Result (first ${filteredTeams.length} shown of ${filteredTeams.length} total teams found):`,
63+
await this.entitiesWithRelatedEntities(filteredTeams, "teams"),
5464
);
5565
}
5666
}

0 commit comments

Comments
 (0)