@delorenj/mcp-server-trello 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Model Context Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # MCP Server Trello
2
+
3
+ A Model Context Protocol (MCP) server that provides tools for interacting with Trello boards. This server enables seamless integration with Trello's API while handling rate limiting, type safety, and error handling automatically.
4
+
5
+ ## Changelog
6
+
7
+ ### 0.1.1
8
+
9
+ - Added `move_card` tool to move cards between lists
10
+ - Improved documentation
11
+
12
+ ### 0.1.0
13
+
14
+ - Initial release with basic Trello board management features
15
+
16
+ ## Features
17
+
18
+ - **Full Trello Board Integration**: Interact with cards, lists, and board activities
19
+ - **Built-in Rate Limiting**: Respects Trello's API limits (300 requests/10s per API key, 100 requests/10s per token)
20
+ - **Type-Safe Implementation**: Written in TypeScript with comprehensive type definitions
21
+ - **Input Validation**: Robust validation for all API inputs
22
+ - **Error Handling**: Graceful error handling with informative messages
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @delorenj/mcp-server-trello
28
+ ```
29
+
30
+ ## Configuration
31
+
32
+ Add the server to your MCP settings file with the following configuration:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "trello": {
38
+ "command": "npx",
39
+ "args": ["-y", "@delorenj/mcp-server-trello"],
40
+ "env": {
41
+ "TRELLO_API_KEY": "your-api-key",
42
+ "TRELLO_TOKEN": "your-token",
43
+ "TRELLO_BOARD_ID": "your-board-id"
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Required Environment Variables
51
+
52
+ - `TRELLO_API_KEY`: Your Trello API key (get from <https://trello.com/app-key>)
53
+ - `TRELLO_TOKEN`: Your Trello token (generate using your API key)
54
+ - `TRELLO_BOARD_ID`: ID of the Trello board to interact with (found in board URL)
55
+
56
+ ## Available Tools
57
+
58
+ ### get_cards_by_list_id
59
+
60
+ Fetch all cards from a specific list.
61
+
62
+ ```typescript
63
+ {
64
+ name: 'get_cards_by_list_id',
65
+ arguments: {
66
+ listId: string // ID of the Trello list
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### get_lists
72
+
73
+ Retrieve all lists from the configured board.
74
+
75
+ ```typescript
76
+ {
77
+ name: 'get_lists',
78
+ arguments: {}
79
+ }
80
+ ```
81
+
82
+ ### get_recent_activity
83
+
84
+ Fetch recent activity on the board.
85
+
86
+ ```typescript
87
+ {
88
+ name: 'get_recent_activity',
89
+ arguments: {
90
+ limit?: number // Optional: Number of activities to fetch (default: 10)
91
+ }
92
+ }
93
+ ```
94
+
95
+ ### add_card_to_list
96
+
97
+ Add a new card to a specified list.
98
+
99
+ ```typescript
100
+ {
101
+ name: 'add_card_to_list',
102
+ arguments: {
103
+ listId: string, // ID of the list to add the card to
104
+ name: string, // Name of the card
105
+ description?: string, // Optional: Description of the card
106
+ dueDate?: string, // Optional: Due date (ISO 8601 format)
107
+ labels?: string[] // Optional: Array of label IDs
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### update_card_details
113
+
114
+ Update an existing card's details.
115
+
116
+ ```typescript
117
+ {
118
+ name: 'update_card_details',
119
+ arguments: {
120
+ cardId: string, // ID of the card to update
121
+ name?: string, // Optional: New name for the card
122
+ description?: string, // Optional: New description
123
+ dueDate?: string, // Optional: New due date (ISO 8601 format)
124
+ labels?: string[] // Optional: New array of label IDs
125
+ }
126
+ }
127
+ ```
128
+
129
+ ### archive_card
130
+
131
+ Send a card to the archive.
132
+
133
+ ```typescript
134
+ {
135
+ name: 'archive_card',
136
+ arguments: {
137
+ cardId: string // ID of the card to archive
138
+ }
139
+ }
140
+ ```
141
+
142
+ ### add_list_to_board
143
+
144
+ Add a new list to the board.
145
+
146
+ ```typescript
147
+ {
148
+ name: 'add_list_to_board',
149
+ arguments: {
150
+ name: string // Name of the new list
151
+ }
152
+ }
153
+ ```
154
+
155
+ ### archive_list
156
+
157
+ Send a list to the archive.
158
+
159
+ ```typescript
160
+ {
161
+ name: 'archive_list',
162
+ arguments: {
163
+ listId: string // ID of the list to archive
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### get_my_cards
169
+
170
+ Fetch all cards assigned to the current user.
171
+
172
+ ```typescript
173
+ {
174
+ name: 'get_my_cards',
175
+ arguments: {}
176
+ }
177
+ ```
178
+
179
+ ### move_card
180
+
181
+ Move a card to a different list.
182
+
183
+ ```typescript
184
+ {
185
+ name: 'move_card',
186
+ arguments: {
187
+ cardId: string, // ID of the card to move
188
+ listId: string // ID of the target list
189
+ }
190
+ }
191
+ ```
192
+
193
+ ## Rate Limiting
194
+
195
+ The server implements a token bucket algorithm for rate limiting to comply with Trello's API limits:
196
+
197
+ - 300 requests per 10 seconds per API key
198
+ - 100 requests per 10 seconds per token
199
+
200
+ Rate limiting is handled automatically, and requests will be queued if limits are reached.
201
+
202
+ ## Error Handling
203
+
204
+ The server provides detailed error messages for various scenarios:
205
+
206
+ - Invalid input parameters
207
+ - Rate limit exceeded
208
+ - API authentication errors
209
+ - Network issues
210
+ - Invalid board/list/card IDs
211
+
212
+ ## Development
213
+
214
+ ### Prerequisites
215
+
216
+ - Node.js 16 or higher
217
+ - npm or yarn
218
+
219
+ ### Setup
220
+
221
+ 1. Clone the repository
222
+
223
+ ```bash
224
+ git clone https://github.com/delorenj/mcp-server-trello
225
+ cd mcp-server-trello
226
+ ```
227
+
228
+ 2. Install dependencies
229
+
230
+ ```bash
231
+ npm install
232
+ ```
233
+
234
+ 3. Build the project
235
+
236
+ ```bash
237
+ npm run build
238
+ ```
239
+
240
+
241
+ ## Contributing
242
+
243
+ Contributions are welcome!
244
+
245
+ ## License
246
+
247
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
248
+
249
+ ## Acknowledgments
250
+
251
+ - Built with the [Model Context Protocol SDK](https://github.com/modelcontextprotocol/sdk)
252
+ - Uses the [Trello REST API](https://developer.atlassian.com/cloud/trello/rest/)
package/build/index.js ADDED
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { TrelloClient } from './trello-client.js';
6
+ import { validateGetCardsListRequest, validateGetRecentActivityRequest, validateAddCardRequest, validateUpdateCardRequest, validateArchiveCardRequest, validateAddListRequest, validateArchiveListRequest, validateMoveCardRequest, } from './validators.js';
7
+ class TrelloServer {
8
+ constructor() {
9
+ const apiKey = process.env.TRELLO_API_KEY;
10
+ const token = process.env.TRELLO_TOKEN;
11
+ const boardId = process.env.TRELLO_BOARD_ID;
12
+ if (!apiKey || !token || !boardId) {
13
+ throw new Error('TRELLO_API_KEY, TRELLO_TOKEN, and TRELLO_BOARD_ID environment variables are required');
14
+ }
15
+ this.trelloClient = new TrelloClient({ apiKey, token, boardId });
16
+ this.server = new Server({
17
+ name: 'trello-server',
18
+ version: '0.1.1',
19
+ }, {
20
+ capabilities: {
21
+ tools: {},
22
+ },
23
+ });
24
+ this.setupToolHandlers();
25
+ // Error handling
26
+ this.server.onerror = (error) => console.error('[MCP Error]', error);
27
+ process.on('SIGINT', async () => {
28
+ await this.server.close();
29
+ process.exit(0);
30
+ });
31
+ }
32
+ setupToolHandlers() {
33
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
34
+ tools: [
35
+ {
36
+ name: 'get_cards_by_list_id',
37
+ description: 'Fetch cards from a specific Trello list',
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ listId: {
42
+ type: 'string',
43
+ description: 'ID of the Trello list',
44
+ },
45
+ },
46
+ required: ['listId'],
47
+ },
48
+ },
49
+ {
50
+ name: 'get_lists',
51
+ description: 'Retrieve all lists from the specified board',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {},
55
+ required: [],
56
+ },
57
+ },
58
+ {
59
+ name: 'get_recent_activity',
60
+ description: 'Fetch recent activity on the Trello board',
61
+ inputSchema: {
62
+ type: 'object',
63
+ properties: {
64
+ limit: {
65
+ type: 'number',
66
+ description: 'Number of activities to fetch (default: 10)',
67
+ },
68
+ },
69
+ required: [],
70
+ },
71
+ },
72
+ {
73
+ name: 'add_card_to_list',
74
+ description: 'Add a new card to a specified list',
75
+ inputSchema: {
76
+ type: 'object',
77
+ properties: {
78
+ listId: {
79
+ type: 'string',
80
+ description: 'ID of the list to add the card to',
81
+ },
82
+ name: {
83
+ type: 'string',
84
+ description: 'Name of the card',
85
+ },
86
+ description: {
87
+ type: 'string',
88
+ description: 'Description of the card',
89
+ },
90
+ dueDate: {
91
+ type: 'string',
92
+ description: 'Due date for the card (ISO 8601 format)',
93
+ },
94
+ labels: {
95
+ type: 'array',
96
+ items: {
97
+ type: 'string',
98
+ },
99
+ description: 'Array of label IDs to apply to the card',
100
+ },
101
+ },
102
+ required: ['listId', 'name'],
103
+ },
104
+ },
105
+ {
106
+ name: 'update_card_details',
107
+ description: 'Update an existing card\'s details',
108
+ inputSchema: {
109
+ type: 'object',
110
+ properties: {
111
+ cardId: {
112
+ type: 'string',
113
+ description: 'ID of the card to update',
114
+ },
115
+ name: {
116
+ type: 'string',
117
+ description: 'New name for the card',
118
+ },
119
+ description: {
120
+ type: 'string',
121
+ description: 'New description for the card',
122
+ },
123
+ dueDate: {
124
+ type: 'string',
125
+ description: 'New due date for the card (ISO 8601 format)',
126
+ },
127
+ labels: {
128
+ type: 'array',
129
+ items: {
130
+ type: 'string',
131
+ },
132
+ description: 'New array of label IDs for the card',
133
+ },
134
+ },
135
+ required: ['cardId'],
136
+ },
137
+ },
138
+ {
139
+ name: 'archive_card',
140
+ description: 'Send a card to the archive',
141
+ inputSchema: {
142
+ type: 'object',
143
+ properties: {
144
+ cardId: {
145
+ type: 'string',
146
+ description: 'ID of the card to archive',
147
+ },
148
+ },
149
+ required: ['cardId'],
150
+ },
151
+ },
152
+ {
153
+ name: 'move_card',
154
+ description: 'Move a card to a different list',
155
+ inputSchema: {
156
+ type: 'object',
157
+ properties: {
158
+ cardId: {
159
+ type: 'string',
160
+ description: 'ID of the card to move',
161
+ },
162
+ listId: {
163
+ type: 'string',
164
+ description: 'ID of the target list',
165
+ },
166
+ },
167
+ required: ['cardId', 'listId'],
168
+ },
169
+ },
170
+ {
171
+ name: 'add_list_to_board',
172
+ description: 'Add a new list to the board',
173
+ inputSchema: {
174
+ type: 'object',
175
+ properties: {
176
+ name: {
177
+ type: 'string',
178
+ description: 'Name of the new list',
179
+ },
180
+ },
181
+ required: ['name'],
182
+ },
183
+ },
184
+ {
185
+ name: 'archive_list',
186
+ description: 'Send a list to the archive',
187
+ inputSchema: {
188
+ type: 'object',
189
+ properties: {
190
+ listId: {
191
+ type: 'string',
192
+ description: 'ID of the list to archive',
193
+ },
194
+ },
195
+ required: ['listId'],
196
+ },
197
+ },
198
+ {
199
+ name: 'get_my_cards',
200
+ description: 'Fetch all cards assigned to the current user',
201
+ inputSchema: {
202
+ type: 'object',
203
+ properties: {},
204
+ required: [],
205
+ },
206
+ },
207
+ ],
208
+ }));
209
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
210
+ try {
211
+ if (!request.params.arguments) {
212
+ throw new McpError(ErrorCode.InvalidParams, 'Missing arguments');
213
+ }
214
+ const args = request.params.arguments;
215
+ switch (request.params.name) {
216
+ case 'get_cards_by_list_id': {
217
+ const validArgs = validateGetCardsListRequest(args);
218
+ const cards = await this.trelloClient.getCardsByList(validArgs.listId);
219
+ return {
220
+ content: [{ type: 'text', text: JSON.stringify(cards, null, 2) }],
221
+ };
222
+ }
223
+ case 'get_lists': {
224
+ const lists = await this.trelloClient.getLists();
225
+ return {
226
+ content: [{ type: 'text', text: JSON.stringify(lists, null, 2) }],
227
+ };
228
+ }
229
+ case 'get_recent_activity': {
230
+ const validArgs = validateGetRecentActivityRequest(args);
231
+ const activity = await this.trelloClient.getRecentActivity(validArgs.limit);
232
+ return {
233
+ content: [{ type: 'text', text: JSON.stringify(activity, null, 2) }],
234
+ };
235
+ }
236
+ case 'add_card_to_list': {
237
+ const validArgs = validateAddCardRequest(args);
238
+ const card = await this.trelloClient.addCard(validArgs);
239
+ return {
240
+ content: [{ type: 'text', text: JSON.stringify(card, null, 2) }],
241
+ };
242
+ }
243
+ case 'update_card_details': {
244
+ const validArgs = validateUpdateCardRequest(args);
245
+ const card = await this.trelloClient.updateCard(validArgs);
246
+ return {
247
+ content: [{ type: 'text', text: JSON.stringify(card, null, 2) }],
248
+ };
249
+ }
250
+ case 'archive_card': {
251
+ const validArgs = validateArchiveCardRequest(args);
252
+ const card = await this.trelloClient.archiveCard(validArgs.cardId);
253
+ return {
254
+ content: [{ type: 'text', text: JSON.stringify(card, null, 2) }],
255
+ };
256
+ }
257
+ case 'move_card': {
258
+ const validArgs = validateMoveCardRequest(args);
259
+ const card = await this.trelloClient.moveCard(validArgs.cardId, validArgs.listId);
260
+ return {
261
+ content: [{ type: 'text', text: JSON.stringify(card, null, 2) }],
262
+ };
263
+ }
264
+ case 'add_list_to_board': {
265
+ const validArgs = validateAddListRequest(args);
266
+ const list = await this.trelloClient.addList(validArgs.name);
267
+ return {
268
+ content: [{ type: 'text', text: JSON.stringify(list, null, 2) }],
269
+ };
270
+ }
271
+ case 'archive_list': {
272
+ const validArgs = validateArchiveListRequest(args);
273
+ const list = await this.trelloClient.archiveList(validArgs.listId);
274
+ return {
275
+ content: [{ type: 'text', text: JSON.stringify(list, null, 2) }],
276
+ };
277
+ }
278
+ case 'get_my_cards': {
279
+ const cards = await this.trelloClient.getMyCards();
280
+ return {
281
+ content: [{ type: 'text', text: JSON.stringify(cards, null, 2) }],
282
+ };
283
+ }
284
+ default:
285
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
286
+ }
287
+ }
288
+ catch (error) {
289
+ return {
290
+ content: [
291
+ {
292
+ type: 'text',
293
+ text: error instanceof Error ? error.message : 'Unknown error occurred',
294
+ },
295
+ ],
296
+ isError: true,
297
+ };
298
+ }
299
+ });
300
+ }
301
+ async run() {
302
+ const transport = new StdioServerTransport();
303
+ await this.server.connect(transport);
304
+ console.error('Trello MCP server running on stdio');
305
+ }
306
+ }
307
+ const server = new TrelloServer();
308
+ server.run().catch(console.error);
@@ -0,0 +1,55 @@
1
+ export class TokenBucketRateLimiter {
2
+ constructor(maxRequests, windowMs) {
3
+ this.maxTokens = maxRequests;
4
+ this.tokens = maxRequests;
5
+ this.lastRefill = Date.now();
6
+ this.refillInterval = windowMs;
7
+ this.refillRate = maxRequests / windowMs;
8
+ }
9
+ refillTokens() {
10
+ const now = Date.now();
11
+ const timePassed = now - this.lastRefill;
12
+ const newTokens = timePassed * this.refillRate;
13
+ this.tokens = Math.min(this.maxTokens, this.tokens + newTokens);
14
+ this.lastRefill = now;
15
+ }
16
+ canMakeRequest() {
17
+ this.refillTokens();
18
+ if (this.tokens >= 1) {
19
+ this.tokens -= 1;
20
+ return true;
21
+ }
22
+ return false;
23
+ }
24
+ async waitForAvailableToken() {
25
+ return new Promise((resolve) => {
26
+ const check = () => {
27
+ if (this.canMakeRequest()) {
28
+ resolve();
29
+ }
30
+ else {
31
+ // Calculate time until next token is available
32
+ const tokensNeeded = 1 - this.tokens;
33
+ const msToWait = (tokensNeeded / this.refillRate) * 1000;
34
+ setTimeout(check, Math.min(msToWait, 100)); // Check at most every 100ms
35
+ }
36
+ };
37
+ check();
38
+ });
39
+ }
40
+ }
41
+ // Create rate limiters based on Trello's limits
42
+ export const createTrelloRateLimiters = () => {
43
+ const apiKeyLimiter = new TokenBucketRateLimiter(300, 10000); // 300 requests per 10 seconds
44
+ const tokenLimiter = new TokenBucketRateLimiter(100, 10000); // 100 requests per 10 seconds
45
+ return {
46
+ apiKeyLimiter,
47
+ tokenLimiter,
48
+ async waitForAvailable() {
49
+ await Promise.all([
50
+ apiKeyLimiter.waitForAvailableToken(),
51
+ tokenLimiter.waitForAvailableToken(),
52
+ ]);
53
+ },
54
+ };
55
+ };
@@ -0,0 +1,118 @@
1
+ import axios from 'axios';
2
+ import { createTrelloRateLimiters } from './rate-limiter.js';
3
+ export class TrelloClient {
4
+ constructor(config) {
5
+ this.config = config;
6
+ this.axiosInstance = axios.create({
7
+ baseURL: 'https://api.trello.com/1',
8
+ params: {
9
+ key: config.apiKey,
10
+ token: config.token,
11
+ },
12
+ });
13
+ this.rateLimiter = createTrelloRateLimiters();
14
+ // Add rate limiting interceptor
15
+ this.axiosInstance.interceptors.request.use(async (config) => {
16
+ await this.rateLimiter.waitForAvailable();
17
+ return config;
18
+ });
19
+ }
20
+ async handleRequest(request) {
21
+ try {
22
+ return await request();
23
+ }
24
+ catch (error) {
25
+ if (axios.isAxiosError(error)) {
26
+ if (error.response?.status === 429) {
27
+ // Rate limit exceeded, wait and retry
28
+ await new Promise(resolve => setTimeout(resolve, 1000));
29
+ return this.handleRequest(request);
30
+ }
31
+ throw new Error(`Trello API error: ${error.response?.data?.message ?? error.message}`);
32
+ }
33
+ throw error;
34
+ }
35
+ }
36
+ async getCardsByList(listId) {
37
+ return this.handleRequest(async () => {
38
+ const response = await this.axiosInstance.get(`/lists/${listId}/cards`);
39
+ return response.data;
40
+ });
41
+ }
42
+ async getLists() {
43
+ return this.handleRequest(async () => {
44
+ const response = await this.axiosInstance.get(`/boards/${this.config.boardId}/lists`);
45
+ return response.data;
46
+ });
47
+ }
48
+ async getRecentActivity(limit = 10) {
49
+ return this.handleRequest(async () => {
50
+ const response = await this.axiosInstance.get(`/boards/${this.config.boardId}/actions`, {
51
+ params: { limit },
52
+ });
53
+ return response.data;
54
+ });
55
+ }
56
+ async addCard(params) {
57
+ return this.handleRequest(async () => {
58
+ const response = await this.axiosInstance.post('/cards', {
59
+ idList: params.listId,
60
+ name: params.name,
61
+ desc: params.description,
62
+ due: params.dueDate,
63
+ idLabels: params.labels,
64
+ });
65
+ return response.data;
66
+ });
67
+ }
68
+ async updateCard(params) {
69
+ return this.handleRequest(async () => {
70
+ const response = await this.axiosInstance.put(`/cards/${params.cardId}`, {
71
+ name: params.name,
72
+ desc: params.description,
73
+ due: params.dueDate,
74
+ idLabels: params.labels,
75
+ });
76
+ return response.data;
77
+ });
78
+ }
79
+ async archiveCard(cardId) {
80
+ return this.handleRequest(async () => {
81
+ const response = await this.axiosInstance.put(`/cards/${cardId}`, {
82
+ closed: true,
83
+ });
84
+ return response.data;
85
+ });
86
+ }
87
+ async moveCard(cardId, listId) {
88
+ return this.handleRequest(async () => {
89
+ const response = await this.axiosInstance.put(`/cards/${cardId}`, {
90
+ idList: listId,
91
+ });
92
+ return response.data;
93
+ });
94
+ }
95
+ async addList(name) {
96
+ return this.handleRequest(async () => {
97
+ const response = await this.axiosInstance.post('/lists', {
98
+ name,
99
+ idBoard: this.config.boardId,
100
+ });
101
+ return response.data;
102
+ });
103
+ }
104
+ async archiveList(listId) {
105
+ return this.handleRequest(async () => {
106
+ const response = await this.axiosInstance.put(`/lists/${listId}/closed`, {
107
+ value: true,
108
+ });
109
+ return response.data;
110
+ });
111
+ }
112
+ async getMyCards() {
113
+ return this.handleRequest(async () => {
114
+ const response = await this.axiosInstance.get('/members/me/cards');
115
+ return response.data;
116
+ });
117
+ }
118
+ }
package/build/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,104 @@
1
+ import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
2
+ export function validateString(value, field) {
3
+ if (typeof value !== 'string') {
4
+ throw new McpError(ErrorCode.InvalidParams, `${field} must be a string`);
5
+ }
6
+ return value;
7
+ }
8
+ export function validateOptionalString(value) {
9
+ if (value === undefined)
10
+ return undefined;
11
+ return validateString(value, 'value');
12
+ }
13
+ export function validateNumber(value, field) {
14
+ if (typeof value !== 'number') {
15
+ throw new McpError(ErrorCode.InvalidParams, `${field} must be a number`);
16
+ }
17
+ return value;
18
+ }
19
+ export function validateOptionalNumber(value) {
20
+ if (value === undefined)
21
+ return undefined;
22
+ return validateNumber(value, 'value');
23
+ }
24
+ export function validateStringArray(value) {
25
+ if (!Array.isArray(value) || !value.every(item => typeof item === 'string')) {
26
+ throw new McpError(ErrorCode.InvalidParams, 'Value must be an array of strings');
27
+ }
28
+ return value;
29
+ }
30
+ export function validateOptionalStringArray(value) {
31
+ if (value === undefined)
32
+ return undefined;
33
+ return validateStringArray(value);
34
+ }
35
+ export function validateGetCardsListRequest(args) {
36
+ if (!args.listId) {
37
+ throw new McpError(ErrorCode.InvalidParams, 'listId is required');
38
+ }
39
+ return {
40
+ listId: validateString(args.listId, 'listId'),
41
+ };
42
+ }
43
+ export function validateGetRecentActivityRequest(args) {
44
+ return {
45
+ limit: validateOptionalNumber(args.limit),
46
+ };
47
+ }
48
+ export function validateAddCardRequest(args) {
49
+ if (!args.listId || !args.name) {
50
+ throw new McpError(ErrorCode.InvalidParams, 'listId and name are required');
51
+ }
52
+ return {
53
+ listId: validateString(args.listId, 'listId'),
54
+ name: validateString(args.name, 'name'),
55
+ description: validateOptionalString(args.description),
56
+ dueDate: validateOptionalString(args.dueDate),
57
+ labels: validateOptionalStringArray(args.labels),
58
+ };
59
+ }
60
+ export function validateUpdateCardRequest(args) {
61
+ if (!args.cardId) {
62
+ throw new McpError(ErrorCode.InvalidParams, 'cardId is required');
63
+ }
64
+ return {
65
+ cardId: validateString(args.cardId, 'cardId'),
66
+ name: validateOptionalString(args.name),
67
+ description: validateOptionalString(args.description),
68
+ dueDate: validateOptionalString(args.dueDate),
69
+ labels: validateOptionalStringArray(args.labels),
70
+ };
71
+ }
72
+ export function validateArchiveCardRequest(args) {
73
+ if (!args.cardId) {
74
+ throw new McpError(ErrorCode.InvalidParams, 'cardId is required');
75
+ }
76
+ return {
77
+ cardId: validateString(args.cardId, 'cardId'),
78
+ };
79
+ }
80
+ export function validateAddListRequest(args) {
81
+ if (!args.name) {
82
+ throw new McpError(ErrorCode.InvalidParams, 'name is required');
83
+ }
84
+ return {
85
+ name: validateString(args.name, 'name'),
86
+ };
87
+ }
88
+ export function validateArchiveListRequest(args) {
89
+ if (!args.listId) {
90
+ throw new McpError(ErrorCode.InvalidParams, 'listId is required');
91
+ }
92
+ return {
93
+ listId: validateString(args.listId, 'listId'),
94
+ };
95
+ }
96
+ export function validateMoveCardRequest(args) {
97
+ if (!args.cardId || !args.listId) {
98
+ throw new McpError(ErrorCode.InvalidParams, 'cardId and listId are required');
99
+ }
100
+ return {
101
+ cardId: validateString(args.cardId, 'cardId'),
102
+ listId: validateString(args.listId, 'listId'),
103
+ };
104
+ }
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@delorenj/mcp-server-trello",
3
+ "version": "0.1.1",
4
+ "description": "A Model Context Protocol (MCP) server for interacting with Trello boards",
5
+ "type": "module",
6
+ "main": "build/index.js",
7
+ "types": "build/index.d.ts",
8
+ "bin": {
9
+ "mcp-server-trello": "./build/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc && chmod +x build/index.js",
13
+ "start": "node build/index.js",
14
+ "dev": "ts-node --esm src/index.ts",
15
+ "lint": "eslint . --ext .ts",
16
+ "format": "prettier --write \"src/**/*.ts\"",
17
+ "prepare": "npm run build",
18
+ "inspector": "npx @modelcontextprotocol/inspector build/index.js"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/delorenj/mcp-server-trello.git"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "trello",
28
+ "api",
29
+ "integration",
30
+ "automation",
31
+ "typescript",
32
+ "board-management",
33
+ "task-management",
34
+ "kanban"
35
+ ],
36
+ "author": "Jarad DeLorenzo",
37
+ "license": "MIT",
38
+ "bugs": {
39
+ "url": "https://github.com/delorenj/mcp-server-trello/issues"
40
+ },
41
+ "homepage": "https://github.com/delorenj/mcp-server-trello#readme",
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.0.4",
44
+ "axios": "^1.6.2"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^20.10.0",
48
+ "@typescript-eslint/eslint-plugin": "^6.12.0",
49
+ "@typescript-eslint/parser": "^6.12.0",
50
+ "eslint": "^8.54.0",
51
+ "eslint-config-prettier": "^9.0.0",
52
+ "eslint-plugin-prettier": "^5.0.1",
53
+ "prettier": "^3.1.0",
54
+ "ts-node": "^10.9.1",
55
+ "typescript": "^5.3.2"
56
+ },
57
+ "files": [
58
+ "build/**/*"
59
+ ],
60
+ "engines": {
61
+ "node": ">=16.0.0"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
+ }