#
tokens: 11128/50000 5/110 files (page 2/2)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 2 of 2. Use http://codebase.md/k-jarzyna/mcp-miro?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .env.template
├── .gitignore
├── Dockerfile
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│   ├── client.ts
│   ├── index.ts
│   ├── server-response.ts
│   ├── server.ts
│   ├── tool-bootstrapper.ts
│   ├── tool.ts
│   └── tools
│       ├── addProjectMember.ts
│       ├── attachTag.ts
│       ├── copyBoard.ts
│       ├── createAppCardItem.ts
│       ├── createBoard.ts
│       ├── createBoardExportJob.ts
│       ├── createCardItem.ts
│       ├── createConnector.ts
│       ├── createDocumentItem.ts
│       ├── createEmbedItem.ts
│       ├── createFrameItem.ts
│       ├── createGroup.ts
│       ├── createImageItemUsingFileFromDevice.ts
│       ├── createImageItemUsingUrl.ts
│       ├── createItemsInBulk.ts
│       ├── createItemsInBulkUsingFile.ts
│       ├── createMindmapNode.ts
│       ├── createShapeItem.ts
│       ├── createStickyNoteItem.ts
│       ├── createTag.ts
│       ├── createTextItem.ts
│       ├── deleteAppCardItem.ts
│       ├── deleteBoard.ts
│       ├── deleteCardItem.ts
│       ├── deleteConnector.ts
│       ├── deleteDocumentItem.ts
│       ├── deleteEmbedItem.ts
│       ├── deleteFrameItem.ts
│       ├── deleteGroup.ts
│       ├── deleteImageItem.ts
│       ├── deleteItem.ts
│       ├── deleteMindmapNode.ts
│       ├── deleteShapeItem.ts
│       ├── deleteStickyNoteItem.ts
│       ├── deleteTag.ts
│       ├── deleteTextItem.ts
│       ├── detachTag.ts
│       ├── getAllBoardMembers.ts
│       ├── getAllCases.ts
│       ├── getAllGroups.ts
│       ├── getAllLegalHolds.ts
│       ├── getAllTags.ts
│       ├── getAppCardItem.ts
│       ├── getAuditLogs.ts
│       ├── getBoardClassification.ts
│       ├── getBoardContentLogs.ts
│       ├── getBoardExportJobResults.ts
│       ├── getBoardExportJobStatus.ts
│       ├── getCardItem.ts
│       ├── getCase.ts
│       ├── getConnectors.ts
│       ├── getDocumentItem.ts
│       ├── getEmbedItem.ts
│       ├── getFrameItem.ts
│       ├── getGroup.ts
│       ├── getGroupItems.ts
│       ├── getImageItem.ts
│       ├── getItemsOnBoard.ts
│       ├── getItemTags.ts
│       ├── getLegalHold.ts
│       ├── getLegalHoldContentItems.ts
│       ├── getMindmapNode.ts
│       ├── getMindmapNodes.ts
│       ├── getOrganizationInfo.ts
│       ├── getOrganizationMember.ts
│       ├── getOrganizationMembers.ts
│       ├── getProjectMember.ts
│       ├── getShapeItem.ts
│       ├── getSpecificBoard.ts
│       ├── getSpecificBoardMember.ts
│       ├── getSpecificConnector.ts
│       ├── getSpecificItem.ts
│       ├── getStickyNoteItem.ts
│       ├── getTag.ts
│       ├── getTextItem.ts
│       ├── listBoards.ts
│       ├── removeBoardMember.ts
│       ├── removeProjectMember.ts
│       ├── shareBoard.ts
│       ├── ungroupItems.ts
│       ├── updateAppCardItem.ts
│       ├── updateBoard.ts
│       ├── updateBoardClassification.ts
│       ├── updateBoardMember.ts
│       ├── updateCardItem.ts
│       ├── updateConnector.ts
│       ├── updateDocumentItem.ts
│       ├── updateEmbedItem.ts
│       ├── updateFrameItem.ts
│       ├── updateGroup.ts
│       ├── updateImageItem.ts
│       ├── updateImageItemUsingFileFromDevice.ts
│       ├── updateItemPosition.ts
│       ├── updateShapeItem.ts
│       ├── updateStickyNoteItem.ts
│       ├── updateTag.ts
│       └── updateTextItem.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/src/tools/createStickyNoteItem.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import MiroClient from '../client.js';
  2 | import { z } from 'zod';
  3 | import { ServerResponse } from '../server-response.js';
  4 | import { ToolSchema } from '../tool.js';
  5 | 
  6 | import { StickyNoteCreateRequest } from '@mirohq/miro-api/dist/model/stickyNoteCreateRequest.js';
  7 | import { StickyNoteData } from '@mirohq/miro-api/dist/model/stickyNoteData.js';
  8 | 
  9 | const validColors = [
 10 |   'light_yellow', 'yellow', 'orange', 
 11 |   'light_green', 'green', 
 12 |   'light_blue', 'blue', 
 13 |   'light_pink', 'pink',
 14 |   'light_purple', 'purple',
 15 |   'black', 'gray', 'light_gray', 'white'
 16 | ];
 17 | 
 18 | const validTextAligns = ['left', 'center', 'right'];
 19 | 
 20 | const validShapes = ['square', 'rectangle', 'circle', 'triangle', 'rhombus'];
 21 | 
 22 | const createStickyNoteItemTool: ToolSchema = {
 23 |   name: "create-sticky-note-item",
 24 |   description: "Create a new sticky note item on a Miro board",
 25 |   args: {
 26 |     boardId: z.string().describe("Unique identifier (ID) of the board where the sticky note will be created"),
 27 |     data: z.object({
 28 |       content: z.string().describe("Text content of the sticky note"),
 29 |       shape: z.string().optional().nullish().describe("Shape of the sticky note (square, rectangle, circle, triangle, rhombus)")
 30 |     }).describe("The content and configuration of the sticky note"),
 31 |     position: z.object({
 32 |       x: z.number().describe("X coordinate of the sticky note"),
 33 |       y: z.number().describe("Y coordinate of the sticky note"),
 34 |       origin: z.string().optional().nullish().describe("Origin of the sticky note (center, top-left, etc.)"),
 35 |       relativeTo: z.string().optional().nullish().describe("Reference point (canvas_center, etc.)")
 36 |     }).describe("Position of the sticky note on the board"),
 37 |     geometry: z.object({
 38 |       width: z.number().optional().nullish().describe("Width of the sticky note"),
 39 |       height: z.number().optional().nullish().describe("Height of the sticky note")
 40 |     }).optional().nullish().describe("Dimensions of the sticky note"),
 41 |     style: z.object({
 42 |       fillColor: z.string().optional().nullish().describe("Fill color of the sticky note (use predefined values like 'light_yellow', 'light_green', etc.)"),
 43 |       textAlign: z.string().optional().nullish().describe("Alignment of the text (left, center, right)")
 44 |     }).optional().nullish().describe("Style configuration of the sticky note")
 45 |   },
 46 |   fn: async ({ boardId, data, position, geometry, style }) => {
 47 |     try {
 48 |       if (!boardId) {
 49 |         return ServerResponse.error("Board ID is required");
 50 |       }
 51 | 
 52 |       const createRequest = new StickyNoteCreateRequest();
 53 |       
 54 |       const stickyNoteData = new StickyNoteData();
 55 |       stickyNoteData.content = data.content;
 56 |       
 57 |       if (data.shape) {
 58 |         if (!validShapes.includes(data.shape)) {
 59 |           console.warn(`Invalid shape: ${data.shape}. Using default: square`);
 60 |           stickyNoteData.shape = 'square';
 61 |         } else {
 62 |           stickyNoteData.shape = data.shape;
 63 |         }
 64 |       } else {
 65 |         stickyNoteData.shape = 'square';
 66 |       }
 67 |       
 68 |       createRequest.data = stickyNoteData;
 69 |       
 70 |       const completePosition = {
 71 |         ...position,
 72 |         origin: position.origin || "center",
 73 |         relativeTo: position.relativeTo || "canvas_center"
 74 |       };
 75 |       createRequest.position = completePosition;
 76 |       
 77 |       if (geometry) {
 78 |         createRequest.geometry = geometry;
 79 |       }
 80 |       
 81 |       if (style) {
 82 |         const validatedStyle: Record<string, any> = {};
 83 |         
 84 |         if (style.fillColor) {
 85 |           if (!validColors.includes(style.fillColor)) {
 86 |             console.warn(`Invalid color: ${style.fillColor}. Using default: light_yellow`);
 87 |             validatedStyle.fillColor = 'light_yellow';
 88 |           } else {
 89 |             validatedStyle.fillColor = style.fillColor;
 90 |           }
 91 |         } else {
 92 |           validatedStyle.fillColor = 'light_yellow';
 93 |         }
 94 |         
 95 |         if (style.textAlign) {
 96 |           if (!validTextAligns.includes(style.textAlign)) {
 97 |             console.warn(`Invalid text alignment: ${style.textAlign}. Using default: center`);
 98 |             validatedStyle.textAlign = 'center';
 99 |           } else {
100 |             validatedStyle.textAlign = style.textAlign;
101 |           }
102 |         } else {
103 |           validatedStyle.textAlign = 'center';
104 |         }
105 |         
106 |         createRequest.style = validatedStyle;
107 |       } else {
108 |         createRequest.style = {
109 |           fillColor: 'light_yellow',
110 |           textAlign: 'center'
111 |         };
112 |       }
113 | 
114 |       const result = await MiroClient.getApi().createStickyNoteItem(boardId, createRequest);
115 |       return ServerResponse.text(JSON.stringify(result, null, 2));
116 |     } catch (error) {
117 |       return ServerResponse.error(error);
118 |     }
119 |   }
120 | }
121 | 
122 | export default createStickyNoteItemTool;
```

--------------------------------------------------------------------------------
/src/tools/updateStickyNoteItem.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import MiroClient from '../client.js';
  2 | import { z } from 'zod';
  3 | import { ServerResponse } from '../server-response.js';
  4 | import { ToolSchema } from '../tool.js';
  5 | 
  6 | import { StickyNoteUpdateRequest } from '@mirohq/miro-api/dist/model/stickyNoteUpdateRequest.js';
  7 | import { StickyNoteData } from '@mirohq/miro-api/dist/model/stickyNoteData.js';
  8 | 
  9 | const validColors = [
 10 |   'light_yellow', 'yellow', 'orange', 
 11 |   'light_green', 'green', 
 12 |   'light_blue', 'blue', 
 13 |   'light_pink', 'pink',
 14 |   'light_purple', 'purple',
 15 |   'black', 'gray', 'light_gray', 'white'
 16 | ];
 17 | 
 18 | const validTextAligns = ['left', 'center', 'right'];
 19 | 
 20 | const validShapes = ['square', 'rectangle', 'circle', 'triangle', 'rhombus'];
 21 | 
 22 | const updateStickyNoteItemTool: ToolSchema = {
 23 |   name: "update-sticky-note-item",
 24 |   description: "Update an existing sticky note item on a Miro board",
 25 |   args: {
 26 |     boardId: z.string().describe("Unique identifier (ID) of the board that contains the sticky note"),
 27 |     itemId: z.string().describe("Unique identifier (ID) of the sticky note that you want to update"),
 28 |     data: z.object({
 29 |       content: z.string().optional().nullish().describe("Updated text content of the sticky note"),
 30 |       shape: z.string().optional().nullish().describe("Updated shape of the sticky note (square, rectangle, circle, triangle, rhombus)")
 31 |     }).optional().nullish().describe("Updated content and configuration of the sticky note"),
 32 |     position: z.object({
 33 |       x: z.number().optional().nullish().describe("Updated X coordinate of the sticky note"),
 34 |       y: z.number().optional().nullish().describe("Updated Y coordinate of the sticky note"),
 35 |       origin: z.string().optional().nullish().describe("Origin of the sticky note (center, top-left, etc.)"),
 36 |       relativeTo: z.string().optional().nullish().describe("Reference point (canvas_center, etc.)")
 37 |     }).optional().nullish().describe("Updated position of the sticky note on the board"),
 38 |     geometry: z.object({
 39 |       width: z.number().optional().nullish().describe("Updated width of the sticky note"),
 40 |       height: z.number().optional().nullish().describe("Updated height of the sticky note")
 41 |     }).optional().nullish().describe("Updated dimensions of the sticky note"),
 42 |     style: z.object({
 43 |       fillColor: z.string().optional().nullish().describe("Updated fill color of the sticky note (use predefined values)"),
 44 |       textAlign: z.string().optional().nullish().describe("Updated alignment of the text (left, center, right)"),
 45 |       textColor: z.string().optional().nullish().describe("Updated color of the text on the sticky note")
 46 |     }).optional().nullish().describe("Updated style configuration of the sticky note")
 47 |   },
 48 |   fn: async ({ boardId, itemId, data, position, geometry, style }) => {
 49 |     try {
 50 |       if (!boardId) {
 51 |         return ServerResponse.error("Board ID is required");
 52 |       }
 53 |       
 54 |       if (!itemId) {
 55 |         return ServerResponse.error("Item ID is required");
 56 |       }
 57 | 
 58 |       const updateRequest = new StickyNoteUpdateRequest();
 59 |       
 60 |       if (data) {
 61 |         const stickyNoteData = new StickyNoteData();
 62 |         
 63 |         if (data.content !== undefined) {
 64 |           stickyNoteData.content = data.content;
 65 |         }
 66 |         
 67 |         if (data.shape !== undefined) {
 68 |           if (!validShapes.includes(data.shape)) {
 69 |             console.warn(`Invalid shape: ${data.shape}. Skipping this field.`);
 70 |           } else {
 71 |             stickyNoteData.shape = data.shape;
 72 |           }
 73 |         }
 74 |         
 75 |         if (Object.keys(stickyNoteData).length > 0) {
 76 |           updateRequest.data = stickyNoteData;
 77 |         }
 78 |       }
 79 |       
 80 |       if (position) {
 81 |         if (position.x !== undefined || position.y !== undefined) {
 82 |           const completePosition: Record<string, any> = {};
 83 |           
 84 |           if (position.x !== undefined) completePosition.x = position.x;
 85 |           if (position.y !== undefined) completePosition.y = position.y;
 86 |           
 87 |           completePosition.origin = position.origin || "center";
 88 |           completePosition.relativeTo = position.relativeTo || "canvas_center";
 89 |           
 90 |           updateRequest.position = completePosition;
 91 |         }
 92 |       }
 93 |       
 94 |       if (geometry && Object.keys(geometry).length > 0) {
 95 |         updateRequest.geometry = geometry;
 96 |       }
 97 |       
 98 |       if (style) {
 99 |         const validatedStyle: Record<string, any> = {};
100 |         
101 |         if (style.fillColor) {
102 |           if (!validColors.includes(style.fillColor)) {
103 |             console.warn(`Invalid color: ${style.fillColor}. Skipping this field.`);
104 |           } else {
105 |             validatedStyle.fillColor = style.fillColor;
106 |           }
107 |         }
108 |         
109 |         if (style.textAlign) {
110 |           if (!validTextAligns.includes(style.textAlign)) {
111 |             console.warn(`Invalid text alignment: ${style.textAlign}. Skipping this field.`);
112 |           } else {
113 |             validatedStyle.textAlign = style.textAlign;
114 |           }
115 |         }
116 |         
117 |         if (style.textColor) {
118 |           validatedStyle.textColor = style.textColor;
119 |         }
120 |         
121 |         if (Object.keys(validatedStyle).length > 0) {
122 |           updateRequest.style = validatedStyle;
123 |         }
124 |       }
125 | 
126 |       const result = await MiroClient.getApi().updateStickyNoteItem(boardId, itemId, updateRequest);
127 |       return ServerResponse.text(JSON.stringify(result, null, 2));
128 |     } catch (error) {
129 |       return ServerResponse.error(error);
130 |     }
131 |   }
132 | }
133 | 
134 | export default updateStickyNoteItemTool;
```

