trelly 0.1.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 (46) hide show
  1. package/README.md +191 -0
  2. package/bin/run-ts +31 -0
  3. package/bin/trelly +2 -0
  4. package/bin/trelly-mcp +2 -0
  5. package/package.json +64 -0
  6. package/src/api/client.ts +332 -0
  7. package/src/api/http.ts +86 -0
  8. package/src/auth/browser-flow.ts +217 -0
  9. package/src/auth/profiles.ts +163 -0
  10. package/src/cli/commands/actions.ts +17 -0
  11. package/src/cli/commands/api.ts +46 -0
  12. package/src/cli/commands/auth.ts +248 -0
  13. package/src/cli/commands/boards.ts +152 -0
  14. package/src/cli/commands/cards.ts +194 -0
  15. package/src/cli/commands/checklists.ts +79 -0
  16. package/src/cli/commands/custom-fields.ts +75 -0
  17. package/src/cli/commands/labels.ts +47 -0
  18. package/src/cli/commands/lists.ts +54 -0
  19. package/src/cli/commands/members.ts +14 -0
  20. package/src/cli/commands/orgs.ts +23 -0
  21. package/src/cli/commands/run.ts +32 -0
  22. package/src/cli/commands/search.ts +23 -0
  23. package/src/cli/commands/ui.ts +48 -0
  24. package/src/cli/commands/webhooks.ts +44 -0
  25. package/src/cli/context.ts +70 -0
  26. package/src/cli/index.ts +47 -0
  27. package/src/cli/ui/app.tsx +753 -0
  28. package/src/cli/ui/custom-fields.ts +75 -0
  29. package/src/cli/ui/palette.ts +69 -0
  30. package/src/cli/ui/shapes.ts +68 -0
  31. package/src/cli/ui/static.tsx +382 -0
  32. package/src/index.test.ts +117 -0
  33. package/src/mcp/handlers.ts +61 -0
  34. package/src/mcp/server.ts +17 -0
  35. package/src/mcp/tools/api.ts +27 -0
  36. package/src/mcp/tools/boards.ts +116 -0
  37. package/src/mcp/tools/cards.ts +138 -0
  38. package/src/mcp/tools/checklists.ts +40 -0
  39. package/src/mcp/tools/index.ts +22 -0
  40. package/src/mcp/tools/labels.ts +40 -0
  41. package/src/mcp/tools/lists.ts +37 -0
  42. package/src/mcp/tools/profiles.ts +40 -0
  43. package/src/mcp/tools/search.ts +31 -0
  44. package/src/mcp/tools/webhooks.ts +52 -0
  45. package/src/util/runtime.ts +39 -0
  46. package/src/version.ts +15 -0
