trollo-mcp-server 1.0.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 +145 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.js +33 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -0
- package/dist/tools/boards.d.ts +3 -0
- package/dist/tools/boards.js +15 -0
- package/dist/tools/cards.d.ts +3 -0
- package/dist/tools/cards.js +76 -0
- package/dist/tools/checklists.d.ts +3 -0
- package/dist/tools/checklists.js +30 -0
- package/dist/tools/comments.d.ts +3 -0
- package/dist/tools/comments.js +11 -0
- package/dist/tools/lists.d.ts +3 -0
- package/dist/tools/lists.js +34 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Trollo MCP Server
|
|
2
|
+
|
|
3
|
+
An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that lets AI agents manage [Trollo](https://trollo.lol) kanban boards. Works with any MCP-compatible client: Cursor, Claude Desktop, and others.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Using npx (no install)
|
|
8
|
+
|
|
9
|
+
The fastest way to get started. Your MCP client runs the server on demand:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"trollo": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "trollo-mcp-server"],
|
|
17
|
+
"env": {
|
|
18
|
+
"TROLLO_URL": "https://trollo.lol",
|
|
19
|
+
"TROLLO_API_KEY": "trlo_your_key_here"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Global install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g trollo-mcp-server
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then configure your client to run `trollo-mcp`:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"trollo": {
|
|
38
|
+
"command": "trollo-mcp",
|
|
39
|
+
"env": {
|
|
40
|
+
"TROLLO_URL": "https://trollo.lol",
|
|
41
|
+
"TROLLO_API_KEY": "trlo_your_key_here"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### From source (this repo)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd mcp
|
|
52
|
+
npm install
|
|
53
|
+
npm run build
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then point your client to the local build:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"trollo": {
|
|
62
|
+
"command": "node",
|
|
63
|
+
"args": ["mcp/dist/index.js"],
|
|
64
|
+
"env": {
|
|
65
|
+
"TROLLO_URL": "https://trollo.lol",
|
|
66
|
+
"TROLLO_API_KEY": "trlo_your_key_here"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
| Environment Variable | Description |
|
|
76
|
+
|---|---|
|
|
77
|
+
| `TROLLO_URL` | Base URL of the Trollo instance (e.g. `https://trollo.lol`) |
|
|
78
|
+
| `TROLLO_API_KEY` | API key starting with `trlo_` — create one in Trollo under Settings > API Keys |
|
|
79
|
+
|
|
80
|
+
## Where to put the config
|
|
81
|
+
|
|
82
|
+
| Client | Config file |
|
|
83
|
+
|---|---|
|
|
84
|
+
| Cursor | `.cursor/mcp.json` in your project or `~/.cursor/mcp.json` globally |
|
|
85
|
+
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows) |
|
|
86
|
+
|
|
87
|
+
## Tools
|
|
88
|
+
|
|
89
|
+
### Reading
|
|
90
|
+
|
|
91
|
+
| Tool | Description |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `list_boards` | List all boards accessible to the API key |
|
|
94
|
+
| `get_board` | Get full board state: lists, cards, checklists, comments, labels, users |
|
|
95
|
+
|
|
96
|
+
### Lists
|
|
97
|
+
|
|
98
|
+
| Tool | Parameters | Description |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `add_list` | `boardId`, `title`, `position?` | Add a new list |
|
|
101
|
+
| `rename_list` | `boardId`, `listId`, `title` | Rename a list |
|
|
102
|
+
| `move_list` | `boardId`, `listId`, `position` | Reorder a list |
|
|
103
|
+
| `remove_list` | `boardId`, `listId` | Remove a list |
|
|
104
|
+
|
|
105
|
+
### Cards
|
|
106
|
+
|
|
107
|
+
| Tool | Parameters | Description |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `add_card` | `boardId`, `listId`, `title` | Add a card to a list |
|
|
110
|
+
| `move_card` | `boardId`, `cardId`, `toListId`, `position` | Move a card |
|
|
111
|
+
| `remove_card` | `boardId`, `cardId`, `fromListId` | Remove a card |
|
|
112
|
+
| `edit_card` | `boardId`, `cardId`, `title?`, `description?` | Edit title and/or description |
|
|
113
|
+
| `set_card_due` | `boardId`, `cardId`, `due?` | Set or remove due date |
|
|
114
|
+
| `set_card_points` | `boardId`, `cardId`, `points?` | Set or remove story points |
|
|
115
|
+
| `manage_card_label` | `boardId`, `cardId`, `labelIndex`, `action` | Add or remove a label |
|
|
116
|
+
|
|
117
|
+
### Comments
|
|
118
|
+
|
|
119
|
+
| Tool | Parameters | Description |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `add_comment` | `boardId`, `cardId`, `body` | Add a comment (supports markdown) |
|
|
122
|
+
|
|
123
|
+
### Checklists
|
|
124
|
+
|
|
125
|
+
| Tool | Parameters | Description |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `add_checklist` | `boardId`, `cardId`, `title` | Add a checklist to a card |
|
|
128
|
+
| `add_checklist_item` | `boardId`, `cardId`, `checklistId`, `title` | Add a checklist item |
|
|
129
|
+
| `toggle_checklist_item` | `boardId`, `cardId`, `itemId`, `checked` | Check or uncheck an item |
|
|
130
|
+
|
|
131
|
+
## API Key Permissions
|
|
132
|
+
|
|
133
|
+
Each API key in Trollo has per-action permissions. If a tool call is denied, the key may not have the required permission enabled. Manage permissions in Trollo under Settings > API Keys.
|
|
134
|
+
|
|
135
|
+
## Publishing
|
|
136
|
+
|
|
137
|
+
To publish a new version to npm:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
cd mcp
|
|
141
|
+
npm version patch # or minor / major
|
|
142
|
+
npm publish
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The `prepublishOnly` script builds automatically before publishing.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface TrolloBoard {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
shared: number;
|
|
5
|
+
archived?: boolean;
|
|
6
|
+
groupId?: number;
|
|
7
|
+
isOwner: boolean;
|
|
8
|
+
role: number | string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface ActionResult {
|
|
11
|
+
result: string;
|
|
12
|
+
userId?: number;
|
|
13
|
+
value?: string | number | null;
|
|
14
|
+
}
|
|
15
|
+
export declare class TrolloClient {
|
|
16
|
+
private baseUrl;
|
|
17
|
+
private apiKey;
|
|
18
|
+
constructor(baseUrl: string, apiKey: string);
|
|
19
|
+
private request;
|
|
20
|
+
listBoards(): Promise<{
|
|
21
|
+
boards: TrolloBoard[];
|
|
22
|
+
}>;
|
|
23
|
+
getBoard(boardId: string): Promise<{
|
|
24
|
+
board: Record<string, unknown>;
|
|
25
|
+
}>;
|
|
26
|
+
performAction(boardId: string, type: string, data: unknown[]): Promise<ActionResult>;
|
|
27
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export class TrolloClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
apiKey;
|
|
4
|
+
constructor(baseUrl, apiKey) {
|
|
5
|
+
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
}
|
|
8
|
+
async request(method, path, body) {
|
|
9
|
+
const url = `${this.baseUrl}${path}`;
|
|
10
|
+
const res = await fetch(url, {
|
|
11
|
+
method,
|
|
12
|
+
headers: {
|
|
13
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
14
|
+
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
|
|
15
|
+
},
|
|
16
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok) {
|
|
19
|
+
const text = await res.text().catch(() => res.statusText);
|
|
20
|
+
throw new Error(`Trollo API ${method} ${path} failed (${res.status}): ${text}`);
|
|
21
|
+
}
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
24
|
+
async listBoards() {
|
|
25
|
+
return this.request('GET', '/api/v1/boards');
|
|
26
|
+
}
|
|
27
|
+
async getBoard(boardId) {
|
|
28
|
+
return this.request('GET', `/api/v1/boards/${encodeURIComponent(boardId)}`);
|
|
29
|
+
}
|
|
30
|
+
async performAction(boardId, type, data) {
|
|
31
|
+
return this.request('POST', `/api/v1/boards/${encodeURIComponent(boardId)}/action`, { type, data });
|
|
32
|
+
}
|
|
33
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { TrolloClient } from './client.js';
|
|
5
|
+
import { registerBoardTools } from './tools/boards.js';
|
|
6
|
+
import { registerListTools } from './tools/lists.js';
|
|
7
|
+
import { registerCardTools } from './tools/cards.js';
|
|
8
|
+
import { registerCommentTools } from './tools/comments.js';
|
|
9
|
+
import { registerChecklistTools } from './tools/checklists.js';
|
|
10
|
+
const url = process.env.TROLLO_URL;
|
|
11
|
+
const key = process.env.TROLLO_API_KEY;
|
|
12
|
+
if (!url || !key) {
|
|
13
|
+
console.error('Missing required environment variables: TROLLO_URL and TROLLO_API_KEY');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const client = new TrolloClient(url, key);
|
|
17
|
+
const server = new McpServer({ name: 'trollo', version: '1.0.0' }, {
|
|
18
|
+
capabilities: { tools: {} },
|
|
19
|
+
instructions: [
|
|
20
|
+
'Trollo is a Trello-like kanban board app.',
|
|
21
|
+
'',
|
|
22
|
+
'Data model: Board contains labels[] ({color, title?} indexed 0-based), users[] ({id, name, email}), and lists[] ({id, title}). Each list contains cards[] ({id, title, slug, description, due?, points?}). Each card has labels[] (array of label indices), users[] (array of user IDs), checklists[] ({id, title} with items[] ({id, title, checked?})), comments[] ({id, body, userId}), and files[] ({id, name, url}).',
|
|
23
|
+
'',
|
|
24
|
+
'All entity IDs are integers. Always call get_board first to discover IDs before performing actions.',
|
|
25
|
+
'',
|
|
26
|
+
'Workflow patterns:',
|
|
27
|
+
'- Reading: call list_boards to find board IDs, then get_board for the full state.',
|
|
28
|
+
'- Adding cards: get_board to find the target listId, then add_card with boardId, listId, and title.',
|
|
29
|
+
'- Moving cards between lists: get_board to read source list cards, then move_card for each card with its id, the destination listId, and position.',
|
|
30
|
+
'- Checklists: add_checklist on a card, note the returned checklist ID, then add_checklist_item for each item.',
|
|
31
|
+
'',
|
|
32
|
+
'Limitations: encrypted boards are not accessible, file uploads are not supported, viewer-role keys cannot write, each API key has per-action permissions.',
|
|
33
|
+
].join('\n'),
|
|
34
|
+
});
|
|
35
|
+
registerBoardTools(server, client);
|
|
36
|
+
registerListTools(server, client);
|
|
37
|
+
registerCardTools(server, client);
|
|
38
|
+
registerCommentTools(server, client);
|
|
39
|
+
registerChecklistTools(server, client);
|
|
40
|
+
const transport = new StdioServerTransport();
|
|
41
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerBoardTools(server, client) {
|
|
3
|
+
server.tool('list_boards', 'List all Trollo boards accessible to the configured API key', async () => {
|
|
4
|
+
const { boards } = await client.listBoards();
|
|
5
|
+
return {
|
|
6
|
+
content: [{ type: 'text', text: JSON.stringify(boards, null, 2) }],
|
|
7
|
+
};
|
|
8
|
+
});
|
|
9
|
+
server.tool('get_board', 'Get full board state including lists, cards, labels, users, checklists, and comments', { boardId: z.string().describe('The board ID') }, async ({ boardId }) => {
|
|
10
|
+
const { board } = await client.getBoard(boardId);
|
|
11
|
+
return {
|
|
12
|
+
content: [{ type: 'text', text: JSON.stringify(board, null, 2) }],
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerCardTools(server, client) {
|
|
3
|
+
server.tool('add_card', 'Add a new card to a list', {
|
|
4
|
+
boardId: z.string().describe('The board ID'),
|
|
5
|
+
listId: z.number().int().describe('The list ID to add the card to'),
|
|
6
|
+
title: z.string().describe('Title for the new card'),
|
|
7
|
+
}, async ({ boardId, listId, title }) => {
|
|
8
|
+
const result = await client.performAction(boardId, 'CardAdded', [listId, title]);
|
|
9
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
10
|
+
});
|
|
11
|
+
server.tool('move_card', 'Move a card to a different list and/or position', {
|
|
12
|
+
boardId: z.string().describe('The board ID'),
|
|
13
|
+
cardId: z.number().int().describe('The card ID to move'),
|
|
14
|
+
toListId: z.number().int().describe('Destination list ID'),
|
|
15
|
+
position: z.number().int().describe('Position index in the destination list'),
|
|
16
|
+
}, async ({ boardId, cardId, toListId, position }) => {
|
|
17
|
+
const result = await client.performAction(boardId, 'CardMoved', [cardId, toListId, position]);
|
|
18
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
19
|
+
});
|
|
20
|
+
server.tool('remove_card', 'Remove a card from a list', {
|
|
21
|
+
boardId: z.string().describe('The board ID'),
|
|
22
|
+
cardId: z.number().int().describe('The card ID to remove'),
|
|
23
|
+
fromListId: z.number().int().describe('The list ID the card belongs to'),
|
|
24
|
+
}, async ({ boardId, cardId, fromListId }) => {
|
|
25
|
+
const result = await client.performAction(boardId, 'CardRemoved', [cardId, fromListId]);
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
27
|
+
});
|
|
28
|
+
server.tool('edit_card', 'Edit a card\'s title and/or description. Provide at least one of title or description.', {
|
|
29
|
+
boardId: z.string().describe('The board ID'),
|
|
30
|
+
cardId: z.number().int().describe('The card ID to edit'),
|
|
31
|
+
title: z.string().optional().describe('New title (omit to leave unchanged)'),
|
|
32
|
+
description: z.string().optional().describe('New description in markdown (omit to leave unchanged)'),
|
|
33
|
+
}, async ({ boardId, cardId, title, description }) => {
|
|
34
|
+
const results = [];
|
|
35
|
+
if (title !== undefined) {
|
|
36
|
+
results.push(await client.performAction(boardId, 'CardTitleChange', [cardId, title]));
|
|
37
|
+
}
|
|
38
|
+
if (description !== undefined) {
|
|
39
|
+
results.push(await client.performAction(boardId, 'CardDescriptionChange', [cardId, description]));
|
|
40
|
+
}
|
|
41
|
+
if (results.length === 0) {
|
|
42
|
+
return { content: [{ type: 'text', text: 'No changes specified — provide title and/or description.' }], isError: true };
|
|
43
|
+
}
|
|
44
|
+
return { content: [{ type: 'text', text: JSON.stringify(results.length === 1 ? results[0] : results) }] };
|
|
45
|
+
});
|
|
46
|
+
server.tool('set_card_due', 'Set or remove a card\'s due date', {
|
|
47
|
+
boardId: z.string().describe('The board ID'),
|
|
48
|
+
cardId: z.number().int().describe('The card ID'),
|
|
49
|
+
due: z.number().optional().describe('Due date as Unix timestamp in milliseconds. Omit to remove the due date.'),
|
|
50
|
+
}, async ({ boardId, cardId, due }) => {
|
|
51
|
+
const result = due !== undefined
|
|
52
|
+
? await client.performAction(boardId, 'CardDueSet', [cardId, due])
|
|
53
|
+
: await client.performAction(boardId, 'CardDueRemoved', [cardId]);
|
|
54
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
55
|
+
});
|
|
56
|
+
server.tool('set_card_points', 'Set or remove story points on a card', {
|
|
57
|
+
boardId: z.string().describe('The board ID'),
|
|
58
|
+
cardId: z.number().int().describe('The card ID'),
|
|
59
|
+
points: z.number().optional().describe('Story points value. Omit to remove.'),
|
|
60
|
+
}, async ({ boardId, cardId, points }) => {
|
|
61
|
+
const result = points !== undefined
|
|
62
|
+
? await client.performAction(boardId, 'CardPointsSet', [cardId, points])
|
|
63
|
+
: await client.performAction(boardId, 'CardPointsRemoved', [cardId]);
|
|
64
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
65
|
+
});
|
|
66
|
+
server.tool('manage_card_label', 'Add or remove a label on a card', {
|
|
67
|
+
boardId: z.string().describe('The board ID'),
|
|
68
|
+
cardId: z.number().int().describe('The card ID'),
|
|
69
|
+
labelIndex: z.number().int().describe('Index of the label in the board\'s labels array'),
|
|
70
|
+
action: z.enum(['add', 'remove']).describe('Whether to add or remove the label'),
|
|
71
|
+
}, async ({ boardId, cardId, labelIndex, action }) => {
|
|
72
|
+
const type = action === 'add' ? 'CardLabelAdd' : 'CardLabelRemove';
|
|
73
|
+
const result = await client.performAction(boardId, type, [cardId, labelIndex]);
|
|
74
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerChecklistTools(server, client) {
|
|
3
|
+
server.tool('add_checklist', 'Add a checklist to a card', {
|
|
4
|
+
boardId: z.string().describe('The board ID'),
|
|
5
|
+
cardId: z.number().int().describe('The card ID to add the checklist to'),
|
|
6
|
+
title: z.string().describe('Checklist title'),
|
|
7
|
+
}, async ({ boardId, cardId, title }) => {
|
|
8
|
+
const result = await client.performAction(boardId, 'CLAdded', [cardId, title]);
|
|
9
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
10
|
+
});
|
|
11
|
+
server.tool('add_checklist_item', 'Add an item to an existing checklist', {
|
|
12
|
+
boardId: z.string().describe('The board ID'),
|
|
13
|
+
cardId: z.number().int().describe('The card ID'),
|
|
14
|
+
checklistId: z.number().int().describe('The checklist ID'),
|
|
15
|
+
title: z.string().describe('Item title'),
|
|
16
|
+
}, async ({ boardId, cardId, checklistId, title }) => {
|
|
17
|
+
const result = await client.performAction(boardId, 'CLIAdded', [cardId, checklistId, title]);
|
|
18
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
19
|
+
});
|
|
20
|
+
server.tool('toggle_checklist_item', 'Check or uncheck a checklist item', {
|
|
21
|
+
boardId: z.string().describe('The board ID'),
|
|
22
|
+
cardId: z.number().int().describe('The card ID'),
|
|
23
|
+
itemId: z.number().int().describe('The checklist item ID'),
|
|
24
|
+
checked: z.boolean().describe('true to check, false to uncheck'),
|
|
25
|
+
}, async ({ boardId, cardId, itemId, checked }) => {
|
|
26
|
+
const type = checked ? 'CLIChecked' : 'CLIUnchecked';
|
|
27
|
+
const result = await client.performAction(boardId, type, [cardId, itemId]);
|
|
28
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerCommentTools(server, client) {
|
|
3
|
+
server.tool('add_comment', 'Add a comment to a card', {
|
|
4
|
+
boardId: z.string().describe('The board ID'),
|
|
5
|
+
cardId: z.number().int().describe('The card ID to comment on'),
|
|
6
|
+
body: z.string().describe('Comment body (supports markdown)'),
|
|
7
|
+
}, async ({ boardId, cardId, body }) => {
|
|
8
|
+
const result = await client.performAction(boardId, 'CMAdded', [cardId, body]);
|
|
9
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerListTools(server, client) {
|
|
3
|
+
server.tool('add_list', 'Add a new list to a board', {
|
|
4
|
+
boardId: z.string().describe('The board ID'),
|
|
5
|
+
title: z.string().describe('Title for the new list'),
|
|
6
|
+
position: z.number().int().optional().describe('Position index to insert at (appends to end if omitted)'),
|
|
7
|
+
}, async ({ boardId, title, position }) => {
|
|
8
|
+
const result = await client.performAction(boardId, 'ListAdded', [position ?? null, title]);
|
|
9
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
10
|
+
});
|
|
11
|
+
server.tool('rename_list', 'Rename an existing list', {
|
|
12
|
+
boardId: z.string().describe('The board ID'),
|
|
13
|
+
listId: z.number().int().describe('The list ID'),
|
|
14
|
+
title: z.string().describe('New title for the list'),
|
|
15
|
+
}, async ({ boardId, listId, title }) => {
|
|
16
|
+
const result = await client.performAction(boardId, 'ListRenamed', [listId, title]);
|
|
17
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
18
|
+
});
|
|
19
|
+
server.tool('move_list', 'Move a list to a new position on the board', {
|
|
20
|
+
boardId: z.string().describe('The board ID'),
|
|
21
|
+
listId: z.number().int().describe('The list ID'),
|
|
22
|
+
position: z.number().int().describe('New position index'),
|
|
23
|
+
}, async ({ boardId, listId, position }) => {
|
|
24
|
+
const result = await client.performAction(boardId, 'ListMoved', [listId, position]);
|
|
25
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
26
|
+
});
|
|
27
|
+
server.tool('remove_list', 'Remove a list from the board', {
|
|
28
|
+
boardId: z.string().describe('The board ID'),
|
|
29
|
+
listId: z.number().int().describe('The list ID to remove'),
|
|
30
|
+
}, async ({ boardId, listId }) => {
|
|
31
|
+
const result = await client.performAction(boardId, 'ListRemoved', [listId]);
|
|
32
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
33
|
+
});
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "trollo-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for managing Trollo kanban boards from AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"trollo-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"prepublishOnly": "npm run build",
|
|
16
|
+
"start": "node dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"model-context-protocol",
|
|
21
|
+
"trollo",
|
|
22
|
+
"kanban",
|
|
23
|
+
"ai",
|
|
24
|
+
"agent"
|
|
25
|
+
],
|
|
26
|
+
"license": "none",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/Q42/Trollo",
|
|
30
|
+
"directory": "mcp"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.5.0",
|
|
37
|
+
"typescript": "^5.8.2"
|
|
38
|
+
}
|
|
39
|
+
}
|