--------------------------------------------------------------------------------
/src/tools/createItemsInBulkUsingFile.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import MiroClient from '../client.js';
  2 | import { z } from 'zod';
  3 | import { ServerResponse } from '../server-response.js';
  4 | import { ToolSchema } from '../tool.js';
  5 | 
  6 | import { StickyNoteCreateRequest } from '@mirohq/miro-api/dist/model/stickyNoteCreateRequest.js';
  7 | import { StickyNoteData } from '@mirohq/miro-api/dist/model/stickyNoteData.js';
  8 | import { CardCreateRequest } from '@mirohq/miro-api/dist/model/cardCreateRequest.js';
  9 | import { CardData } from '@mirohq/miro-api/dist/model/cardData.js';
 10 | import { TextCreateRequest } from '@mirohq/miro-api/dist/model/textCreateRequest.js';
 11 | import { TextData } from '@mirohq/miro-api/dist/model/textData.js';
 12 | 
 13 | const validStickyNoteColors = [
 14 |   'light_yellow', 'yellow', 'orange', 
 15 |   'light_green', 'green', 
 16 |   'light_blue', 'blue', 
 17 |   'light_pink', 'pink',
 18 |   'light_purple', 'purple',
 19 |   'black', 'gray', 'white'
 20 | ];
 21 | 
 22 | const validStickyNoteShapes = ['square', 'rectangle'];
 23 | const validTextAligns = ['left', 'center', 'right'];
 24 | 
 25 | const createItemsInBulkUsingFileTool: ToolSchema = {
 26 |   name: "create-items-in-bulk-using-file",
 27 |   description: "Create multiple items on a Miro board in a single operation using a JSON file from device",
 28 |   args: {
 29 |     boardId: z.string().describe("Unique identifier (ID) of the board where the items will be created"),
 30 |     fileData: z.string().describe("Base64 encoded JSON file data containing items to create")
 31 |   },
 32 |   fn: async ({ boardId, fileData }) => {
 33 |     try {
 34 |       if (!boardId) {
 35 |         return ServerResponse.error("Board ID is required");
 36 |       }
 37 |       
 38 |       if (!fileData) {
 39 |         return ServerResponse.error("File data is required");
 40 |       }
 41 | 
 42 |       // Decode the base64 file data
 43 |       let jsonData;
 44 |       try {
 45 |         const base64Data = fileData.replace(/^data:application\/json;base64,/, '');
 46 |         const fileBuffer = Buffer.from(base64Data, 'base64');
 47 |         const fileContent = fileBuffer.toString('utf-8');
 48 |         jsonData = JSON.parse(fileContent);
 49 |       } catch (error) {
 50 |         return ServerResponse.error(`Error decoding or parsing JSON file data: ${error.message}`);
 51 |       }
 52 | 
 53 |       // Validate that the decoded data contains an 'items' array
 54 |       if (!jsonData.items || !Array.isArray(jsonData.items) || jsonData.items.length === 0) {
 55 |         return ServerResponse.error("JSON file must contain a non-empty 'items' array");
 56 |       }
 57 | 
 58 |       const items = jsonData.items;
 59 |       const results = [];
 60 |       const errors = [];
 61 | 
 62 |       const createPromises = items.map(async (item, index) => {
 63 |         try {
 64 |           // Validate item structure
 65 |           if (!item.type || !item.data || !item.position) {
 66 |             throw new Error(`Item at index ${index} is missing required fields (type, data, or position)`);
 67 |           }
 68 | 
 69 |           let result;
 70 |           
 71 |           if (item.type === 'sticky_note') {
 72 |             result = await createStickyNote(boardId, item);
 73 |           } else if (item.type === 'card') {
 74 |             result = await createCard(boardId, item);
 75 |           } else if (item.type === 'text') {
 76 |             result = await createText(boardId, item);
 77 |           } else {
 78 |             throw new Error(`Unsupported item type: ${item.type}`);
 79 |           }
 80 |           
 81 |           return { index, result };
 82 |         } catch (error) {
 83 |           return { index, error: error.message || String(error) };
 84 |         }
 85 |       });
 86 |       
 87 |       const promiseResults = await Promise.all(createPromises);
 88 |       
 89 |       for (const promiseResult of promiseResults) {
 90 |         const { index, result, error } = promiseResult;
 91 |         if (error) {
 92 |           errors.push({ index, error });
 93 |         } else if (result) {
 94 |           results.push({ index, item: result });
 95 |         }
 96 |       }
 97 |       
 98 |       return ServerResponse.text(JSON.stringify({
 99 |         created: results.length,
100 |         failed: errors.length,
101 |         results,
102 |         errors
103 |       }, null, 2));
104 |       
105 |     } catch (error) {
106 |       return ServerResponse.error(error);
107 |     }
108 |   }
109 | }
110 | 
111 | async function createStickyNote(boardId: string, item: any) {
112 |   const createRequest = new StickyNoteCreateRequest();
113 |   
114 |   const stickyNoteData = new StickyNoteData();
115 |   stickyNoteData.content = item.data.content;
116 |   stickyNoteData.shape = item.data.shape || 'square';
117 |   
118 |   createRequest.data = stickyNoteData;
119 |   createRequest.position = item.position;
120 |   
121 |   if (item.style) {
122 |     const style: Record<string, string> = {};
123 |     
124 |     if (item.style.fillColor) {
125 |       if (validStickyNoteColors.includes(item.style.fillColor)) {
126 |         style.fillColor = item.style.fillColor;
127 |       } else {
128 |         style.fillColor = 'light_yellow';
129 |       }
130 |     }
131 |     
132 |     if (item.style.textAlign) {
133 |       if (validTextAligns.includes(item.style.textAlign)) {
134 |         style.textAlign = item.style.textAlign;
135 |       } else {
136 |         style.textAlign = 'center';
137 |       }
138 |     }
139 |     
140 |     createRequest.style = style;
141 |   }
142 |   
143 |   return await MiroClient.getApi().createStickyNoteItem(boardId, createRequest);
144 | }
145 | 
146 | async function createCard(boardId: string, item: any) {
147 |   const createRequest = new CardCreateRequest();
148 |   
149 |   const cardData = new CardData();
150 |   cardData.title = item.data.title;
151 |   
152 |   if (item.data.description) {
153 |     cardData.description = item.data.description;
154 |   }
155 |   
156 |   if (item.data.assigneeId) {
157 |     cardData.assigneeId = item.data.assigneeId;
158 |   }
159 |   
160 |   if (item.data.dueDate) {
161 |     cardData.dueDate = new Date(item.data.dueDate);
162 |   }
163 |   
164 |   createRequest.data = cardData;
165 |   createRequest.position = item.position;
166 |   
167 |   if (item.style) {
168 |     createRequest.style = item.style as Record<string, any>;
169 |   }
170 |   
171 |   return await MiroClient.getApi().createCardItem(boardId, createRequest);
172 | }
173 | 
174 | async function createText(boardId: string, item: any) {
175 |   const createRequest = new TextCreateRequest();
176 |   
177 |   const textData = new TextData();
178 |   textData.content = item.data.content;
179 |   
180 |   createRequest.data = textData;
181 |   createRequest.position = item.position;
182 |   
183 |   if (item.style) {
184 |     createRequest.style = item.style as Record<string, any>;
185 |   }
186 |   
187 |   return await MiroClient.getApi().createTextItem(boardId, createRequest);
188 | }
189 | 
190 | export default createItemsInBulkUsingFileTool;
```

--------------------------------------------------------------------------------
/src/tools/createItemsInBulk.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import MiroClient from '../client.js';
  2 | import { z } from 'zod';
  3 | import { ServerResponse } from '../server-response.js';
  4 | import { ToolSchema } from '../tool.js';
  5 | 
  6 | import { StickyNoteCreateRequest } from '@mirohq/miro-api/dist/model/stickyNoteCreateRequest.js';
  7 | import { StickyNoteData } from '@mirohq/miro-api/dist/model/stickyNoteData.js';
  8 | import { CardCreateRequest } from '@mirohq/miro-api/dist/model/cardCreateRequest.js';
  9 | import { CardData } from '@mirohq/miro-api/dist/model/cardData.js';
 10 | import { TextCreateRequest } from '@mirohq/miro-api/dist/model/textCreateRequest.js';
 11 | import { TextData } from '@mirohq/miro-api/dist/model/textData.js';
 12 | 
 13 | const validStickyNoteColors = [
 14 |   'light_yellow', 'yellow', 'orange', 
 15 |   'light_green', 'green', 
 16 |   'light_blue', 'blue', 
 17 |   'light_pink', 'pink',
 18 |   'light_purple', 'purple',
 19 |   'black', 'gray', 'white'
 20 | ];
 21 | 
 22 | const validStickyNoteShapes = ['square', 'rectangle'];
 23 | const validTextAligns = ['left', 'center', 'right'];
 24 | 
 25 | const stickyNoteSchema = z.object({
 26 |   type: z.literal('sticky_note'),
 27 |   data: z.object({
 28 |     content: z.string().describe("Text content of the sticky note"),
 29 |     shape: z.enum(['square', 'rectangle']).optional().nullish().describe("Shape of the sticky note")
 30 |   }),
 31 |   position: z.object({
 32 |     x: z.number().describe("X coordinate"),
 33 |     y: z.number().describe("Y coordinate")
 34 |   }),
 35 |   style: z.object({
 36 |     fillColor: z.string().optional().nullish().describe("Fill color of the sticky note"),
 37 |     textAlign: z.enum(['left', 'center', 'right']).optional().nullish().describe("Text alignment")
 38 |   }).optional().nullish()
 39 | });
 40 | 
 41 | const cardSchema = z.object({
 42 |   type: z.literal('card'),
 43 |   data: z.object({
 44 |     title: z.string().describe("Title of the card"),
 45 |     description: z.string().optional().nullish().describe("Description of the card"),
 46 |     assigneeId: z.string().optional().nullish().describe("User ID of the assignee"),
 47 |     dueDate: z.string().optional().nullish().describe("Due date in ISO 8601 format")
 48 |   }),
 49 |   position: z.object({
 50 |     x: z.number().describe("X coordinate"),
 51 |     y: z.number().describe("Y coordinate")
 52 |   }),
 53 |   style: z.object({
 54 |     fillColor: z.string().optional().nullish().describe("Fill color"),
 55 |     textColor: z.string().optional().nullish().describe("Text color")
 56 |   }).optional().nullish()
 57 | });
 58 | 
 59 | const textSchema = z.object({
 60 |   type: z.literal('text'),
 61 |   data: z.object({
 62 |     content: z.string().describe("Text content")
 63 |   }),
 64 |   position: z.object({
 65 |     x: z.number().describe("X coordinate"),
 66 |     y: z.number().describe("Y coordinate")
 67 |   }),
 68 |   style: z.object({
 69 |     color: z.string().optional().nullish().describe("Text color (hex format, e.g. #000000)"),
 70 |     fontSize: z.number().optional().nullish().describe("Font size"),
 71 |     textAlign: z.enum(['left', 'center', 'right']).optional().nullish().describe("Text alignment")
 72 |   }).optional().nullish()
 73 | });
 74 | 
 75 | const itemSchema = z.discriminatedUnion('type', [
 76 |   stickyNoteSchema,
 77 |   cardSchema,
 78 |   textSchema
 79 | ]);
 80 | 
 81 | const createItemsInBulkTool: ToolSchema = {
 82 |   name: "create-items-in-bulk",
 83 |   description: "Create multiple items on a Miro board in a single operation",
 84 |   args: {
 85 |     boardId: z.string().describe("Unique identifier (ID) of the board where the items will be created"),
 86 |     items: z.array(itemSchema).describe("Array of items to create")
 87 |   },
 88 |   fn: async ({ boardId, items }) => {
 89 |     try {
 90 |       if (!boardId) {
 91 |         return ServerResponse.error("Board ID is required");
 92 |       }
 93 | 
 94 |       if (!items || !Array.isArray(items) || items.length === 0) {
 95 |         return ServerResponse.error("At least one item is required");
 96 |       }
 97 | 
 98 |       const results = [];
 99 |       const errors = [];
100 | 
101 |       const createPromises = items.map(async (item, index) => {
102 |         try {
103 |           let result;
104 |           
105 |           if (item.type === 'sticky_note') {
106 |             result = await createStickyNote(boardId, item);
107 |           } else if (item.type === 'card') {
108 |             result = await createCard(boardId, item);
109 |           } else if (item.type === 'text') {
110 |             result = await createText(boardId, item);
111 |           }
112 |           
113 |           return { index, result };
114 |         } catch (error) {
115 |           return { index, error: error.message || String(error) };
116 |         }
117 |       });
118 |       
119 |       const promiseResults = await Promise.all(createPromises);
120 |       
121 |       for (const promiseResult of promiseResults) {
122 |         const { index, result, error } = promiseResult;
123 |         if (error) {
124 |           errors.push({ index, error });
125 |         } else if (result) {
126 |           results.push({ index, item: result });
127 |         }
128 |       }
129 |       
130 |       return ServerResponse.text(JSON.stringify({
131 |         created: results.length,
132 |         failed: errors.length,
133 |         results,
134 |         errors
135 |       }, null, 2));
136 |       
137 |     } catch (error) {
138 |       return ServerResponse.error(error);
139 |     }
140 |   }
141 | }
142 | 
143 | async function createStickyNote(boardId: string, item: z.infer<typeof stickyNoteSchema>) {
144 |   const createRequest = new StickyNoteCreateRequest();
145 |   
146 |   const stickyNoteData = new StickyNoteData();
147 |   stickyNoteData.content = item.data.content;
148 |   stickyNoteData.shape = item.data.shape || 'square';
149 |   
150 |   createRequest.data = stickyNoteData;
151 |   createRequest.position = item.position;
152 |   
153 |   if (item.style) {
154 |     const style: Record<string, string> = {};
155 |     
156 |     if (item.style.fillColor) {
157 |       if (validStickyNoteColors.includes(item.style.fillColor)) {
158 |         style.fillColor = item.style.fillColor;
159 |       } else {
160 |         style.fillColor = 'light_yellow';
161 |       }
162 |     }
163 |     
164 |     if (item.style.textAlign) {
165 |       if (validTextAligns.includes(item.style.textAlign)) {
166 |         style.textAlign = item.style.textAlign;
167 |       } else {
168 |         style.textAlign = 'center';
169 |       }
170 |     }
171 |     
172 |     createRequest.style = style;
173 |   }
174 |   
175 |   return await MiroClient.getApi().createStickyNoteItem(boardId, createRequest);
176 | }
177 | 
178 | async function createCard(boardId: string, item: z.infer<typeof cardSchema>) {
179 |   const createRequest = new CardCreateRequest();
180 |   
181 |   const cardData = new CardData();
182 |   cardData.title = item.data.title;
183 |   
184 |   if (item.data.description) {
185 |     cardData.description = item.data.description;
186 |   }
187 |   
188 |   if (item.data.assigneeId) {
189 |     cardData.assigneeId = item.data.assigneeId;
190 |   }
191 |   
192 |   if (item.data.dueDate) {
193 |     cardData.dueDate = new Date(item.data.dueDate);
194 |   }
195 |   
196 |   createRequest.data = cardData;
197 |   createRequest.position = item.position;
198 |   
199 |   if (item.style) {
200 |     createRequest.style = item.style as Record<string, any>;
201 |   }
202 |   
203 |   return await MiroClient.getApi().createCardItem(boardId, createRequest);
204 | }
205 | 
206 | // Helper function to create a text item
207 | async function createText(boardId: string, item: z.infer<typeof textSchema>) {
208 |   const createRequest = new TextCreateRequest();
209 |   
210 |   const textData = new TextData();
211 |   textData.content = item.data.content;
212 |   
213 |   createRequest.data = textData;
214 |   createRequest.position = item.position;
215 |   
216 |   if (item.style) {
217 |     createRequest.style = item.style as Record<string, any>;
218 |   }
219 |   
220 |   return await MiroClient.getApi().createTextItem(boardId, createRequest);
221 | }
222 | 
223 | export default createItemsInBulkTool;
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import fs from 'fs';
  4 | import path from 'path';
  5 | 
  6 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  7 | import * as dotenv from "dotenv";
  8 | import server from './server.js';
  9 | import { ToolBootstrapper } from './tool-bootstrapper.js';
 10 | import listBoardsTool from './tools/listBoards.js';
 11 | import createBoardTool from './tools/createBoard.js';
 12 | import updateBoardTool from './tools/updateBoard.js';
 13 | import deleteBoardTool from './tools/deleteBoard.js';
 14 | import copyBoardTool from './tools/copyBoard.js';
 15 | import getSpecificBoardTool from './tools/getSpecificBoard.js';
 16 | import getItemsOnBoardTool from './tools/getItemsOnBoard.js';
 17 | import getSpecificItemTool from './tools/getSpecificItem.js';
 18 | import updateItemPositionTool from './tools/updateItemPosition.js';
 19 | import deleteItemTool from './tools/deleteItem.js';
 20 | import createAppCardItemTool from './tools/createAppCardItem.js';
 21 | import getAppCardItemTool from './tools/getAppCardItem.js';
 22 | import updateAppCardItemTool from './tools/updateAppCardItem.js';
 23 | import deleteAppCardItemTool from './tools/deleteAppCardItem.js';
 24 | import createCardItemTool from './tools/createCardItem.js';
 25 | import getCardItemTool from './tools/getCardItem.js';
 26 | import updateCardItemTool from './tools/updateCardItem.js';
 27 | import deleteCardItemTool from './tools/deleteCardItem.js';
 28 | import createConnectorTool from './tools/createConnector.js';
 29 | import getConnectorsTool from './tools/getConnectors.js';
 30 | import getSpecificConnectorTool from './tools/getSpecificConnector.js';
 31 | import updateConnectorTool from './tools/updateConnector.js';
 32 | import deleteConnectorTool from './tools/deleteConnector.js';
 33 | import createStickyNoteItemTool from './tools/createStickyNoteItem.js';
 34 | import getStickyNoteItemTool from './tools/getStickyNoteItem.js';
 35 | import updateStickyNoteItemTool from './tools/updateStickyNoteItem.js';
 36 | import deleteStickyNoteItemTool from './tools/deleteStickyNoteItem.js';
 37 | import createFrameItemTool from './tools/createFrameItem.js';
 38 | import getFrameItemTool from './tools/getFrameItem.js';
 39 | import updateFrameItemTool from './tools/updateFrameItem.js';
 40 | import deleteFrameItemTool from './tools/deleteFrameItem.js';
 41 | import createDocumentItemTool from './tools/createDocumentItem.js';
 42 | import getDocumentItemTool from './tools/getDocumentItem.js';
 43 | import updateDocumentItemTool from './tools/updateDocumentItem.js';
 44 | import deleteDocumentItemTool from './tools/deleteDocumentItem.js';
 45 | import createTextItemTool from './tools/createTextItem.js';
 46 | import getTextItemTool from './tools/getTextItem.js';
 47 | import updateTextItemTool from './tools/updateTextItem.js';
 48 | import deleteTextItemTool from './tools/deleteTextItem.js';
 49 | import createItemsInBulkTool from './tools/createItemsInBulk.js';
 50 | import createItemsInBulkUsingFileTool from './tools/createItemsInBulkUsingFile.js';
 51 | import createImageItemUsingUrlTool from './tools/createImageItemUsingUrl.js';
 52 | import createImageItemUsingFileFromDeviceTool from './tools/createImageItemUsingFileFromDevice.js';
 53 | import getImageItemTool from './tools/getImageItem.js';
 54 | import updateImageItemTool from './tools/updateImageItem.js';
 55 | import updateImageItemUsingFileFromDeviceTool from './tools/updateImageItemUsingFileFromDevice.js';
 56 | import deleteImageItemTool from './tools/deleteImageItem.js';
 57 | import createShapeItemTool from './tools/createShapeItem.js';
 58 | import getShapeItemTool from './tools/getShapeItem.js';
 59 | import updateShapeItemTool from './tools/updateShapeItem.js';
 60 | import deleteShapeItemTool from './tools/deleteShapeItem.js';
 61 | import createEmbedItemTool from './tools/createEmbedItem.js';
 62 | import getEmbedItemTool from './tools/getEmbedItem.js';
 63 | import updateEmbedItemTool from './tools/updateEmbedItem.js';
 64 | import deleteEmbedItemTool from './tools/deleteEmbedItem.js';
 65 | import createTagTool from './tools/createTag.js';
 66 | import getTagTool from './tools/getTag.js';
 67 | import getAllTagsTool from './tools/getAllTags.js';
 68 | import updateTagTool from './tools/updateTag.js';
 69 | import deleteTagTool from './tools/deleteTag.js';
 70 | import attachTagTool from './tools/attachTag.js';
 71 | import detachTagTool from './tools/detachTag.js';
 72 | import getItemTagsTool from './tools/getItemTags.js';
 73 | import getAllBoardMembers from './tools/getAllBoardMembers.js';
 74 | import getSpecificBoardMemberTool from './tools/getSpecificBoardMember.js';
 75 | import removeBoardMemberTool from './tools/removeBoardMember.js';
 76 | import shareBoardTool from './tools/shareBoard.js';
 77 | import updateBoardMemberTool from './tools/updateBoardMember.js';
 78 | import getAllGroupsTool from './tools/getAllGroups.js';
 79 | import getGroupTool from './tools/getGroup.js';
 80 | import getGroupItemsTool from './tools/getGroupItems.js';
 81 | import updateGroupTool from './tools/updateGroup.js';
 82 | import ungroupItemsTool from './tools/ungroupItems.js';
 83 | import deleteGroupTool from './tools/deleteGroup.js';
 84 | import createGroupTool from './tools/createGroup.js';
 85 | import createMindmapNodeTool from './tools/createMindmapNode.js';
 86 | import getMindmapNodeTool from './tools/getMindmapNode.js';
 87 | import getMindmapNodesTool from './tools/getMindmapNodes.js';
 88 | import deleteMindmapNodeTool from './tools/deleteMindmapNode.js';
 89 | import getBoardClassificationTool from './tools/getBoardClassification.js';
 90 | import updateBoardClassificationTool from './tools/updateBoardClassification.js';
 91 | import createBoardExportJobTool from './tools/createBoardExportJob.js';
 92 | import getBoardExportJobStatusTool from './tools/getBoardExportJobStatus.js';
 93 | import getBoardExportJobResultsTool from './tools/getBoardExportJobResults.js';
 94 | import getAuditLogsTool from './tools/getAuditLogs.js';
 95 | import getOrganizationInfoTool from './tools/getOrganizationInfo.js';
 96 | import getOrganizationMembersTool from './tools/getOrganizationMembers.js';
 97 | import getOrganizationMemberTool from './tools/getOrganizationMember.js';
 98 | import addProjectMemberTool from './tools/addProjectMember.js';
 99 | import getProjectMemberTool from './tools/getProjectMember.js';
100 | import removeProjectMemberTool from './tools/removeProjectMember.js';
101 | import getAllCasesTool from './tools/getAllCases.js';
102 | import getCaseTool from './tools/getCase.js';
103 | import getLegalHoldsTool from './tools/getAllLegalHolds.js';
104 | import getLegalHoldTool from './tools/getLegalHold.js';
105 | import getLegalHoldContentItemsTool from './tools/getLegalHoldContentItems.js';
106 | import getBoardContentLogsTool from './tools/getBoardContentLogs.js';
107 | 
108 | dotenv.config();
109 | 
110 | new ToolBootstrapper(server)
111 |   .register(listBoardsTool)
112 |   .register(createBoardTool)
113 |   .register(updateBoardTool)
114 |   .register(deleteBoardTool)
115 |   .register(copyBoardTool)
116 |   .register(getSpecificBoardTool)
117 |   .register(getItemsOnBoardTool)
118 |   .register(getSpecificItemTool)
119 |   .register(updateItemPositionTool)
120 |   .register(deleteItemTool)
121 |   .register(createAppCardItemTool)
122 |   .register(getAppCardItemTool)
123 |   .register(updateAppCardItemTool)
124 |   .register(deleteAppCardItemTool)
125 |   .register(createCardItemTool)
126 |   .register(getCardItemTool)
127 |   .register(updateCardItemTool)
128 |   .register(deleteCardItemTool)
129 |   .register(createConnectorTool)
130 |   .register(getConnectorsTool)
131 |   .register(getSpecificConnectorTool)
132 |   .register(updateConnectorTool)
133 |   .register(deleteConnectorTool)
134 |   .register(createStickyNoteItemTool)
135 |   .register(getStickyNoteItemTool)
136 |   .register(updateStickyNoteItemTool)
137 |   .register(deleteStickyNoteItemTool)
138 |   .register(createFrameItemTool)
139 |   .register(getFrameItemTool)
140 |   .register(updateFrameItemTool)
141 |   .register(deleteFrameItemTool)
142 |   .register(createDocumentItemTool)
143 |   .register(getDocumentItemTool)
144 |   .register(updateDocumentItemTool)
145 |   .register(deleteDocumentItemTool)
146 |   .register(createTextItemTool)
147 |   .register(getTextItemTool)
148 |   .register(updateTextItemTool)
149 |   .register(deleteTextItemTool)
150 |   .register(createItemsInBulkTool)
151 |   .register(createImageItemUsingUrlTool)
152 |   .register(createImageItemUsingFileFromDeviceTool)
153 |   .register(getImageItemTool)
154 |   .register(updateImageItemTool)
155 |   .register(updateImageItemUsingFileFromDeviceTool)
156 |   .register(deleteImageItemTool)
157 |   .register(createShapeItemTool)
158 |   .register(getShapeItemTool)
159 |   .register(updateShapeItemTool)
160 |   .register(deleteShapeItemTool)
161 |   .register(createEmbedItemTool)
162 |   .register(getEmbedItemTool)
163 |   .register(updateEmbedItemTool)
164 |   .register(deleteEmbedItemTool)
165 |   .register(createTagTool)
166 |   .register(getTagTool)
167 |   .register(getAllTagsTool)
168 |   .register(updateTagTool)
169 |   .register(deleteTagTool)
170 |   .register(attachTagTool)
171 |   .register(detachTagTool)
172 |   .register(getItemTagsTool)
173 |   .register(getAllBoardMembers)
174 |   .register(getSpecificBoardMemberTool)
175 |   .register(removeBoardMemberTool)
176 |   .register(shareBoardTool)
177 |   .register(updateBoardMemberTool)
178 |   .register(createGroupTool)
179 |   .register(getAllGroupsTool)
180 |   .register(getGroupTool)
181 |   .register(getGroupItemsTool)
182 |   .register(updateGroupTool)
183 |   .register(ungroupItemsTool)
184 |   .register(deleteGroupTool)
185 |   .register(createItemsInBulkUsingFileTool)
186 |   .register(createMindmapNodeTool)
187 |   .register(getMindmapNodeTool)
188 |   .register(getMindmapNodesTool)
189 |   .register(deleteMindmapNodeTool)
190 |   .register(getBoardClassificationTool)
191 |   .register(updateBoardClassificationTool)
192 |   .register(createBoardExportJobTool)
193 |   .register(getBoardExportJobStatusTool)
194 |   .register(getBoardExportJobResultsTool)
195 |   .register(getAuditLogsTool)
196 |   .register(getOrganizationInfoTool)
197 |   .register(getOrganizationMembersTool)
198 |   .register(getOrganizationMemberTool)
199 |   .register(addProjectMemberTool)
200 |   .register(getProjectMemberTool)
201 |   .register(removeProjectMemberTool)
202 |   .register(getAllCasesTool)
203 |   .register(getCaseTool)
204 |   .register(getLegalHoldsTool)
205 |   .register(getLegalHoldTool)
206 |   .register(getLegalHoldContentItemsTool)
207 |   .register(getBoardContentLogsTool);
208 | 
209 | async function main() {
210 |   try {
211 |     const transport = new StdioServerTransport();
212 |     await server.connect(transport);
213 | 
214 |     if (!process.env.MIRO_ACCESS_TOKEN) {
215 |       process.stderr.write("Warning: MIRO_ACCESS_TOKEN environment variable is not set. The server will not be able to connect to Miro.\n");
216 |     }
217 |   } catch (error) {
218 |     process.stderr.write(`Server error: ${error}\n`);
219 |     process.exit(1);
220 |   }
221 | }
222 | 
223 | main().catch(error => {
224 |   process.stderr.write(`Fatal error: ${error}\n`);
225 |   process.exit(1);
226 | });
```
Page 2/2FirstPrevNextLast