Skip to main content
Glama

Shortcut MCP Server

Official
by useshortcut
objectives.test.ts6.91 kB
import { describe, expect, mock, test } from "bun:test"; import type { Milestone } from "@shortcut/client"; import type { ShortcutClientWrapper } from "@/client/shortcut"; import type { CustomMcpServer } from "@/mcp/CustomMcpServer"; import { ObjectiveTools } from "./objectives"; describe("ObjectiveTools", () => { const mockCurrentUser = { id: "user1", mention_name: "testuser", name: "Test User", }; const mockMilestones: Milestone[] = [ { entity_type: "milestone", id: 1, name: "Objective 1", description: "Description for Objective 1", app_url: "https://app.shortcut.com/test/milestone/1", archived: false, completed: false, started: true, state: "started", categories: [], } as unknown as Milestone, { entity_type: "milestone", id: 2, name: "Objective 2", description: "Description for Objective 2", app_url: "https://app.shortcut.com/test/milestone/2", archived: false, completed: true, started: true, state: "completed", categories: [], } as unknown as Milestone, ]; describe("create method", () => { test("should register the correct tools with the server", () => { const mockClient = {} as unknown as ShortcutClientWrapper; const mockTool = mock(); const mockServer = { addToolWithReadAccess: mockTool } as unknown as CustomMcpServer; ObjectiveTools.create(mockClient, mockServer); expect(mockTool).toHaveBeenCalledTimes(2); expect(mockTool.mock.calls?.[0]?.[0]).toBe("objectives-get-by-id"); expect(mockTool.mock.calls?.[1]?.[0]).toBe("objectives-search"); }); }); describe("searchObjectives method", () => { const searchMilestonesMock = mock(async () => ({ milestones: mockMilestones, total: mockMilestones.length, })); const getCurrentUserMock = mock(async () => mockCurrentUser); const mockClient = { searchMilestones: searchMilestonesMock, getCurrentUser: getCurrentUserMock, getUserMap: mock(async () => new Map()), getWorkflowMap: mock(async () => new Map()), getTeamMap: mock(async () => new Map()), getMilestone: mock(async () => null), getIteration: mock(async () => null), getEpic: mock(async () => null), } as unknown as ShortcutClientWrapper; test("should return formatted list of objectives when objectives are found", async () => { const objectiveTools = new ObjectiveTools(mockClient); const result = await objectiveTools.searchObjectives({}); expect(result.content[0].type).toBe("text"); const textContent = String(result.content[0].text); expect(textContent).toContain("Result (2 shown of 2 total milestones found):"); expect(textContent).toContain('"id": 1'); expect(textContent).toContain('"name": "Objective 1"'); expect(textContent).toContain('"id": 2'); expect(textContent).toContain('"name": "Objective 2"'); }); test("should return no objectives found message when no objectives exist", async () => { const objectiveTools = new ObjectiveTools({ ...mockClient, searchMilestones: mock(async () => ({ milestones: [], total: 0 })), } as unknown as ShortcutClientWrapper); const result = await objectiveTools.searchObjectives({}); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toBe("Result: No milestones found."); }); test("should throw error when objectives search fails", async () => { const objectiveTools = new ObjectiveTools({ ...mockClient, searchMilestones: mock(async () => ({ milestones: null, total: null })), } as unknown as ShortcutClientWrapper); await expect(() => objectiveTools.searchObjectives({})).toThrow( "Failed to search for milestones matching your query", ); }); }); describe("getObjective method", () => { const getMilestoneMock = mock(async (id: number) => mockMilestones.find((milestone) => milestone.id === id), ); const mockClient = { getMilestone: getMilestoneMock, getUserMap: mock(async () => new Map()), getWorkflowMap: mock(async () => new Map()), getTeamMap: mock(async () => new Map()), getIteration: mock(async () => null), getEpic: mock(async () => null), } as unknown as ShortcutClientWrapper; test("should return formatted objective details when objective is found", async () => { const objectiveTools = new ObjectiveTools(mockClient); const result = await objectiveTools.getObjective(1, true); expect(result.content[0].type).toBe("text"); const textContent = String(result.content[0].text); expect(textContent).toContain("Objective: 1"); expect(textContent).toContain('"id": 1'); expect(textContent).toContain('"name": "Objective 1"'); expect(textContent).toContain('"description": "Description for Objective 1"'); expect(textContent).toContain('"archived": false'); expect(textContent).toContain('"completed": false'); expect(textContent).toContain('"started": true'); expect(textContent).toContain('"app_url": "https://app.shortcut.com/test/milestone/1"'); }); test("should return simplified objective when full = false", async () => { const objectiveTools = new ObjectiveTools(mockClient); const result = await objectiveTools.getObjective(1, false); expect(result.content[0].type).toBe("text"); const textContent = String(result.content[0].text); expect(textContent).toContain("Objective: 1"); expect(textContent).toContain('"id": 1'); expect(textContent).toContain('"name": "Objective 1"'); expect(textContent).toContain('"app_url": "https://app.shortcut.com/test/milestone/1"'); // Should have basic objective fields in simplified format expect(textContent).toContain('"archived"'); expect(textContent).toContain('"state"'); expect(textContent).toContain('"categories"'); }); test("should handle objective not found", async () => { const objectiveTools = new ObjectiveTools({ getMilestone: mock(async () => null), getUserMap: mock(async () => new Map()), getWorkflowMap: mock(async () => new Map()), getTeamMap: mock(async () => new Map()), getIteration: mock(async () => null), getEpic: mock(async () => null), } as unknown as ShortcutClientWrapper); await expect(() => objectiveTools.getObjective(999)).toThrow( "Failed to retrieve Shortcut objective with public ID: 999", ); }); test("should handle completed objective", async () => { const objectiveTools = new ObjectiveTools({ getMilestone: mock(async () => mockMilestones[1]), getUserMap: mock(async () => new Map()), getWorkflowMap: mock(async () => new Map()), getTeamMap: mock(async () => new Map()), getIteration: mock(async () => null), getEpic: mock(async () => null), } as unknown as ShortcutClientWrapper); const result = await objectiveTools.getObjective(2, true); expect(result.content[0].type).toBe("text"); expect(result.content[0].text).toContain('"completed": true'); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/useshortcut/mcp-server-shortcut'

If you have feedback or need assistance with the MCP directory API, please join our Discord server