@@ -0,0 +1,117 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import { parseTrelloResponse, trelloErrorMessage } from "./api/http.ts";
4
+ import { authLoginUrl } from "./auth/profiles.ts";
5
+ import { parseKvPairs } from "./cli/context.ts";
6
+ import { customFieldChips } from "./cli/ui/custom-fields.ts";
7
+ import { dueStatus, labelHex, listAccentHex } from "./cli/ui/palette.ts";
8
+ import { isBoard, isCard, isLabel, isList } from "./cli/ui/shapes.ts";
9
+
10
+ describe("parseKvPairs", () => {
11
+ it("parses key=value pairs", () => {
12
+ assert.deepEqual(parseKvPairs(["name=foo", "closed=true"]), {
13
+ name: "foo",
14
+ closed: "true",
15
+ });
16
+ });
17
+
18
+ it("rejects invalid pairs", () => {
19
+ assert.throws(() => parseKvPairs(["invalid"]), /Invalid key=value/);
20
+ });
21
+ });
22
+
23
+ describe("authLoginUrl", () => {
24
+ it("defaults to read,write and 30days", () => {
25
+ const url = new URL(authLoginUrl("test-key"));
26
+ assert.equal(url.searchParams.get("key"), "test-key");
27
+ assert.equal(url.searchParams.get("scope"), "read,write");
28
+ assert.equal(url.searchParams.get("expiration"), "30days");
29
+ });
30
+
31
+ it("supports callback return_url", () => {
32
+ const url = new URL(
33
+ authLoginUrl("test-key", {
34
+ returnUrl: "http://127.0.0.1:14189/callback",
35
+ }),
36
+ );
37
+ assert.equal(url.searchParams.get("callback_method"), "fragment");
38
+ assert.equal(url.searchParams.get("return_url"), "http://127.0.0.1:14189/callback");
39
+ });
40
+ });
41
+
42
+ describe("trello HTTP helpers", () => {
43
+ it("parses JSON responses", () => {
44
+ assert.deepEqual(parseTrelloResponse('{"id":"1"}'), { id: "1" });
45
+ });
46
+
47
+ it("extracts Trello error messages", () => {
48
+ assert.equal(
49
+ trelloErrorMessage({ message: "invalid token" }, 401),
50
+ "invalid token",
51
+ );
52
+ assert.equal(trelloErrorMessage(null, 500), "Trello API 500");
53
+ });
54
+ });
55
+
56
+ describe("ui palette", () => {
57
+ it("maps Trello label colors including shades", () => {
58
+ assert.equal(labelHex("green"), "#61bd4f");
59
+ assert.equal(labelHex("green_dark"), "#61bd4f");
60
+ assert.equal(labelHex(null), "#6b778c");
61
+ assert.equal(labelHex("mauve"), "#6b778c");
62
+ });
63
+
64
+ it("maps list accents, falling back to Trello blue", () => {
65
+ assert.equal(listAccentHex(null), "#0079bf");
66
+ assert.equal(listAccentHex(undefined), "#0079bf");
67
+ assert.equal(listAccentHex("teal"), "#6cc3e0");
68
+ assert.equal(listAccentHex("purple"), "#0079bf");
69
+ });
70
+
71
+ it("classifies due status", () => {
72
+ const now = new Date("2026-07-03T12:00:00Z");
73
+ assert.equal(dueStatus(null, false, now), "none");
74
+ assert.equal(dueStatus("2026-07-01T00:00:00Z", true, now), "complete");
75
+ assert.equal(dueStatus("2026-07-01T00:00:00Z", false, now), "overdue");
76
+ assert.equal(dueStatus("2026-07-03T18:00:00Z", false, now), "soon");
77
+ assert.equal(dueStatus("2026-08-01T00:00:00Z", false, now), "later");
78
+ });
79
+ });
80
+
81
+ describe("custom field chips", () => {
82
+ const defs = [
83
+ {
84
+ id: "f1",
85
+ name: "Priority",
86
+ type: "list",
87
+ cardFront: true,
88
+ options: [{ id: "o1", text: "Highest", color: "red" }],
89
+ },
90
+ { id: "f2", name: "Points", type: "number", cardFront: true, options: [] },
91
+ ];
92
+
93
+ it("resolves list options and raw values, skipping unknown fields", () => {
94
+ const chips = customFieldChips(defs, [
95
+ { idCustomField: "f1", idValue: "o1" },
96
+ { idCustomField: "f2", value: { number: "5" } },
97
+ { idCustomField: "missing", idValue: "x" },
98
+ ]);
99
+ assert.deepEqual(chips, [
100
+ { id: "f1", label: "Priority: Highest", color: "red" },
101
+ { id: "f2", label: "Points: 5", color: null },
102
+ ]);
103
+ });
104
+ });
105
+
106
+ describe("ui shapes", () => {
107
+ it("distinguishes cards, lists, labels, and boards", () => {
108
+ const card = { id: "c", name: "Card", idList: "l", idBoard: "b", pos: 1 };
109
+ const list = { id: "l", name: "List", idBoard: "b", pos: 2, closed: false };
110
+ const label = { id: "lb", name: "bug", color: "red", idBoard: "b" };
111
+ const board = { id: "b", name: "Board", prefs: {}, idOrganization: "o" };
112
+ assert.ok(isCard(card) && !isCard(list) && !isCard(board));
113
+ assert.ok(isList(list) && !isList(card) && !isList(label));
114
+ assert.ok(isLabel(label) && !isLabel(list) && !isLabel(card));
115
+ assert.ok(isBoard(board) && !isBoard(card) && !isBoard(list));
116
+ });
117
+ });
@@ -0,0 +1,61 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+ import { z } from "zod";
3
+ import { type JsonValue, type TrelloClient, TrelloError } from "../api/client.ts";
4
+ import { getClient } from "../cli/context.ts";
5
+
6
+ export const toolEnvelopeSchema = z.object({
7
+ ok: z.boolean(),
8
+ profile: z.string().optional(),
9
+ data: z.unknown().optional(),
10
+ error: z.string().optional(),
11
+ status: z.number().optional(),
12
+ details: z.unknown().optional(),
13
+ });
14
+
15
+ export type ToolEnvelope = z.infer<typeof toolEnvelopeSchema>;
16
+
17
+ export const profileField = z.string().optional().describe("Auth profile name");
18
+
19
+ export function toolResult(envelope: ToolEnvelope): CallToolResult {
20
+ return {
21
+ structuredContent: envelope,
22
+ content: [{ type: "text", text: JSON.stringify(envelope, null, 2) }],
23
+ isError: envelope.ok === false,
24
+ };
25
+ }
26
+
27
+ export async function withClient<T>(
28
+ profile: string | undefined,
29
+ fn: (client: TrelloClient, profileName: string) => Promise<T>,
30
+ ): Promise<CallToolResult> {
31
+ try {
32
+ const { profileName, client } = getClient(profile);
33
+ const data = await fn(client, profileName);
34
+ return toolResult({ ok: true, profile: profileName, data });
35
+ } catch (err) {
36
+ if (err instanceof TrelloError) {
37
+ return toolResult({
38
+ ok: false,
39
+ error: err.message,
40
+ status: err.status,
41
+ details: err.body,
42
+ });
43
+ }
44
+ return toolResult({
45
+ ok: false,
46
+ error: err instanceof Error ? err.message : String(err),
47
+ });
48
+ }
49
+ }
50
+
51
+ export async function runApi(
52
+ profile: string | undefined,
53
+ method: string,
54
+ path: string,
55
+ query: Record<string, string> | undefined,
56
+ body: JsonValue | undefined,
57
+ ): Promise<CallToolResult> {
58
+ return withClient(profile, (client) =>
59
+ client.request(method.toUpperCase(), path, query ?? {}, body),
60
+ );
61
+ }
@@ -0,0 +1,17 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { packageVersion } from "../version.ts";
4
+ import { registerTrelloTools } from "./tools/index.ts";
5
+
6
+ const server = new McpServer({ name: "trelly", version: packageVersion() });
7
+ registerTrelloTools(server);
8
+
9
+ async function main(): Promise<void> {
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ }
13
+
14
+ main().catch((err) => {
15
+ console.error(err);
16
+ process.exit(1);
17
+ });
@@ -0,0 +1,27 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import type { JsonValue } from "../../api/client.ts";
4
+ import { profileField, runApi, toolEnvelopeSchema } from "../handlers.ts";
5
+
6
+ export function registerApiTools(server: McpServer): void {
7
+ const outputSchema = toolEnvelopeSchema;
8
+
9
+ server.registerTool(
10
+ "trello_api",
11
+ {
12
+ description:
13
+ "Raw Trello REST escape hatch. path like /boards/{id}. Prefer specific tools when possible.",
14
+ inputSchema: {
15
+ profile: profileField,
16
+ method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET"),
17
+ path: z.string().min(1),
18
+ query: z.record(z.string(), z.string()).optional(),
19
+ body: z.unknown().optional(),
20
+ },
21
+ annotations: { destructiveHint: true },
22
+ outputSchema,
23
+ },
24
+ async ({ profile, method, path, query, body }) =>
25
+ runApi(profile, method, path, query, body as JsonValue | undefined),
26
+ );
27
+ }
@@ -0,0 +1,116 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
4
+
5
+ export function registerBoardTools(server: McpServer): void {
6
+ const outputSchema = toolEnvelopeSchema;
7
+
8
+ server.registerTool(
9
+ "trello_boards_list",
10
+ {
11
+ description: "List boards visible to the authenticated member.",
12
+ inputSchema: {
13
+ profile: profileField,
14
+ filter: z
15
+ .enum([
16
+ "all",
17
+ "closed",
18
+ "memberships",
19
+ "open",
20
+ "organization",
21
+ "pinned",
22
+ "public",
23
+ "starred",
24
+ "unpinned",
25
+ ])
26
+ .optional()
27
+ .default("open"),
28
+ fields: z.string().optional(),
29
+ },
30
+ annotations: { readOnlyHint: true },
31
+ outputSchema,
32
+ },
33
+ async ({ profile, filter, fields }) =>
34
+ withClient(profile, (client) => client.memberBoards("me", { filter, fields })),
35
+ );
36
+
37
+ server.registerTool(
38
+ "trello_board_get",
39
+ {
40
+ description: "Get a board by id.",
41
+ inputSchema: {
42
+ profile: profileField,
43
+ boardId: z.string().min(1),
44
+ fields: z.string().optional(),
45
+ },
46
+ annotations: { readOnlyHint: true },
47
+ outputSchema,
48
+ },
49
+ async ({ profile, boardId, fields }) =>
50
+ withClient(profile, (client) => client.boardGet(boardId, { fields })),
51
+ );
52
+
53
+ server.registerTool(
54
+ "trello_board_create",
55
+ {
56
+ description: "Create a board.",
57
+ inputSchema: {
58
+ profile: profileField,
59
+ name: z.string().min(1),
60
+ description: z.string().optional(),
61
+ organizationId: z.string().optional(),
62
+ defaultLists: z.boolean().optional(),
63
+ },
64
+ outputSchema,
65
+ },
66
+ async ({ profile, name, description, organizationId, defaultLists }) =>
67
+ withClient(profile, (client) =>
68
+ client.boardCreate({
69
+ name,
70
+ desc: description,
71
+ idOrganization: organizationId,
72
+ defaultLists,
73
+ }),
74
+ ),
75
+ );
76
+
77
+ server.registerTool(
78
+ "trello_board_archive",
79
+ {
80
+ description: "Close (archive) a board. Reversible in the Trello UI.",
81
+ inputSchema: { profile: profileField, boardId: z.string().min(1) },
82
+ annotations: { destructiveHint: true },
83
+ outputSchema,
84
+ },
85
+ async ({ profile, boardId }) =>
86
+ withClient(profile, (client) => client.boardArchive(boardId)),
87
+ );
88
+
89
+ server.registerTool(
90
+ "trello_board_lists",
91
+ {
92
+ description: "List lists on a board.",
93
+ inputSchema: {
94
+ profile: profileField,
95
+ boardId: z.string().min(1),
96
+ filter: z.string().optional().default("open"),
97
+ },
98
+ annotations: { readOnlyHint: true },
99
+ outputSchema,
100
+ },
101
+ async ({ profile, boardId, filter }) =>
102
+ withClient(profile, (client) => client.boardLists(boardId, { filter })),
103
+ );
104
+
105
+ server.registerTool(
106
+ "trello_board_cards",
107
+ {
108
+ description: "List all cards on a board.",
109
+ inputSchema: { profile: profileField, boardId: z.string().min(1) },
110
+ annotations: { readOnlyHint: true },
111
+ outputSchema,
112
+ },
113
+ async ({ profile, boardId }) =>
114
+ withClient(profile, (client) => client.boardCards(boardId)),
115
+ );
116
+ }
@@ -0,0 +1,138 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
4
+
5
+ export function registerCardTools(server: McpServer): void {
6
+ const outputSchema = toolEnvelopeSchema;
7
+
8
+ server.registerTool(
9
+ "trello_card_get",
10
+ {
11
+ description: "Get a card by id or short link.",
12
+ inputSchema: {
13
+ profile: profileField,
14
+ cardId: z.string().min(1),
15
+ fields: z.string().optional(),
16
+ },
17
+ annotations: { readOnlyHint: true },
18
+ outputSchema,
19
+ },
20
+ async ({ profile, cardId, fields }) =>
21
+ withClient(profile, (client) => client.cardGet(cardId, { fields })),
22
+ );
23
+
24
+ server.registerTool(
25
+ "trello_card_create",
26
+ {
27
+ description: "Create a card on a list.",
28
+ inputSchema: {
29
+ profile: profileField,
30
+ listId: z.string().min(1),
31
+ name: z.string().min(1),
32
+ description: z.string().optional(),
33
+ due: z.string().optional(),
34
+ position: z.string().optional(),
35
+ },
36
+ outputSchema,
37
+ },
38
+ async ({ profile, listId, name, description, due, position }) =>
39
+ withClient(profile, (client) =>
40
+ client.cardCreate({
41
+ idList: listId,
42
+ name,
43
+ desc: description,
44
+ due,
45
+ pos: position,
46
+ }),
47
+ ),
48
+ );
49
+
50
+ server.registerTool(
51
+ "trello_card_update",
52
+ {
53
+ description: "Update card fields (name, desc, due, closed, idList, etc.).",
54
+ inputSchema: {
55
+ profile: profileField,
56
+ cardId: z.string().min(1),
57
+ fields: z.record(z.string(), z.string()),
58
+ },
59
+ outputSchema,
60
+ },
61
+ async ({ profile, cardId, fields }) =>
62
+ withClient(profile, (client) => client.cardUpdate(cardId, fields)),
63
+ );
64
+
65
+ server.registerTool(
66
+ "trello_card_move",
67
+ {
68
+ description: "Move a card to another list.",
69
+ inputSchema: {
70
+ profile: profileField,
71
+ cardId: z.string().min(1),
72
+ listId: z.string().min(1),
73
+ position: z.string().optional(),
74
+ },
75
+ outputSchema,
76
+ },
77
+ async ({ profile, cardId, listId, position }) =>
78
+ withClient(profile, (client) =>
79
+ client.cardUpdate(cardId, { idList: listId, pos: position }),
80
+ ),
81
+ );
82
+
83
+ server.registerTool(
84
+ "trello_card_comments",
85
+ {
86
+ description: "List comments on a card.",
87
+ inputSchema: {
88
+ profile: profileField,
89
+ cardId: z.string().min(1),
90
+ limit: z.number().int().positive().optional(),
91
+ },
92
+ annotations: { readOnlyHint: true },
93
+ outputSchema,
94
+ },
95
+ async ({ profile, cardId, limit }) =>
96
+ withClient(profile, (client) => client.cardComments(cardId, { limit })),
97
+ );
98
+
99
+ server.registerTool(
100
+ "trello_card_comment",
101
+ {
102
+ description: "Add a comment to a card.",
103
+ inputSchema: {
104
+ profile: profileField,
105
+ cardId: z.string().min(1),
106
+ text: z.string().min(1),
107
+ },
108
+ outputSchema,
109
+ },
110
+ async ({ profile, cardId, text }) =>
111
+ withClient(profile, (client) => client.cardComment(cardId, text)),
112
+ );
113
+
114
+ server.registerTool(
115
+ "trello_card_archive",
116
+ {
117
+ description: "Close (archive) a card. Reversible in the Trello UI.",
118
+ inputSchema: { profile: profileField, cardId: z.string().min(1) },
119
+ annotations: { destructiveHint: true },
120
+ outputSchema,
121
+ },
122
+ async ({ profile, cardId }) =>
123
+ withClient(profile, (client) => client.cardArchive(cardId)),
124
+ );
125
+
126
+ server.registerTool(
127
+ "trello_card_delete",
128
+ {
129
+ description:
130
+ "Permanently delete a card. Irreversible — prefer trello_card_archive to close.",
131
+ inputSchema: { profile: profileField, cardId: z.string().min(1) },
132
+ annotations: { destructiveHint: true },
133
+ outputSchema,
134
+ },
135
+ async ({ profile, cardId }) =>
136
+ withClient(profile, (client) => client.cardDelete(cardId)),
137
+ );
138
+ }
@@ -0,0 +1,40 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
4
+
5
+ export function registerChecklistTools(server: McpServer): void {
6
+ const outputSchema = toolEnvelopeSchema;
7
+
8
+ server.registerTool(
9
+ "trello_checklist_create",
10
+ {
11
+ description: "Create a checklist on a card.",
12
+ inputSchema: {
13
+ profile: profileField,
14
+ cardId: z.string().min(1),
15
+ name: z.string().min(1),
16
+ },
17
+ outputSchema,
18
+ },
19
+ async ({ profile, cardId, name }) =>
20
+ withClient(profile, (client) => client.checklistCreate({ idCard: cardId, name })),
21
+ );
22
+
23
+ server.registerTool(
24
+ "trello_checklist_add_item",
25
+ {
26
+ description: "Add an item to a checklist.",
27
+ inputSchema: {
28
+ profile: profileField,
29
+ checklistId: z.string().min(1),
30
+ name: z.string().min(1),
31
+ checked: z.boolean().optional(),
32
+ },
33
+ outputSchema,
34
+ },
35
+ async ({ profile, checklistId, name, checked }) =>
36
+ withClient(profile, (client) =>
37
+ client.checklistAddItem(checklistId, { name, checked }),
38
+ ),
39
+ );
40
+ }
@@ -0,0 +1,22 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { registerApiTools } from "./api.ts";
3
+ import { registerBoardTools } from "./boards.ts";
4
+ import { registerCardTools } from "./cards.ts";
5
+ import { registerChecklistTools } from "./checklists.ts";
6
+ import { registerLabelTools } from "./labels.ts";
7
+ import { registerListTools } from "./lists.ts";
8
+ import { registerProfileTools } from "./profiles.ts";
9
+ import { registerSearchTools } from "./search.ts";
10
+ import { registerWebhookTools } from "./webhooks.ts";
11
+
12
+ export function registerTrelloTools(server: McpServer): void {
13
+ registerProfileTools(server);
14
+ registerBoardTools(server);
15
+ registerListTools(server);
16
+ registerCardTools(server);
17
+ registerChecklistTools(server);
18
+ registerLabelTools(server);
19
+ registerSearchTools(server);
20
+ registerWebhookTools(server);
21
+ registerApiTools(server);
22
+ }
@@ -0,0 +1,40 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
4
+
5
+ export function registerLabelTools(server: McpServer): void {
6
+ const outputSchema = toolEnvelopeSchema;
7
+
8
+ server.registerTool(
9
+ "trello_label_create",
10
+ {
11
+ description: "Create a board label.",
12
+ inputSchema: {
13
+ profile: profileField,
14
+ boardId: z.string().min(1),
15
+ name: z.string().min(1),
16
+ color: z.string().min(1),
17
+ },
18
+ outputSchema,
19
+ },
20
+ async ({ profile, boardId, name, color }) =>
21
+ withClient(profile, (client) =>
22
+ client.labelCreate({ idBoard: boardId, name, color }),
23
+ ),
24
+ );
25
+
26
+ server.registerTool(
27
+ "trello_card_add_label",
28
+ {
29
+ description: "Add a label to a card.",
30
+ inputSchema: {
31
+ profile: profileField,
32
+ cardId: z.string().min(1),
33
+ labelId: z.string().min(1),
34
+ },
35
+ outputSchema,
36
+ },
37
+ async ({ profile, cardId, labelId }) =>
38
+ withClient(profile, (client) => client.cardAddLabel(cardId, labelId)),
39
+ );
40
+ }
@@ -0,0 +1,37 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
4
+
5
+ export function registerListTools(server: McpServer): void {
6
+ const outputSchema = toolEnvelopeSchema;
7
+
8
+ server.registerTool(
9
+ "trello_list_create",
10
+ {
11
+ description: "Create a list on a board.",
12
+ inputSchema: {
13
+ profile: profileField,
14
+ boardId: z.string().min(1),
15
+ name: z.string().min(1),
16
+ position: z.string().optional(),
17
+ },
18
+ outputSchema,
19
+ },
20
+ async ({ profile, boardId, name, position }) =>
21
+ withClient(profile, (client) =>
22
+ client.listCreate({ idBoard: boardId, name, pos: position }),
23
+ ),
24
+ );
25
+
26
+ server.registerTool(
27
+ "trello_list_cards",
28
+ {
29
+ description: "List cards in a list.",
30
+ inputSchema: { profile: profileField, listId: z.string().min(1) },
31
+ annotations: { readOnlyHint: true },
32
+ outputSchema,
33
+ },
34
+ async ({ profile, listId }) =>
35
+ withClient(profile, (client) => client.listCards(listId)),
36
+ );
37
+ }
@@ -0,0 +1,40 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
3
+
4
+ export function registerProfileTools(server: McpServer): void {
5
+ const outputSchema = toolEnvelopeSchema;
6
+
7
+ server.registerTool(
8
+ "trello_profiles_list",
9
+ {
10
+ description: "List saved Trello auth profiles and the active default.",
11
+ annotations: { readOnlyHint: true },
12
+ outputSchema,
13
+ },
14
+ async () =>
15
+ withClient(undefined, async (_client, profileName) => {
16
+ const { loadConfig } = await import("../../auth/profiles.ts");
17
+ const config = loadConfig();
18
+ return {
19
+ activeProfile: profileName,
20
+ defaultProfile: config.defaultProfile,
21
+ profiles: Object.entries(config.profiles).map(([name, p]) => ({
22
+ name,
23
+ label: p.label,
24
+ isDefault: name === config.defaultProfile,
25
+ })),
26
+ };
27
+ }),
28
+ );
29
+
30
+ server.registerTool(
31
+ "trello_member_me",
32
+ {
33
+ description: "Get the authenticated Trello member.",
34
+ inputSchema: { profile: profileField },
35
+ annotations: { readOnlyHint: true },
36
+ outputSchema,
37
+ },
38
+ async ({ profile }) => withClient(profile, (client) => client.memberMe()),
39
+ );
40
+ }
@@ -0,0 +1,31 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { profileField, toolEnvelopeSchema, withClient } from "../handlers.ts";
4
+
5
+ export function registerSearchTools(server: McpServer): void {
6
+ const outputSchema = toolEnvelopeSchema;
7
+
8
+ server.registerTool(
9
+ "trello_search",
10
+ {
11
+ description: "Search Trello for cards, boards, members, and actions.",
12
+ inputSchema: {
13
+ profile: profileField,
14
+ query: z.string().min(1),
15
+ modelTypes: z.string().optional(),
16
+ cardsLimit: z.number().int().positive().optional(),
17
+ boardsLimit: z.number().int().positive().optional(),
18
+ },
19
+ annotations: { readOnlyHint: true },
20
+ outputSchema,
21
+ },
22
+ async ({ profile, query, modelTypes, cardsLimit, boardsLimit }) =>
23
+ withClient(profile, (client) =>
24
+ client.search(query, {
25
+ modelTypes,
26
+ cards_limit: cardsLimit,
27
+ boards_limit: boardsLimit,
28
+ }),
29
+ ),
30
+ );
31
+ }