This is page 1 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 -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- ``` 1 | # Miro API Configuration 2 | # Get your access token from https://developers.miro.com/ 3 | MIRO_ACCESS_TOKEN=your_miro_access_token_here 4 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Dependency directories 2 | node_modules/ 3 | 4 | # TypeScript compiled output 5 | build/ 6 | dist/ 7 | 8 | # Environment files 9 | .env 10 | 11 | # IDE files 12 | .idea/ 13 | .vscode/ 14 | 15 | # Logs 16 | logs 17 | *.log 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # OS specific 23 | .DS_Store 24 | Thumbs.db 25 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # MCP Miro Server 2 | 3 | [](https://archestra.ai/mcp-catalog/k-jarzyna__mcp-miro) 4 | [](https://smithery.ai/server/@k-jarzyna/mcp-miro) 5 | 6 | Model Context Protocol (MCP) server integrating with the [Miro](https://miro.com/) platform. It enables AI assistants (like Claude) to access Miro boards and manage their content through a standardized interface. 7 | 8 | --- 9 | ### Requirements 10 | 11 | - Node.js v16 or newer installed 12 | - Miro account with API token 13 | 14 | ### Generate Miro Access Token 15 | 16 | 1. Go to the [Miro Developer Portal](https://developers.miro.com/docs) 17 | 2. Create a new app or use an existing one 18 | 3. Make sure to create token with permission selected below 19 | 4. Generate OAuth token by selecting `Install app and get OAuth token` 20 | 21 | | Permission | Required | 22 | |-------------------|:--------:| 23 | | boards:read | ✅ | 24 | | boards:write | ✅ | 25 | | identity:read | ✅ | 26 | | identity:write | ✅ | 27 | | team:read | ✅ | 28 | | team:write | ✅ | 29 | | microphone:listen | ❌ | 30 | | screen:record | ❌ | 31 | | webcam:record | ❌ | 32 | | auditlogs:read | ❌ | 33 | | sessions:delete | ❌ | 34 | 35 | ### Connecting with Claude Desktop 36 | 37 | 1. Install [Claude Desktop](https://claude.ai/download) 38 | 2. Open or create the configuration file: 39 | - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` 40 | - Windows: `%APPDATA%\Claude\claude_desktop_config.json` 41 | 42 | 3. Update it to include this server: 43 | 44 | ```json 45 | { 46 | "mcpServers":{ 47 | "miro":{ 48 | "command":"npx", 49 | "args":[ 50 | "-y", 51 | "@k-jarzyna/mcp-miro" 52 | ], 53 | "env":{ 54 | "MIRO_ACCESS_TOKEN":"your_miro_access_token" 55 | } 56 | } 57 | } 58 | } 59 | ``` 60 | 61 | 4. Restart Claude Desktop 62 | 63 | --- 64 | ## Available Tools and Resources 65 | 66 | #### Tools 67 | | Miro SDK Function | MCP Tool | Available | 68 | |-------------------|----------|-----------| 69 | | List boards | list-boards | ✅ | 70 | | Create board | create-board | ✅ | 71 | | Update board | update-board | ✅ | 72 | | Delete board | delete-board | ✅ | 73 | | Copy board | copy-board | ✅ | 74 | | Get specific board | get-specific-board | ✅ | 75 | | Get items on board | get-items-on-board | ✅ | 76 | | Get specific item | get-specific-item | ✅ | 77 | | Update item position | update-item-position | ✅ | 78 | | Delete item | delete-item | ✅ | 79 | | Create app card item | create-app-card-item | ✅ | 80 | | Get app card item | get-app-card-item | ✅ | 81 | | Update app card item | update-app-card-item | ✅ | 82 | | Delete app card item | delete-app-card-item | ✅ | 83 | | Create card item | create-card-item | ✅ | 84 | | Get card item | get-card-item | ✅ | 85 | | Update card item | update-card-item | ✅ | 86 | | Delete card item | delete-card-item | ✅ | 87 | | Create connector | create-connector | ✅ | 88 | | Get connectors | get-connectors | ✅ | 89 | | Get specific connector | get-specific-connector | ✅ | 90 | | Update connector | update-connector | ✅ | 91 | | Delete connector | delete-connector | ✅ | 92 | | Create sticky note item | create-sticky-note-item | ✅ | 93 | | Get sticky note item | get-sticky-note-item | ✅ | 94 | | Update sticky note item | update-sticky-note-item | ✅ | 95 | | Delete sticky note item | delete-sticky-note-item | ✅ | 96 | | Create frame | create-frame | ✅ | 97 | | Get frame item | get-frame-item | ✅ | 98 | | Update frame item | update-frame-item | ✅ | 99 | | Delete frame item | delete-frame-item | ✅ | 100 | | Create document item | create-document-item | ✅ | 101 | | Get document item | get-document-item | ✅ | 102 | | Update document item | update-document-item | ✅ | 103 | | Delete document item | delete-document-item | ✅ | 104 | | Create text item | create-text-item | ✅ | 105 | | Get text item | get-text-item | ✅ | 106 | | Update text item | update-text-item | ✅ | 107 | | Delete text item | delete-text-item | ✅ | 108 | | Create items in bulk | create-items-in-bulk | ✅ | 109 | | Create image item using URL | create-image-item-using-url | ✅ | 110 | | Create image item using file | create-image-item-using-file | ✅ | 111 | | Get image item | get-image-item | ✅ | 112 | | Update image item | update-image-item | ✅ | 113 | | Update image item using file | update-image-item-using-file | ✅ | 114 | | Delete image item | delete-image-item | ✅ | 115 | | Create shape item | create-shape-item | ✅ | 116 | | Get shape item | get-shape-item | ✅ | 117 | | Update shape item | update-shape-item | ✅ | 118 | | Delete shape item | delete-shape-item | ✅ | 119 | | Create embed item | create-embed-item | ✅ | 120 | | Get embed item | get-embed-item | ✅ | 121 | | Update embed item | update-embed-item | ✅ | 122 | | Delete embed item | delete-embed-item | ✅ | 123 | | Create tag | create-tag | ✅ | 124 | | Get tag | get-tag | ✅ | 125 | | Get all tags | get-all-tags | ✅ | 126 | | Update tag | update-tag | ✅ | 127 | | Delete tag | delete-tag | ✅ | 128 | | Attach tag | attach-tag | ✅ | 129 | | Detach tag | detach-tag | ✅ | 130 | | Get item tags | get-item-tags | ✅ | 131 | | Get all board members | get-all-board-members | ✅ | 132 | | Get specific board member | get-specific-board-member | ✅ | 133 | | Remove board member | remove-board-member | ✅ | 134 | | Share board | share-board | ✅ | 135 | | Update board member | update-board-member | ✅ | 136 | | Create group | create-group | ✅ | 137 | | Get all groups | get-all-groups | ✅ | 138 | | Get group | get-group | ✅ | 139 | | Get group items | get-group-items | ✅ | 140 | | Update group | update-group | ✅ | 141 | | Ungroup items | ungroup-items | ✅ | 142 | | Delete group | delete-group | ✅ | 143 | | Create items in bulk using file | create-items-in-bulk-using-file | ✅ | 144 | | Create mindmap node | create-mindmap-node | ✅ | 145 | | Get mindmap node | get-mindmap-node | ✅ | 146 | | Get mindmap nodes | get-mindmap-nodes | ✅ | 147 | | Delete mindmap node | delete-mindmap-node | ✅ | 148 | | Add project member | add-project-member | ✅ | 149 | | Create board export job | create-board-export-job | ✅ | 150 | | Get all cases | get-all-cases | ✅ | 151 | | Get all legal holds | get-all-legal-holds | ✅ | 152 | | Get audit logs | get-audit-logs | ✅ | 153 | | Get board classification | get-board-classification | ✅ | 154 | | Get board content logs | get-board-content-logs | ✅ | 155 | | Get board export job results | get-board-export-job-results | ✅ | 156 | | Get board export job status | get-board-export-job-status | ✅ | 157 | | Get case | get-case | ✅ | 158 | | Get legal hold | get-legal-hold | ✅ | 159 | | Get legal hold content items | get-legal-hold-content-items | ✅ | 160 | | Get organization info | get-organization-info | ✅ | 161 | | Get organization member | get-organization-member | ✅ | 162 | | Get organization members | get-organization-members | ✅ | 163 | | Get project member | get-project-member | ✅ | 164 | | Remove project member | remove-project-member | ✅ | 165 | | Update board classification | update-board-classification | ✅ | 166 | 167 | 168 | --- 169 | ## Local Development 170 | 171 | 1. Install dependencies: 172 | 173 | ```bash 174 | npm install 175 | ``` 176 | 177 | 2. Create a `.env` file based on the template: 178 | 179 | ```bash 180 | cp .env.template .env 181 | ``` 182 | 183 | 3. Edit the `.env` file and add your Miro access token 184 | 185 | 4. Build the server: 186 | 187 | ```bash 188 | npm run build 189 | ``` 190 | 191 | ### Running the Server 192 | 193 | To run the server: 194 | 195 | ```bash 196 | node build/index.js 197 | ``` 198 | 199 | --- 200 | ## License 201 | 202 | Apache License 2.0 203 | 204 | This project is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details. 205 | ``` -------------------------------------------------------------------------------- /src/tool.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'; 2 | import { ZodRawShape } from 'zod'; 3 | 4 | export type ToolSchema = { 5 | name: string; 6 | description: string; 7 | args: ZodRawShape; 8 | fn: ToolCallback<any>; 9 | } ``` -------------------------------------------------------------------------------- /src/server.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | 3 | const server = new McpServer({ 4 | name: "mcp-miro", 5 | version: "1.0.0", 6 | capabilities: { 7 | tools: { listChanged: true }, 8 | resources: { listChanged: true } 9 | } 10 | }); 11 | 12 | export default server; ``` -------------------------------------------------------------------------------- /src/tool-bootstrapper.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 2 | import { ToolSchema } from './tool.js'; 3 | 4 | export class ToolBootstrapper { 5 | constructor( 6 | private readonly server: McpServer 7 | ) { 8 | } 9 | 10 | public register(tool: ToolSchema): this { 11 | this.server.tool(tool.name, tool.description, tool.args, tool.fn); 12 | 13 | return this; 14 | } 15 | } ``` -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- ```dockerfile 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM node:lts-alpine 3 | 4 | # Create app directory 5 | WORKDIR /app 6 | 7 | # Install dependencies without running scripts 8 | COPY package.json package-lock.json ./ 9 | RUN npm install --ignore-scripts 10 | 11 | # Copy source and build 12 | COPY . . 13 | RUN npm run build 14 | 15 | # Expose no ports; use stdio 16 | CMD ["node", "build/index.js"] 17 | ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "strict": false, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noImplicitAny": false, 13 | "preserveSymlinks": true 14 | }, 15 | "include": ["src/**/*"], 16 | "exclude": ["node_modules"] 17 | } 18 | ``` -------------------------------------------------------------------------------- /src/server-response.ts: -------------------------------------------------------------------------------- ```typescript 1 | export class ServerResponse { 2 | content: { type: string, text: string }[]; 3 | isError: boolean = false; 4 | 5 | public static text(text: string): any { 6 | return Object.assign(new ServerResponse(), { 7 | content: [ 8 | { 9 | type: 'text', 10 | text 11 | } 12 | ] 13 | }) 14 | } 15 | 16 | public static error(err: string): any { 17 | return Object.assign(new ServerResponse(), { 18 | content: [ 19 | { 20 | type: 'text', 21 | text: typeof err === 'object' ? JSON.stringify(err) : err 22 | }, 23 | ], 24 | isError: true 25 | }) 26 | } 27 | } ``` -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { MiroLowlevelApi } from "@mirohq/miro-api"; 2 | 3 | export class MiroClient { 4 | private static instance: MiroClient; 5 | private api: MiroLowlevelApi; 6 | 7 | private constructor() { 8 | const accessToken = process.env.MIRO_ACCESS_TOKEN; 9 | 10 | if (!accessToken) { 11 | throw new Error('MIRO_ACCESS_TOKEN environment variable is required'); 12 | } 13 | 14 | this.api = new MiroLowlevelApi(accessToken); 15 | } 16 | 17 | public static getApi(): MiroLowlevelApi { 18 | if (!MiroClient.instance) { 19 | MiroClient.instance = new MiroClient(); 20 | } 21 | 22 | return MiroClient.instance.api; 23 | } 24 | } 25 | 26 | export default MiroClient; 27 | ``` -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- ```yaml 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - miroAccessToken 10 | properties: 11 | miroAccessToken: 12 | type: string 13 | description: Miro OAuth access token 14 | commandFunction: 15 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 16 | |- 17 | (config) => ({ command: 'node', args: ['build/index.js'], env: { MIRO_ACCESS_TOKEN: config.miroAccessToken } }) 18 | exampleConfig: 19 | miroAccessToken: your_miro_access_token_here 20 | ``` -------------------------------------------------------------------------------- /src/tools/getOrganizationInfo.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 | const getOrganizationInfoTool: ToolSchema = { 7 | name: "get-organization-info", 8 | description: "Retrieves organization information (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("id of the organization") 11 | }, 12 | fn: async ({ orgId }) => { 13 | try { 14 | const response = await MiroClient.getApi().enterpriseGetOrganization(orgId); 15 | 16 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 17 | } catch (error) { 18 | process.stderr.write(`Error retrieving organization info: ${error}\n`); 19 | return ServerResponse.error(error); 20 | } 21 | } 22 | }; 23 | 24 | export default getOrganizationInfoTool; ``` -------------------------------------------------------------------------------- /src/tools/listBoards.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 | const listBoardsTool: ToolSchema = { 7 | name: "list-boards", 8 | description: "List all available Miro boards", 9 | args: { 10 | limit: z.number().optional().nullish().describe("Maximum number of boards to return (default: 50)"), 11 | offset: z.number().optional().nullish().describe("Offset for pagination (default: 0)") 12 | }, 13 | fn: async ({ limit = 50, offset = 0 }) => { 14 | try { 15 | 16 | const boardsData = await MiroClient.getApi().getBoards(); 17 | 18 | return ServerResponse.text(JSON.stringify(boardsData, null, 2)) 19 | } catch (error) { 20 | process.stderr.write(`Error fetching Miro boards: ${error}\n`); 21 | 22 | return ServerResponse.error(error) 23 | } 24 | } 25 | } 26 | 27 | export default listBoardsTool; ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@k-jarzyna/mcp-miro", 3 | "version": "1.0.9", 4 | "main": "build/index.js", 5 | "type": "module", 6 | "scripts": { 7 | "build": "tsc", 8 | "clean": "rm -rf build/", 9 | "prebuild": "npm run clean", 10 | "postbuild": "chmod +x build/index.js" 11 | }, 12 | "bin": { 13 | "mcp-miro": "build/index.js" 14 | }, 15 | "keywords": [], 16 | "author": "Konrad Jarzyna", 17 | "license": "Apache-2.0", 18 | "description": "Miro integration for Model Context Protocol", 19 | "dependencies": { 20 | "@mirohq/miro-api": "^2.2.4", 21 | "@modelcontextprotocol/sdk": "^1.8.0", 22 | "dotenv": "^16.4.7", 23 | "zod": "^3.24.2" 24 | }, 25 | "devDependencies": { 26 | "@types/node": "^22.14.0", 27 | "typescript": "^5.8.2" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "https://github.com/k-jarzyna/mcp-miro" 32 | }, 33 | "files": [ 34 | "build" 35 | ], 36 | "homepage": "https://github.com/k-jarzyna/mcp-miro" 37 | } 38 | ``` -------------------------------------------------------------------------------- /src/tools/getCase.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 | const getCaseTool: ToolSchema = { 7 | name: "get-case", 8 | description: "Retrieves information about a specific eDiscovery case (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("The ID of the organization for which you want to retrieve the case information"), 11 | caseId: z.string().describe("The ID of the case you want to retrieve") 12 | }, 13 | fn: async ({ orgId, caseId }) => { 14 | try { 15 | const response = await MiroClient.getApi().getCase(orgId, caseId); 16 | 17 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 18 | } catch (error) { 19 | process.stderr.write(`Error retrieving case: ${error}\n`); 20 | return ServerResponse.error(error); 21 | } 22 | } 23 | }; 24 | 25 | export default getCaseTool; ``` -------------------------------------------------------------------------------- /src/tools/getSpecificBoard.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 | const getSpecificBoardTool: ToolSchema = { 7 | name: "get-specific-board", 8 | description: "Retrieve information about a specific Miro board by its ID", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that you want to retrieve") 11 | }, 12 | fn: async ({ boardId }) => { 13 | try { 14 | if (!boardId) { 15 | return ServerResponse.error("Board ID is required"); 16 | } 17 | 18 | const boardData = await MiroClient.getApi().getSpecificBoard(boardId); 19 | 20 | return ServerResponse.text(JSON.stringify(boardData, null, 2)); 21 | } catch (error) { 22 | process.stderr.write(`Error fetching specific Miro board: ${error}\n`); 23 | 24 | return ServerResponse.error(error); 25 | } 26 | } 27 | } 28 | 29 | export default getSpecificBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/getOrganizationMember.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 | const getOrganizationMemberTool: ToolSchema = { 7 | name: "get-organization-member", 8 | description: "Retrieves information about a specific organization member (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("id of the organization"), 11 | memberId: z.string().describe("id of the organization member") 12 | }, 13 | fn: async ({ orgId, memberId }) => { 14 | try { 15 | const response = await MiroClient.getApi().enterpriseGetOrganizationMember(orgId, memberId); 16 | 17 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 18 | } catch (error) { 19 | process.stderr.write(`Error retrieving organization member: ${error}\n`); 20 | return ServerResponse.error(error); 21 | } 22 | } 23 | }; 24 | 25 | export default getOrganizationMemberTool; ``` -------------------------------------------------------------------------------- /src/tools/getBoardExportJobResults.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 | const getBoardExportJobResultsTool: ToolSchema = { 7 | name: "get-board-export-job-results", 8 | description: "Retrieves the results of a board export job (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("Unique identifier of the organization"), 11 | jobId: z.string().describe("Unique identifier of the job") 12 | }, 13 | fn: async ({ orgId, jobId }) => { 14 | try { 15 | const response = await MiroClient.getApi().enterpriseBoardExportJobResults(orgId, jobId); 16 | 17 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 18 | } catch (error) { 19 | process.stderr.write(`Error retrieving board export job results: ${error}\n`); 20 | return ServerResponse.error(error); 21 | } 22 | } 23 | }; 24 | 25 | export default getBoardExportJobResultsTool; ``` -------------------------------------------------------------------------------- /src/tools/getBoardExportJobStatus.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 | const getBoardExportJobStatusTool: ToolSchema = { 7 | name: "get-board-export-job-status", 8 | description: "Retrieves the status of a board export job (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("Unique identifier of the organization"), 11 | jobId: z.string().describe("Unique identifier of the board export job") 12 | }, 13 | fn: async ({ orgId, jobId }) => { 14 | try { 15 | const response = await MiroClient.getApi().enterpriseBoardExportJobStatus(orgId, jobId); 16 | 17 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 18 | } catch (error) { 19 | process.stderr.write(`Error retrieving board export job status: ${error}\n`); 20 | return ServerResponse.error(error); 21 | } 22 | } 23 | }; 24 | 25 | export default getBoardExportJobStatusTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteMindmapNode.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 | const deleteMindmapNodeTool: ToolSchema = { 7 | name: "delete-mindmap-node", 8 | description: "Delete a mind map node from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board from which you want to delete the mind map node"), 11 | itemId: z.string().describe("Unique identifier (ID) of the mind map node that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | const response = await MiroClient.getApi().deleteMindmapNodeExperimental(boardId, itemId); 16 | 17 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 18 | } catch (error) { 19 | process.stderr.write(`Error deleting Miro mind map node: ${error}\n`); 20 | return ServerResponse.error(error); 21 | } 22 | } 23 | }; 24 | 25 | export default deleteMindmapNodeTool; ``` -------------------------------------------------------------------------------- /src/tools/getTag.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 | const getTagTool: ToolSchema = { 7 | name: "get-tag", 8 | description: "Retrieve information about a specific tag on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the tag"), 11 | tagId: z.string().describe("Unique identifier (ID) of the tag that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, tagId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!tagId) { 20 | return ServerResponse.error("Tag ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().getTag(boardId, tagId); 24 | return ServerResponse.text(JSON.stringify(result, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getTagTool; ``` -------------------------------------------------------------------------------- /src/tools/getMindmapNode.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 | const getMindmapNodeTool: ToolSchema = { 7 | name: "get-mindmap-node", 8 | description: "Retrieve information about a specific mind map node on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board from which you want to retrieve a mind map node"), 11 | itemId: z.string().describe("Unique identifier (ID) of the mind map node that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | const response = await MiroClient.getApi().getMindmapNodeExperimental(boardId, itemId); 16 | 17 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 18 | } catch (error) { 19 | process.stderr.write(`Error retrieving Miro mind map node: ${error}\n`); 20 | return ServerResponse.error(error); 21 | } 22 | } 23 | }; 24 | 25 | export default getMindmapNodeTool; ``` -------------------------------------------------------------------------------- /src/tools/getBoardClassification.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 | const getBoardClassificationTool: ToolSchema = { 7 | name: "get-board-classification", 8 | description: "Retrieves board classification for a board (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("id of the organization"), 11 | teamId: z.string().describe("id of the team"), 12 | boardId: z.string().describe("Unique identifier of the board that you want to retrieve"), 13 | }, 14 | fn: async ({ orgId, teamId, boardId }) => { 15 | try { 16 | const response = await MiroClient.getApi().enterpriseDataclassificationBoardGet(orgId, teamId, boardId); 17 | 18 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 19 | } catch (error) { 20 | process.stderr.write(`Error retrieving board classification: ${error}\n`); 21 | return ServerResponse.error(error); 22 | } 23 | } 24 | }; 25 | 26 | export default getBoardClassificationTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteTag.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 | const deleteTagTool: ToolSchema = { 7 | name: "delete-tag", 8 | description: "Delete a specific tag from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the tag"), 11 | tagId: z.string().describe("Unique identifier (ID) of the tag that you want to delete") 12 | }, 13 | fn: async ({ boardId, tagId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!tagId) { 20 | return ServerResponse.error("Tag ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteTag(boardId, tagId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Tag deleted successfully" }, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteTagTool; ``` -------------------------------------------------------------------------------- /src/tools/getEmbedItem.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 | const getEmbedItemTool: ToolSchema = { 7 | name: "get-embed-item", 8 | description: "Retrieve information about a specific embed item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the embed"), 11 | itemId: z.string().describe("Unique identifier (ID) of the embed that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().getEmbedItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify(result, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getEmbedItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getShapeItem.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 | const getShapeItemTool: ToolSchema = { 7 | name: "get-shape-item", 8 | description: "Retrieve information about a specific shape item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the shape"), 11 | itemId: z.string().describe("Unique identifier (ID) of the shape that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().getShapeItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify(result, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getShapeItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getItemTags.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 | const getItemTagsTool: ToolSchema = { 7 | name: "get-item-tags", 8 | description: "Retrieve all tags attached to a specific item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the item"), 11 | itemId: z.string().describe("Unique identifier (ID) of the item whose tags you want to retrieve"), 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().getTagsFromItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify(result, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getItemTagsTool; ``` -------------------------------------------------------------------------------- /src/tools/getCardItem.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 | const getCardItemTool: ToolSchema = { 7 | name: "get-card-item", 8 | description: "Retrieve information about a specific card item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the card"), 11 | itemId: z.string().describe("Unique identifier (ID) of the card that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const itemData = await MiroClient.getApi().getCardItem(boardId, itemId); 24 | 25 | return ServerResponse.text(JSON.stringify(itemData, null, 2)); 26 | } catch (error) { 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | } 31 | 32 | export default getCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/removeBoardMember.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 | const removeBoardMemberTool: ToolSchema = { 7 | name: "remove-board-member", 8 | description: "Remove a specific member from a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board"), 11 | memberId: z.string().describe("ID of the board member to remove") 12 | }, 13 | fn: async ({ boardId, memberId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!memberId) { 20 | return ServerResponse.error("Member ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().removeBoardMember(boardId, memberId); 24 | 25 | return ServerResponse.text(JSON.stringify(result, null, 2)); 26 | } catch (error) { 27 | process.stderr.write(`Error removing board member: ${error}\n`); 28 | return ServerResponse.error(error); 29 | } 30 | } 31 | } 32 | 33 | export default removeBoardMemberTool; 34 | ``` -------------------------------------------------------------------------------- /src/tools/deleteBoard.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 | const deleteBoardTool: ToolSchema = { 7 | name: "delete-board", 8 | description: "Delete a Miro board by its ID. Deleted boards go to Trash (on paid plans) and can be restored via UI within 90 days after deletion.", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that you want to delete") 11 | }, 12 | fn: async ({ boardId }) => { 13 | try { 14 | if (!boardId) { 15 | return ServerResponse.error("Board ID is required"); 16 | } 17 | 18 | await MiroClient.getApi().deleteBoard(boardId); 19 | 20 | return ServerResponse.text(JSON.stringify({ 21 | success: true, 22 | message: `Board ${boardId} has been successfully deleted.` 23 | }, null, 2)); 24 | } catch (error) { 25 | process.stderr.write(`Error deleting Miro board: ${error}\n`); 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/getTextItem.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 | const getTextItemTool: ToolSchema = { 7 | name: "get-text-item", 8 | description: "Retrieve information about a specific text item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the text item"), 11 | itemId: z.string().describe("Unique identifier (ID) of the text item that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const textData = await MiroClient.getApi().getTextItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify(textData, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getTextItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getGroup.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 | const getGroupTool: ToolSchema = { 7 | name: "get-group", 8 | description: "Retrieve information about a specific group on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board that contains the group"), 11 | groupId: z.string().describe("ID of the group that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, groupId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!groupId) { 20 | return ServerResponse.error("Group ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().getGroupById(boardId, groupId); 24 | 25 | return ServerResponse.text(JSON.stringify(result, null, 2)); 26 | } catch (error) { 27 | process.stderr.write(`Error retrieving group: ${error}\n`); 28 | return ServerResponse.error(error); 29 | } 30 | } 31 | } 32 | 33 | export default getGroupTool; ``` -------------------------------------------------------------------------------- /src/tools/getSpecificItem.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 | const getSpecificItemTool: ToolSchema = { 7 | name: "get-specific-item", 8 | description: "Retrieve information about a specific item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the item"), 11 | itemId: z.string().describe("Unique identifier (ID) of the item that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const itemData = await MiroClient.getApi().getSpecificItem(boardId, itemId); 24 | 25 | return ServerResponse.text(JSON.stringify(itemData, null, 2)); 26 | } catch (error) { 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | } 31 | 32 | export default getSpecificItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteEmbedItem.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 | const deleteEmbedItemTool: ToolSchema = { 7 | name: "delete-embed-item", 8 | description: "Delete a specific embed item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the embed"), 11 | itemId: z.string().describe("Unique identifier (ID) of the embed that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteEmbedItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Embed deleted successfully" }, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteEmbedItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteShapeItem.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 | const deleteShapeItemTool: ToolSchema = { 7 | name: "delete-shape-item", 8 | description: "Delete a specific shape item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the shape"), 11 | itemId: z.string().describe("Unique identifier (ID) of the shape that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteShapeItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Shape deleted successfully" }, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteShapeItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getAppCardItem.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 | const getAppCardItemTool: ToolSchema = { 7 | name: "get-app-card-item", 8 | description: "Retrieve information about a specific app card item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the app card"), 11 | itemId: z.string().describe("Unique identifier (ID) of the app card that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const itemData = await MiroClient.getApi().getAppCardItem(boardId, itemId); 24 | 25 | return ServerResponse.text(JSON.stringify(itemData, null, 2)); 26 | } catch (error) { 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | } 31 | 32 | export default getAppCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteTextItem.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 | const deleteTextItemTool: ToolSchema = { 7 | name: "delete-text-item", 8 | description: "Delete a specific text item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the text item"), 11 | itemId: z.string().describe("Unique identifier (ID) of the text item that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteTextItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Text item successfully deleted" })); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteTextItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getDocumentItem.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 | const getDocumentItemTool: ToolSchema = { 7 | name: "get-document-item", 8 | description: "Retrieve information about a specific document item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the document"), 11 | itemId: z.string().describe("Unique identifier (ID) of the document that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const documentData = await MiroClient.getApi().getDocumentItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify(documentData, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getDocumentItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getImageItem.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 | const getImageItemTool: ToolSchema = { 7 | name: "get-image-item", 8 | description: "Retrieve information about a specific image item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the image"), 11 | itemId: z.string().describe("Unique identifier (ID) of the image that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | // Use generic getItem for image item 24 | const result = await MiroClient.getApi().getImageItem(boardId, itemId); 25 | return ServerResponse.text(JSON.stringify(result, null, 2)); 26 | } catch (error) { 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | } 31 | 32 | export default getImageItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getLegalHold.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 | const getLegalHoldTool: ToolSchema = { 7 | name: "get-legal-hold", 8 | description: "Retrieves information about a specific legal hold (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("The ID of the organization for which you want to retrieve the legal hold information"), 11 | caseId: z.string().describe("The ID of the case for which you want to retrieve the legal hold information"), 12 | legalHoldId: z.string().describe("The ID of the legal hold you want to retrieve") 13 | }, 14 | fn: async ({ orgId, caseId, legalHoldId }) => { 15 | try { 16 | const response = await MiroClient.getApi().getLegalHold(orgId, caseId, legalHoldId); 17 | 18 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 19 | } catch (error) { 20 | process.stderr.write(`Error retrieving legal hold: ${error}\n`); 21 | return ServerResponse.error(error); 22 | } 23 | } 24 | }; 25 | 26 | export default getLegalHoldTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteDocumentItem.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 | const deleteDocumentItemTool: ToolSchema = { 7 | name: "delete-document-item", 8 | description: "Delete a specific document item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the document"), 11 | itemId: z.string().describe("Unique identifier (ID) of the document that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteDocumentItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Document item successfully deleted" })); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteDocumentItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteImageItem.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 | const deleteImageItemTool: ToolSchema = { 7 | name: "delete-image-item", 8 | description: "Delete a specific image item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the image"), 11 | itemId: z.string().describe("Unique identifier (ID) of the image that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | // Use generic deleteItem 24 | await MiroClient.getApi().deleteItem(boardId, itemId); 25 | return ServerResponse.text(JSON.stringify({ success: true, message: "Image deleted successfully" }, null, 2)); 26 | } catch (error) { 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | } 31 | 32 | export default deleteImageItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getAllCases.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 | const getAllCasesTool: ToolSchema = { 7 | name: "get-all-cases", 8 | description: "Retrieves the list of eDiscovery cases in an organization (Enterprise only)", 9 | args: { 10 | limit: z.number().describe("The maximum number of items in the result list"), 11 | orgId: z.string().describe("The ID of the organization for which you want to retrieve the list of cases"), 12 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 13 | }, 14 | fn: async ({ limit, orgId, cursor }) => { 15 | try { 16 | const query: any = {}; 17 | if (cursor) query.cursor = cursor; 18 | 19 | const response = await MiroClient.getApi().getAllCases(limit, orgId, query); 20 | 21 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 22 | } catch (error) { 23 | process.stderr.write(`Error retrieving cases: ${error}\n`); 24 | return ServerResponse.error(error); 25 | } 26 | } 27 | }; 28 | 29 | export default getAllCasesTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteItem.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 | const deleteItemTool: ToolSchema = { 7 | name: "delete-item", 8 | description: "Delete a specific item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the item"), 11 | itemId: z.string().describe("Unique identifier (ID) of the item that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteItem(boardId, itemId); 24 | 25 | return ServerResponse.text(JSON.stringify({ 26 | success: true, 27 | message: `Item ${itemId} successfully deleted from board ${boardId}` 28 | }, null, 2)); 29 | } catch (error) { 30 | return ServerResponse.error(error); 31 | } 32 | } 33 | } 34 | 35 | export default deleteItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getItemsOnBoard.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 | const getItemsOnBoardTool: ToolSchema = { 7 | name: "get-items-on-board", 8 | description: "Retrieve all items on a specific Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board whose items you want to retrieve"), 11 | limit: z.number().optional().nullish().describe("Maximum number of items to return (default: 50)"), 12 | offset: z.number().optional().nullish().describe("Offset for pagination (default: 0)") 13 | }, 14 | fn: async ({ boardId, limit = 50, offset = 0 }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | const itemsData = await MiroClient.getApi().getItems(boardId, { 21 | limit: limit.toString(), 22 | }); 23 | 24 | return ServerResponse.text(JSON.stringify(itemsData, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getItemsOnBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/getStickyNoteItem.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 | const getStickyNoteItemTool: ToolSchema = { 7 | name: "get-sticky-note-item", 8 | description: "Retrieve information about a specific sticky note item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the sticky note"), 11 | itemId: z.string().describe("Unique identifier (ID) of the sticky note that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | const stickyNoteData = await MiroClient.getApi().getStickyNoteItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify(stickyNoteData, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getStickyNoteItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getSpecificBoardMember.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 | const getSpecificBoardMemberTool: ToolSchema = { 7 | name: "get-specific-board-member", 8 | description: "Retrieve details of a specific member on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board"), 11 | memberId: z.string().describe("ID of the specific board member to retrieve") 12 | }, 13 | fn: async ({ boardId, memberId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!memberId) { 20 | return ServerResponse.error("Member ID is required"); 21 | } 22 | 23 | const result = await MiroClient.getApi().getSpecificBoardMember(boardId, memberId); 24 | 25 | return ServerResponse.text(JSON.stringify(result, null, 2)); 26 | } catch (error) { 27 | process.stderr.write(`Error retrieving specific board member: ${error}\n`); 28 | return ServerResponse.error(error); 29 | } 30 | } 31 | } 32 | 33 | export default getSpecificBoardMemberTool; 34 | ``` -------------------------------------------------------------------------------- /src/tools/getFrameItem.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 | const getFrameItemTool: ToolSchema = { 7 | name: "get-frame-item", 8 | description: "Retrieve information for a specific frame on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the frame that you want to retrieve"), 11 | itemId: z.string().describe("Unique identifier (ID) of the frame that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, itemId }: { 14 | boardId: string, 15 | itemId: string 16 | }) => { 17 | try { 18 | if (!boardId) { 19 | return ServerResponse.error("Board ID is required"); 20 | } 21 | 22 | if (!itemId) { 23 | return ServerResponse.error("Item ID is required"); 24 | } 25 | 26 | const result = await MiroClient.getApi().getFrameItem(boardId, itemId); 27 | return ServerResponse.text(JSON.stringify(result, null, 2)); 28 | } catch (error) { 29 | return ServerResponse.error(error); 30 | } 31 | } 32 | } 33 | 34 | export default getFrameItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteConnector.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 | const deleteConnectorTool: ToolSchema = { 7 | name: "delete-connector", 8 | description: "Delete a specific connector from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the connector"), 11 | connectorId: z.string().describe("Unique identifier (ID) of the connector that you want to delete") 12 | }, 13 | fn: async ({ boardId, connectorId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!connectorId) { 20 | return ServerResponse.error("Connector ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteConnector(boardId, connectorId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Connector deleted successfully" }, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteConnectorTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteFrameItem.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 | const deleteFrameItemTool: ToolSchema = { 7 | name: "delete-frame-item", 8 | description: "Delete a frame from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board from which you want to delete the frame"), 11 | itemId: z.string().describe("Unique identifier (ID) of the frame that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }: { 14 | boardId: string, 15 | itemId: string 16 | }) => { 17 | try { 18 | if (!boardId) { 19 | return ServerResponse.error("Board ID is required"); 20 | } 21 | 22 | if (!itemId) { 23 | return ServerResponse.error("Item ID is required"); 24 | } 25 | 26 | await MiroClient.getApi().deleteFrameItem(boardId, itemId); 27 | return ServerResponse.text(JSON.stringify({ success: true, message: "Frame successfully deleted" }, null, 2)); 28 | } catch (error) { 29 | return ServerResponse.error(error); 30 | } 31 | } 32 | } 33 | 34 | export default deleteFrameItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getSpecificConnector.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 | const getSpecificConnectorTool: ToolSchema = { 7 | name: "get-specific-connector", 8 | description: "Retrieve information about a specific connector on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the connector"), 11 | connectorId: z.string().describe("Unique identifier (ID) of the connector that you want to retrieve") 12 | }, 13 | fn: async ({ boardId, connectorId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!connectorId) { 20 | return ServerResponse.error("Connector ID is required"); 21 | } 22 | 23 | const connectorData = await MiroClient.getApi().getConnector(boardId, connectorId); 24 | return ServerResponse.text(JSON.stringify(connectorData, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default getSpecificConnectorTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteStickyNoteItem.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 | const deleteStickyNoteItemTool: ToolSchema = { 7 | name: "delete-sticky-note-item", 8 | description: "Delete a specific sticky note item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the sticky note"), 11 | itemId: z.string().describe("Unique identifier (ID) of the sticky note that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteStickyNoteItem(boardId, itemId); 24 | return ServerResponse.text(JSON.stringify({ success: true, message: "Sticky note deleted successfully" }, null, 2)); 25 | } catch (error) { 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | } 30 | 31 | export default deleteStickyNoteItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteCardItem.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 | const deleteCardItemTool: ToolSchema = { 7 | name: "delete-card-item", 8 | description: "Delete a specific card item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the card"), 11 | itemId: z.string().describe("Unique identifier (ID) of the card that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteCardItem(boardId, itemId); 24 | 25 | return ServerResponse.text(JSON.stringify({ 26 | success: true, 27 | message: `Card Item ${itemId} successfully deleted from board ${boardId}` 28 | }, null, 2)); 29 | } catch (error) { 30 | return ServerResponse.error(error); 31 | } 32 | } 33 | } 34 | 35 | export default deleteCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteAppCardItem.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 | const deleteAppCardItemTool: ToolSchema = { 7 | name: "delete-app-card-item", 8 | description: "Delete a specific app card item from a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the app card"), 11 | itemId: z.string().describe("Unique identifier (ID) of the app card that you want to delete") 12 | }, 13 | fn: async ({ boardId, itemId }) => { 14 | try { 15 | if (!boardId) { 16 | return ServerResponse.error("Board ID is required"); 17 | } 18 | 19 | if (!itemId) { 20 | return ServerResponse.error("Item ID is required"); 21 | } 22 | 23 | await MiroClient.getApi().deleteAppCardItem(boardId, itemId); 24 | 25 | return ServerResponse.text(JSON.stringify({ 26 | success: true, 27 | message: `App Card Item ${itemId} successfully deleted from board ${boardId}` 28 | }, null, 2)); 29 | } catch (error) { 30 | return ServerResponse.error(error); 31 | } 32 | } 33 | } 34 | 35 | export default deleteAppCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/getAllGroups.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 | const getAllGroupsTool: ToolSchema = { 7 | name: "get-all-groups", 8 | description: "Retrieve all groups on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board whose groups you want to retrieve"), 11 | limit: z.number().optional().nullish().describe("Maximum number of groups to return (default: 50)"), 12 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 13 | }, 14 | fn: async ({ boardId, limit, cursor }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | const options: any = {}; 21 | if (limit) options.limit = limit; 22 | if (cursor) options.cursor = cursor; 23 | 24 | const result = await MiroClient.getApi().getAllGroups(boardId, options); 25 | 26 | return ServerResponse.text(JSON.stringify(result, null, 2)); 27 | } catch (error) { 28 | process.stderr.write(`Error retrieving groups: ${error}\n`); 29 | return ServerResponse.error(error); 30 | } 31 | } 32 | } 33 | 34 | export default getAllGroupsTool; ``` -------------------------------------------------------------------------------- /src/tools/getAllBoardMembers.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 | const getAllBoardMembersTool: ToolSchema = { 7 | name: "get-all-board-members", 8 | description: "Retrieve all members of a specific Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board to retrieve members from"), 11 | limit: z.number().optional().nullish().describe("Maximum number of members to retrieve (default: 50)"), 12 | offset: z.number().optional().nullish().describe("Offset for pagination (default: 0)") 13 | }, 14 | fn: async ({ boardId, limit = 50, offset = 0 }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | const result = await MiroClient.getApi().getBoardMembers(boardId, { 21 | limit: limit.toString(), 22 | offset: offset.toString() 23 | }); 24 | 25 | return ServerResponse.text(JSON.stringify(result, null, 2)); 26 | } catch (error) { 27 | process.stderr.write(`Error retrieving board members: ${error}\n`); 28 | return ServerResponse.error(error); 29 | } 30 | } 31 | } 32 | 33 | export default getAllBoardMembersTool; 34 | ``` -------------------------------------------------------------------------------- /src/tools/createGroup.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 | const createGroupTool: ToolSchema = { 7 | name: "create-group", 8 | description: "Create a new group on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board where the group will be created"), 11 | data: z.object({ 12 | items: z.array(z.string()).describe("List of item IDs to include in the group") 13 | }).describe("Group data with item IDs to include in the group") 14 | }, 15 | fn: async ({ boardId, data }) => { 16 | try { 17 | if (!boardId) { 18 | return ServerResponse.error("Board ID is required"); 19 | } 20 | 21 | if (!data || !data.items || data.items.length === 0) { 22 | return ServerResponse.error("At least one item ID is required in the 'items' array"); 23 | } 24 | 25 | const result = await MiroClient.getApi().createGroup(boardId, { data }); 26 | 27 | return ServerResponse.text(JSON.stringify(result, null, 2)); 28 | } catch (error) { 29 | process.stderr.write(`Error creating group: ${error}\n`); 30 | return ServerResponse.error(error); 31 | } 32 | } 33 | } 34 | 35 | export default createGroupTool; ``` -------------------------------------------------------------------------------- /src/tools/removeProjectMember.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 | const removeProjectMemberTool: ToolSchema = { 7 | name: "remove-project-member", 8 | description: "Removes a member from a project (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("The ID of the organization to which the project belongs"), 11 | teamId: z.string().describe("The ID of the team to which the project belongs"), 12 | projectId: z.string().describe("The ID of the project from which you want to remove a member"), 13 | memberId: z.string().describe("The ID of the member that you want to remove from a project") 14 | }, 15 | fn: async ({ orgId, teamId, projectId, memberId }) => { 16 | try { 17 | const response = await MiroClient.getApi().enterpriseDeleteProjectMember( 18 | orgId, 19 | teamId, 20 | projectId, 21 | memberId 22 | ); 23 | 24 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 25 | } catch (error) { 26 | process.stderr.write(`Error removing project member: ${error}\n`); 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | }; 31 | 32 | export default removeProjectMemberTool; ``` -------------------------------------------------------------------------------- /src/tools/shareBoard.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 | const shareBoardTool: ToolSchema = { 7 | name: "share-board", 8 | description: "Share a Miro board with specific access level and optional team assignment", 9 | args: { 10 | boardId: z.string().describe("ID of the board to share"), 11 | accessLevel: z.enum(['private', 'view', 'comment', 'edit']).describe("Access level for shared board"), 12 | teamId: z.string().optional().nullish().describe("Team ID to assign the board to"), 13 | }, 14 | fn: async ({ boardId, accessLevel, teamId }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | const boardChanges = { 21 | sharingPolicy: { 22 | access: accessLevel 23 | }, 24 | teamId 25 | }; 26 | 27 | const result = await MiroClient.getApi().updateBoard(boardId, boardChanges); 28 | 29 | return ServerResponse.text(JSON.stringify(result, null, 2)); 30 | } catch (error) { 31 | process.stderr.write(`Error sharing Miro board: ${error}\n`); 32 | return ServerResponse.error(error); 33 | } 34 | } 35 | } 36 | 37 | export default shareBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/getMindmapNodes.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 | const getMindmapNodesTool: ToolSchema = { 7 | name: "get-mindmap-nodes", 8 | description: "Retrieve a list of mind map nodes on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board from which you want to retrieve mind map nodes"), 11 | limit: z.number().optional().nullish().describe("Maximum number of results to return (default: 50)"), 12 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 13 | }, 14 | fn: async ({ boardId, limit, cursor }) => { 15 | try { 16 | // Prepare query parameters 17 | const query: any = {}; 18 | if (limit) query.limit = limit.toString(); 19 | if (cursor) query.cursor = cursor; 20 | 21 | const response = await MiroClient.getApi().getMindmapNodesExperimental(boardId, query); 22 | 23 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 24 | } catch (error) { 25 | process.stderr.write(`Error retrieving Miro mind map nodes: ${error}\n`); 26 | return ServerResponse.error(error); 27 | } 28 | } 29 | }; 30 | 31 | export default getMindmapNodesTool; ``` -------------------------------------------------------------------------------- /src/tools/getAllTags.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 | const getAllTagsTool: ToolSchema = { 7 | name: "get-all-tags", 8 | description: "Retrieve all tags on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board for which you want to retrieve all tags"), 11 | limit: z.number().optional().nullish().describe("Maximum number of tags to return (default: 50)"), 12 | offset: z.number().optional().nullish().describe("Offset for pagination (default: 0)") 13 | }, 14 | fn: async ({ boardId, limit, offset }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | const query: Record<string, any> = {}; 21 | 22 | if (limit !== undefined) { 23 | query.limit = limit; 24 | } 25 | 26 | if (offset !== undefined) { 27 | query.offset = offset; 28 | } 29 | 30 | const result = await MiroClient.getApi().getTagsFromBoard(boardId, query); 31 | return ServerResponse.text(JSON.stringify(result, null, 2)); 32 | } catch (error) { 33 | return ServerResponse.error(error); 34 | } 35 | } 36 | } 37 | 38 | export default getAllTagsTool; ``` -------------------------------------------------------------------------------- /src/tools/getConnectors.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 | const getConnectorsTool: ToolSchema = { 7 | name: "get-connectors", 8 | description: "Retrieve all connectors on a specific Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board whose connectors you want to retrieve"), 11 | limit: z.number().optional().nullish().describe("Maximum number of connectors to return (default: 50)"), 12 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 13 | }, 14 | fn: async ({ boardId, limit, cursor }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | const queryParams: { limit?: string; cursor?: string } = {}; 21 | if (limit) queryParams.limit = limit.toString(); 22 | if (cursor) queryParams.cursor = cursor; 23 | 24 | const connectorsData = await MiroClient.getApi().getConnectors(boardId, queryParams); 25 | return ServerResponse.text(JSON.stringify(connectorsData, null, 2)); 26 | } catch (error) { 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | } 31 | 32 | export default getConnectorsTool; ``` -------------------------------------------------------------------------------- /src/tools/getAllLegalHolds.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 | const getLegalHoldsTool: ToolSchema = { 7 | name: "get-all-legal-holds", 8 | description: "Retrieves the list of all legal holds within a case (Enterprise only)", 9 | args: { 10 | limit: z.number().describe("The maximum number of items in the result list"), 11 | orgId: z.string().describe("The ID of the organization for which you want to retrieve the list of legal holds"), 12 | caseId: z.string().describe("The ID of the case for which you want to retrieve the list of legal holds"), 13 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 14 | }, 15 | fn: async ({ limit, orgId, caseId, cursor }) => { 16 | try { 17 | const query: any = {}; 18 | if (cursor) query.cursor = cursor; 19 | 20 | const response = await MiroClient.getApi().getAllLegalHolds(limit, orgId, caseId, query); 21 | 22 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 23 | } catch (error) { 24 | process.stderr.write(`Error retrieving legal holds: ${error}\n`); 25 | return ServerResponse.error(error); 26 | } 27 | } 28 | }; 29 | 30 | export default getLegalHoldsTool; ``` -------------------------------------------------------------------------------- /src/tools/attachTag.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 | const attachTagTool: ToolSchema = { 7 | name: "attach-tag", 8 | description: "Attach a tag to an item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the tag and item"), 11 | tagId: z.string().describe("Unique identifier (ID) of the tag that you want to attach"), 12 | itemId: z.string().describe("Unique identifier (ID) of the item to which you want to attach the tag") 13 | }, 14 | fn: async ({ boardId, tagId, itemId }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | if (!tagId) { 21 | return ServerResponse.error("Tag ID is required"); 22 | } 23 | 24 | if (!itemId) { 25 | return ServerResponse.error("Item ID is required"); 26 | } 27 | 28 | await MiroClient.getApi().attachTagToItem(boardId, itemId, tagId); 29 | return ServerResponse.text(JSON.stringify({ success: true, message: "Tag attached successfully" }, null, 2)); 30 | } catch (error) { 31 | return ServerResponse.error(error); 32 | } 33 | } 34 | } 35 | 36 | export default attachTagTool; ``` -------------------------------------------------------------------------------- /src/tools/getProjectMember.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 | const getProjectMemberTool: ToolSchema = { 7 | name: "get-project-member", 8 | description: "Retrieves information about a specific project member (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("The ID of the organization to which the project belongs"), 11 | teamId: z.string().describe("The ID of the team to which the project belongs"), 12 | projectId: z.string().describe("The ID of the project from which you want to retrieve specific member information"), 13 | memberId: z.string().describe("The ID of the member for which you want to retrieve information") 14 | }, 15 | fn: async ({ orgId, teamId, projectId, memberId }) => { 16 | try { 17 | const response = await MiroClient.getApi().enterpriseGetProjectMember( 18 | orgId, 19 | teamId, 20 | projectId, 21 | memberId 22 | ); 23 | 24 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 25 | } catch (error) { 26 | process.stderr.write(`Error retrieving project member: ${error}\n`); 27 | return ServerResponse.error(error); 28 | } 29 | } 30 | }; 31 | 32 | export default getProjectMemberTool; ``` -------------------------------------------------------------------------------- /src/tools/deleteGroup.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 | const deleteGroupTool: ToolSchema = { 7 | name: "delete-group", 8 | description: "Delete a specific group from a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board that contains the group"), 11 | groupId: z.string().describe("ID of the group that you want to delete"), 12 | deleteItems: z.boolean().optional().nullish().describe("Indicates whether the items should be removed. Set to true to delete items in the group, false to keep them") 13 | }, 14 | fn: async ({ boardId, groupId, deleteItems }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | if (!groupId) { 21 | return ServerResponse.error("Group ID is required"); 22 | } 23 | 24 | await MiroClient.getApi().deleteGroup(boardId, groupId, deleteItems ?? false); 25 | 26 | return ServerResponse.text(JSON.stringify({ success: true, message: "Group successfully deleted" }, null, 2)); 27 | } catch (error) { 28 | process.stderr.write(`Error deleting group: ${error}\n`); 29 | return ServerResponse.error(error); 30 | } 31 | } 32 | } 33 | 34 | export default deleteGroupTool; ``` -------------------------------------------------------------------------------- /src/tools/updateBoardClassification.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 | const updateBoardClassificationTool: ToolSchema = { 7 | name: "update-board-classification", 8 | description: "Updates board classification for an existing board (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("id of the organization"), 11 | teamId: z.string().describe("id of the team"), 12 | boardId: z.string().describe("Unique identifier of the board that you want to update"), 13 | labelId: z.string().describe("Unique identifier of the classification label to apply") 14 | }, 15 | fn: async ({ orgId, teamId, boardId, labelId }) => { 16 | try { 17 | const dataClassificationLabelId = { 18 | labelId: labelId 19 | }; 20 | 21 | const response = await MiroClient.getApi().enterpriseDataclassificationBoardSet( 22 | orgId, 23 | teamId, 24 | boardId, 25 | dataClassificationLabelId 26 | ); 27 | 28 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 29 | } catch (error) { 30 | process.stderr.write(`Error updating board classification: ${error}\n`); 31 | return ServerResponse.error(error); 32 | } 33 | } 34 | }; 35 | 36 | export default updateBoardClassificationTool; ``` -------------------------------------------------------------------------------- /src/tools/createBoardExportJob.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 | const createBoardExportJobTool: ToolSchema = { 7 | name: "create-board-export-job", 8 | description: "Creates an export job for one or more boards (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("Unique identifier of the organization"), 11 | requestId: z.string().describe("Unique identifier of the board export job"), 12 | boardIds: z.array(z.string()).describe("Array of board IDs to export"), 13 | format: z.enum(["pdf", "csv"]).optional().nullish().describe("Export format (default: pdf)") 14 | }, 15 | fn: async ({ orgId, requestId, boardIds, format }) => { 16 | try { 17 | const createBoardExportRequest = { 18 | boardIds: boardIds, 19 | ...(format && { format: format }) 20 | }; 21 | 22 | const response = await MiroClient.getApi().enterpriseCreateBoardExport( 23 | orgId, 24 | requestId, 25 | createBoardExportRequest 26 | ); 27 | 28 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 29 | } catch (error) { 30 | process.stderr.write(`Error creating board export job: ${error}\n`); 31 | return ServerResponse.error(error); 32 | } 33 | } 34 | }; 35 | 36 | export default createBoardExportJobTool; ``` -------------------------------------------------------------------------------- /src/tools/detachTag.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 | const detachTagTool: ToolSchema = { 7 | name: "detach-tag", 8 | description: "Detach a tag from an item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the tag and item"), 11 | tagId: z.string().describe("Unique identifier (ID) of the tag that you want to detach"), 12 | itemId: z.string().describe("Unique identifier (ID) of the item from which you want to detach the tag") 13 | }, 14 | fn: async ({ boardId, tagId, itemId }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | if (!tagId) { 21 | return ServerResponse.error("Tag ID is required"); 22 | } 23 | 24 | if (!itemId) { 25 | return ServerResponse.error("Item ID is required"); 26 | } 27 | 28 | // Use the SDK method to detach a tag from an item 29 | await MiroClient.getApi().removeTagFromItem(boardId, itemId, tagId); 30 | return ServerResponse.text(JSON.stringify({ success: true, message: "Tag detached successfully" }, null, 2)); 31 | } catch (error) { 32 | return ServerResponse.error(error); 33 | } 34 | } 35 | } 36 | 37 | export default detachTagTool; ``` -------------------------------------------------------------------------------- /src/tools/ungroupItems.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 | const ungroupItemsTool: ToolSchema = { 7 | name: "ungroup-items", 8 | description: "Ungroup a specific group on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board that contains the group"), 11 | groupId: z.string().describe("ID of the group that you want to ungroup"), 12 | deleteItems: z.boolean().optional().nullish().describe("Indicates whether the items should be removed. By default, false.") 13 | }, 14 | fn: async ({ boardId, groupId, deleteItems }) => { 15 | try { 16 | if (!boardId) { 17 | return ServerResponse.error("Board ID is required"); 18 | } 19 | 20 | if (!groupId) { 21 | return ServerResponse.error("Group ID is required"); 22 | } 23 | 24 | const options: any = {}; 25 | if (deleteItems !== undefined) options.deleteItems = deleteItems; 26 | 27 | await MiroClient.getApi().unGroup(boardId, groupId, options); 28 | 29 | return ServerResponse.text(JSON.stringify({ success: true, message: "Group successfully ungrouped" }, null, 2)); 30 | } catch (error) { 31 | process.stderr.write(`Error ungrouping items: ${error}\n`); 32 | return ServerResponse.error(error); 33 | } 34 | } 35 | } 36 | 37 | export default ungroupItemsTool; ``` -------------------------------------------------------------------------------- /src/tools/createTag.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 { TagCreateRequest } from '@mirohq/miro-api/dist/model/tagCreateRequest.js'; 7 | 8 | const createTagTool: ToolSchema = { 9 | name: "create-tag", 10 | description: "Create a new tag on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board where the tag will be created"), 13 | data: z.object({ 14 | title: z.string().describe("Title of the tag") 15 | }).describe("The content and configuration of the tag"), 16 | fillColor: z.string().optional().nullish().describe("Fill color of the tag (hex format, e.g. #000000)") 17 | }, 18 | fn: async ({ boardId, data, fillColor }) => { 19 | try { 20 | if (!boardId) { 21 | return ServerResponse.error("Board ID is required"); 22 | } 23 | 24 | const createRequest = new TagCreateRequest(); 25 | createRequest.title = data.title; 26 | 27 | if (fillColor) { 28 | createRequest.fillColor = fillColor; 29 | } 30 | 31 | const result = await MiroClient.getApi().createTag(boardId, createRequest); 32 | return ServerResponse.text(JSON.stringify(result, null, 2)); 33 | } catch (error) { 34 | return ServerResponse.error(error); 35 | } 36 | } 37 | } 38 | 39 | export default createTagTool; ``` -------------------------------------------------------------------------------- /src/tools/addProjectMember.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 | const addProjectMemberTool: ToolSchema = { 7 | name: "add-project-member", 8 | description: "Adds a member to a project (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("The ID of the organization to which the project belongs"), 11 | teamId: z.string().describe("The ID of the team to which the project belongs"), 12 | projectId: z.string().describe("The ID of the project to which you want to add a user"), 13 | email: z.string().describe("Email ID of the user to add to the project"), 14 | role: z.enum(["owner", "editor", "commenter", "viewer"]).describe("Role to assign to the user") 15 | }, 16 | fn: async ({ orgId, teamId, projectId, email, role }) => { 17 | try { 18 | const addProjectMemberRequest = { 19 | email, 20 | role 21 | }; 22 | 23 | const response = await MiroClient.getApi().enterpriseAddProjectMember( 24 | orgId, 25 | teamId, 26 | projectId, 27 | addProjectMemberRequest 28 | ); 29 | 30 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 31 | } catch (error) { 32 | process.stderr.write(`Error adding project member: ${error}\n`); 33 | return ServerResponse.error(error); 34 | } 35 | } 36 | }; 37 | 38 | export default addProjectMemberTool; ``` -------------------------------------------------------------------------------- /src/tools/createBoard.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 | const createBoardTool: ToolSchema = { 7 | name: "create-board", 8 | description: "Create a new Miro board with specified name and sharing policies", 9 | args: { 10 | name: z.string().describe("Name of the board to create"), 11 | description: z.string().optional().nullish().describe("Description of the board"), 12 | sharingPolicy: z.enum(['private', 'view', 'comment', 'edit']).optional().nullish().describe("Sharing policy for the board"), 13 | teamId: z.string().optional().nullish().describe("Team ID to assign the board to") 14 | }, 15 | fn: async ({ name, description, sharingPolicy, teamId }) => { 16 | try { 17 | if (!name) { 18 | return ServerResponse.error("Board name is required"); 19 | } 20 | 21 | const boardChanges = { 22 | name, 23 | description, 24 | sharingPolicy: { 25 | access: sharingPolicy || 'private' 26 | }, 27 | teamId 28 | }; 29 | 30 | const boardData = await MiroClient.getApi().createBoard(boardChanges); 31 | 32 | return ServerResponse.text(JSON.stringify(boardData, null, 2)); 33 | } catch (error) { 34 | process.stderr.write(`Error creating Miro board: ${error}\n`); 35 | return ServerResponse.error(error); 36 | } 37 | } 38 | } 39 | 40 | export default createBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/getGroupItems.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 | const getGroupItemsTool: ToolSchema = { 7 | name: "get-group-items", 8 | description: "Retrieve all items in a specific group on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board that contains the group"), 11 | groupId: z.string().describe("ID of the group whose items you want to retrieve"), 12 | limit: z.number().optional().nullish().describe("Maximum number of items to return (default: 50)"), 13 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 14 | }, 15 | fn: async ({ boardId, groupId, limit, cursor }) => { 16 | try { 17 | if (!boardId) { 18 | return ServerResponse.error("Board ID is required"); 19 | } 20 | 21 | if (!groupId) { 22 | return ServerResponse.error("Group ID is required"); 23 | } 24 | 25 | const options: any = {}; 26 | if (limit) options.limit = limit; 27 | if (cursor) options.cursor = cursor; 28 | 29 | const result = await MiroClient.getApi().getItemsByGroupId(boardId, groupId, options); 30 | 31 | return ServerResponse.text(JSON.stringify(result, null, 2)); 32 | } catch (error) { 33 | process.stderr.write(`Error retrieving group items: ${error}\n`); 34 | return ServerResponse.error(error); 35 | } 36 | } 37 | } 38 | 39 | export default getGroupItemsTool; ``` -------------------------------------------------------------------------------- /src/tools/updateGroup.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 | const updateGroupTool: ToolSchema = { 7 | name: "update-group", 8 | description: "Update a specific group on a Miro board with new items", 9 | args: { 10 | boardId: z.string().describe("ID of the board that contains the group"), 11 | groupId: z.string().describe("ID of the group that you want to update"), 12 | data: z.object({ 13 | items: z.array(z.string()).describe("Updated list of item IDs to include in the group") 14 | }).describe("Updated group data with item IDs to include in the group") 15 | }, 16 | fn: async ({ boardId, groupId, data }) => { 17 | try { 18 | if (!boardId) { 19 | return ServerResponse.error("Board ID is required"); 20 | } 21 | 22 | if (!groupId) { 23 | return ServerResponse.error("Group ID is required"); 24 | } 25 | 26 | if (!data || !data.items || data.items.length === 0) { 27 | return ServerResponse.error("At least one item ID is required in the 'items' array"); 28 | } 29 | 30 | const result = await MiroClient.getApi().updateGroup(boardId, groupId, { data }); 31 | 32 | return ServerResponse.text(JSON.stringify(result, null, 2)); 33 | } catch (error) { 34 | process.stderr.write(`Error updating group: ${error}\n`); 35 | return ServerResponse.error(error); 36 | } 37 | } 38 | } 39 | 40 | export default updateGroupTool; ``` -------------------------------------------------------------------------------- /src/tools/getAuditLogs.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 | const getAuditLogsTool: ToolSchema = { 7 | name: "get-audit-logs", 8 | description: "Retrieves a page of audit events from the last 90 days (Enterprise only)", 9 | args: { 10 | createdAfter: z.string().describe("Retrieve audit logs created after this date (ISO 8601 format)"), 11 | createdBefore: z.string().describe("Retrieve audit logs created before this date (ISO 8601 format)"), 12 | cursor: z.string().optional().nullish().describe("Cursor for pagination"), 13 | limit: z.number().optional().nullish().describe("Maximum number of results to return (default: 100)"), 14 | sorting: z.enum(["ASC", "DESC"]).optional().nullish().describe("Sort order for results (default: ASC)") 15 | }, 16 | fn: async ({ createdAfter, createdBefore, cursor, limit, sorting }) => { 17 | try { 18 | const query: any = {}; 19 | if (cursor) query.cursor = cursor; 20 | if (limit) query.limit = limit; 21 | if (sorting) query.sorting = sorting; 22 | 23 | const response = await MiroClient.getApi().enterpriseGetAuditLogs( 24 | createdAfter, 25 | createdBefore, 26 | query 27 | ); 28 | 29 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 30 | } catch (error) { 31 | process.stderr.write(`Error retrieving audit logs: ${error}\n`); 32 | return ServerResponse.error(error); 33 | } 34 | } 35 | }; 36 | 37 | export default getAuditLogsTool; ``` -------------------------------------------------------------------------------- /src/tools/getLegalHoldContentItems.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 | const getLegalHoldContentItemsTool: ToolSchema = { 7 | name: "get-legal-hold-content-items", 8 | description: "Retrieves the list of content items under legal hold (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("The ID of the organization for which you want to retrieve the list of content items under hold"), 11 | caseId: z.string().describe("The ID of the case for which you want to retrieve the list of content items under hold"), 12 | legalHoldId: z.string().describe("The ID of the legal hold for which you want to retrieve the list of content items under hold"), 13 | limit: z.number().describe("The maximum number of items in the result list"), 14 | cursor: z.string().optional().nullish().describe("Cursor for pagination") 15 | }, 16 | fn: async ({ orgId, caseId, legalHoldId, limit, cursor }) => { 17 | try { 18 | const query: any = {}; 19 | if (cursor) query.cursor = cursor; 20 | 21 | const response = await MiroClient.getApi().getLegalHoldContentItems( 22 | orgId, 23 | caseId, 24 | legalHoldId, 25 | limit, 26 | query 27 | ); 28 | 29 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 30 | } catch (error) { 31 | process.stderr.write(`Error retrieving legal hold content items: ${error}\n`); 32 | return ServerResponse.error(error); 33 | } 34 | } 35 | }; 36 | 37 | export default getLegalHoldContentItemsTool; ``` -------------------------------------------------------------------------------- /src/tools/updateTag.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 { TagUpdateRequest } from '@mirohq/miro-api/dist/model/tagUpdateRequest.js'; 7 | 8 | const updateTagTool: ToolSchema = { 9 | name: "update-tag", 10 | description: "Update an existing tag on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the tag"), 13 | tagId: z.string().describe("Unique identifier (ID) of the tag that you want to update"), 14 | title: z.string().optional().nullish().describe("Updated title of the tag"), 15 | fillColor: z.string().optional().nullish().describe("Updated fill color of the tag (hex format, e.g. #000000)") 16 | }, 17 | fn: async ({ boardId, tagId, title, fillColor }) => { 18 | try { 19 | if (!boardId) { 20 | return ServerResponse.error("Board ID is required"); 21 | } 22 | 23 | if (!tagId) { 24 | return ServerResponse.error("Tag ID is required"); 25 | } 26 | 27 | const updateRequest = new TagUpdateRequest(); 28 | 29 | if (title !== undefined) { 30 | updateRequest.title = title; 31 | } 32 | 33 | if (fillColor !== undefined) { 34 | updateRequest.fillColor = fillColor; 35 | } 36 | 37 | const result = await MiroClient.getApi().updateTag(boardId, tagId, updateRequest); 38 | return ServerResponse.text(JSON.stringify(result, null, 2)); 39 | } catch (error) { 40 | return ServerResponse.error(error); 41 | } 42 | } 43 | } 44 | 45 | export default updateTagTool; ``` -------------------------------------------------------------------------------- /src/tools/updateBoardMember.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 | const updateBoardMemberTool: ToolSchema = { 7 | name: "update-board-member", 8 | description: "Update a specific member's role or status on a Miro board", 9 | args: { 10 | boardId: z.string().describe("ID of the board"), 11 | memberId: z.string().describe("ID of the board member to update"), 12 | role: z.enum(['member', 'admin', 'owner']).optional().nullish().describe("New role for the board member"), 13 | status: z.enum(['active', 'pending', 'blocked']).optional().nullish().describe("New status for the board member") 14 | }, 15 | fn: async ({ boardId, memberId, role, status }) => { 16 | try { 17 | if (!boardId) { 18 | return ServerResponse.error("Board ID is required"); 19 | } 20 | 21 | if (!memberId) { 22 | return ServerResponse.error("Member ID is required"); 23 | } 24 | 25 | if (!role && !status) { 26 | return ServerResponse.error("At least one of role or status must be provided"); 27 | } 28 | 29 | const memberChanges: any = {}; 30 | if (role) memberChanges.role = role; 31 | if (status) memberChanges.status = status; 32 | 33 | const result = await MiroClient.getApi().updateBoardMember(boardId, memberId, memberChanges); 34 | 35 | return ServerResponse.text(JSON.stringify(result, null, 2)); 36 | } catch (error) { 37 | process.stderr.write(`Error updating board member: ${error}\n`); 38 | return ServerResponse.error(error); 39 | } 40 | } 41 | } 42 | 43 | export default updateBoardMemberTool; 44 | ``` -------------------------------------------------------------------------------- /src/tools/updateBoard.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 | const updateBoardTool: ToolSchema = { 7 | name: "update-board", 8 | description: "Update an existing Miro board with new settings", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that you want to update"), 11 | name: z.string().optional().nullish().describe("New name for the board"), 12 | description: z.string().optional().nullish().describe("New description for the board"), 13 | sharingPolicy: z.enum(['private', 'view', 'comment', 'edit']).optional().nullish().describe("New sharing policy for the board"), 14 | teamId: z.string().optional().nullish().describe("New team ID to assign the board to") 15 | }, 16 | fn: async ({ boardId, name, description, sharingPolicy, teamId }) => { 17 | try { 18 | if (!boardId) { 19 | return ServerResponse.error("Board ID is required"); 20 | } 21 | 22 | const boardChanges = {}; 23 | if (name) boardChanges['name'] = name; 24 | if (description !== undefined) boardChanges['description'] = description; 25 | if (sharingPolicy) boardChanges['sharingPolicy'] = { access: sharingPolicy }; 26 | if (teamId) boardChanges['teamId'] = teamId; 27 | 28 | const boardData = await MiroClient.getApi().updateBoard(boardId, boardChanges); 29 | 30 | return ServerResponse.text(JSON.stringify(boardData, null, 2)); 31 | } catch (error) { 32 | process.stderr.write(`Error updating Miro board: ${error}\n`); 33 | return ServerResponse.error(error); 34 | } 35 | } 36 | } 37 | 38 | export default updateBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/copyBoard.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 | const copyBoardTool: ToolSchema = { 7 | name: "copy-board", 8 | description: "Create a copy of an existing Miro board with optional new settings", 9 | args: { 10 | copyFrom: z.string().describe("Unique identifier (ID) of the board that you want to copy"), 11 | name: z.string().optional().nullish().describe("Name for the new copied board"), 12 | description: z.string().optional().nullish().describe("Description for the new copied board"), 13 | sharingPolicy: z.enum(['private', 'view', 'comment', 'edit']).optional().nullish().describe("Sharing policy for the new copied board"), 14 | teamId: z.string().optional().nullish().describe("Team ID to assign the new copied board to") 15 | }, 16 | fn: async ({ copyFrom, name, description, sharingPolicy, teamId }) => { 17 | try { 18 | if (!copyFrom) { 19 | return ServerResponse.error("Source board ID is required"); 20 | } 21 | 22 | const copyBoardChanges = {}; 23 | if (name) copyBoardChanges['name'] = name; 24 | if (description !== undefined) copyBoardChanges['description'] = description; 25 | if (sharingPolicy) copyBoardChanges['sharingPolicy'] = { access: sharingPolicy }; 26 | if (teamId) copyBoardChanges['teamId'] = teamId; 27 | 28 | const boardData = await MiroClient.getApi().copyBoard(copyFrom, copyBoardChanges); 29 | 30 | return ServerResponse.text(JSON.stringify(boardData, null, 2)); 31 | } catch (error) { 32 | process.stderr.write(`Error copying Miro board: ${error}\n`); 33 | return ServerResponse.error(error); 34 | } 35 | } 36 | } 37 | 38 | export default copyBoardTool; ``` -------------------------------------------------------------------------------- /src/tools/updateItemPosition.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 | const updateItemPositionTool: ToolSchema = { 7 | name: "update-item-position", 8 | description: "Update the position or parent of a specific item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the item"), 11 | itemId: z.string().describe("Unique identifier (ID) of the item that you want to update"), 12 | position: z.object({ 13 | x: z.number().optional().nullish().describe("X coordinate of the item"), 14 | y: z.number().optional().nullish().describe("Y coordinate of the item") 15 | }).optional().nullish().describe("New position coordinates for the item"), 16 | parentId: z.string().optional().nullish().describe("Unique identifier (ID) of the new parent item") 17 | }, 18 | fn: async ({ boardId, itemId, position, parentId }) => { 19 | try { 20 | if (!boardId) { 21 | return ServerResponse.error("Board ID is required"); 22 | } 23 | 24 | if (!itemId) { 25 | return ServerResponse.error("Item ID is required"); 26 | } 27 | 28 | const updateData = {}; 29 | 30 | if (position) { 31 | updateData['position'] = position; 32 | } 33 | 34 | if (parentId) { 35 | updateData['parent'] = { id: parentId }; 36 | } 37 | 38 | if (Object.keys(updateData).length === 0) { 39 | return ServerResponse.error("At least one update parameter (position or parentId) is required"); 40 | } 41 | 42 | const updatedItem = await MiroClient.getApi().updateItemPositionOrParent(boardId, itemId, updateData); 43 | 44 | return ServerResponse.text(JSON.stringify(updatedItem, null, 2)); 45 | } catch (error) { 46 | return ServerResponse.error(error); 47 | } 48 | } 49 | } 50 | 51 | export default updateItemPositionTool; ``` -------------------------------------------------------------------------------- /src/tools/getOrganizationMembers.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 | const getOrganizationMembersTool: ToolSchema = { 7 | name: "get-organization-members", 8 | description: "Retrieves a list of members for an organization (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("id of the organization"), 11 | emails: z.string().optional().nullish().describe("Filter by comma-separated email addresses"), 12 | role: z.enum(['organization_internal_admin', 'organization_internal_user', 'organization_external_user', 'organization_team_guest_user', 'unknown']).optional().nullish().describe("Filter by user role"), 13 | license: z.enum(['full', 'occasional', 'free', 'free_restricted', 'full_trial', 'unknown']).optional().nullish().describe("Filter by license type"), 14 | active: z.boolean().optional().nullish().describe("Filter by active status"), 15 | cursor: z.string().optional().nullish().describe("Cursor for pagination"), 16 | limit: z.number().optional().nullish().describe("Maximum number of results to return") 17 | }, 18 | fn: async ({ orgId, emails, role, license, active, cursor, limit }) => { 19 | try { 20 | const query: any = {}; 21 | if (emails) query.emails = emails; 22 | if (role) query.role = role; 23 | if (license) query.license = license; 24 | if (active !== undefined) query.active = active; 25 | if (cursor) query.cursor = cursor; 26 | if (limit) query.limit = limit; 27 | 28 | const response = await MiroClient.getApi().enterpriseGetOrganizationMembers(orgId, query); 29 | 30 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 31 | } catch (error) { 32 | process.stderr.write(`Error retrieving organization members: ${error}\n`); 33 | return ServerResponse.error(error); 34 | } 35 | } 36 | }; 37 | 38 | export default getOrganizationMembersTool; ``` -------------------------------------------------------------------------------- /src/tools/getBoardContentLogs.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 | const getBoardContentLogsTool: ToolSchema = { 7 | name: "get-board-content-logs", 8 | description: "Retrieves content change logs of board items (Enterprise only)", 9 | args: { 10 | orgId: z.string().describe("Unique identifier of the organization"), 11 | from: z.string().describe("Start date for filtering (ISO 8601 format)"), 12 | to: z.string().describe("End date for filtering (ISO 8601 format)"), 13 | boardIds: z.array(z.string()).optional().nullish().describe("List of board IDs to filter by"), 14 | emails: z.array(z.string()).optional().nullish().describe("List of user emails to filter by"), 15 | cursor: z.string().optional().nullish().describe("Cursor for pagination"), 16 | limit: z.number().optional().nullish().describe("Maximum number of results to return"), 17 | sorting: z.enum(["asc", "desc"]).optional().nullish().describe("Sort order for results") 18 | }, 19 | fn: async ({ orgId, from, to, boardIds, emails, cursor, limit, sorting }) => { 20 | try { 21 | const query: any = {}; 22 | if (boardIds) query.boardIds = boardIds; 23 | if (emails) query.emails = emails; 24 | if (cursor) query.cursor = cursor; 25 | if (limit) query.limit = limit; 26 | if (sorting) query.sorting = sorting; 27 | 28 | // Convert string dates to Date objects 29 | const fromDate = new Date(from); 30 | const toDate = new Date(to); 31 | 32 | const response = await MiroClient.getApi().enterpriseBoardContentItemLogsFetch( 33 | orgId, 34 | fromDate, 35 | toDate, 36 | query 37 | ); 38 | 39 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 40 | } catch (error) { 41 | process.stderr.write(`Error retrieving board content logs: ${error}\n`); 42 | return ServerResponse.error(error); 43 | } 44 | } 45 | }; 46 | 47 | export default getBoardContentLogsTool; ``` -------------------------------------------------------------------------------- /src/tools/createMindmapNode.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 | const createMindmapNodeTool: ToolSchema = { 7 | name: "create-mindmap-node", 8 | description: "Create a new mind map node on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board where you want to create the node"), 11 | data: z.object({ 12 | content: z.string().describe("Text content for the mind map node"), 13 | parentId: z.string().optional().nullish().describe("ID of the parent node (if this is a child node)"), 14 | style: z.object({ 15 | fillColor: z.string().optional().nullish().describe("Fill color for the node"), 16 | textColor: z.string().optional().nullish().describe("Text color for the node") 17 | }).optional().nullish() 18 | }).describe("The content and style configuration of the mind map node"), 19 | position: z.object({ 20 | x: z.number().describe("X coordinate of the node"), 21 | y: z.number().describe("Y coordinate of the node") 22 | }).describe("Position of the node on the board") 23 | }, 24 | fn: async ({ boardId, data, position }) => { 25 | try { 26 | // Prepare the request data 27 | const mindmapCreateRequest = { 28 | data: { 29 | nodeView: { 30 | data: { 31 | type: "text", 32 | content: data.content 33 | } 34 | }, 35 | ...(data.parentId && { parentId: data.parentId }), 36 | ...(data.style && { style: data.style }) 37 | }, 38 | position 39 | }; 40 | 41 | // Call the Miro API to create a mind map node 42 | const response = await MiroClient.getApi().createMindmapNodesExperimental(boardId, mindmapCreateRequest); 43 | 44 | return ServerResponse.text(JSON.stringify(response.body, null, 2)); 45 | } catch (error) { 46 | process.stderr.write(`Error creating Miro mind map node: ${error}\n`); 47 | return ServerResponse.error(error); 48 | } 49 | } 50 | }; 51 | 52 | export default createMindmapNodeTool; ``` -------------------------------------------------------------------------------- /src/tools/createConnector.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 { ConnectorCreationData } from '@mirohq/miro-api/dist/model/connectorCreationData.js'; 7 | 8 | const createConnectorTool: ToolSchema = { 9 | name: "create-connector", 10 | description: "Create a new connector between items on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board where the connector will be created"), 13 | startItem: z.object({ 14 | id: z.string().describe("ID of the item at the start of the connector") 15 | }).describe("Start item of the connector"), 16 | endItem: z.object({ 17 | id: z.string().describe("ID of the item at the end of the connector") 18 | }).describe("End item of the connector"), 19 | style: z.object({ 20 | strokeColor: z.string().optional().nullish().describe("Color of the connector stroke"), 21 | strokeWidth: z.number().optional().nullish().describe("Width of the connector stroke"), 22 | strokeStyle: z.string().optional().nullish().describe("Style of the connector stroke (normal, dashed, etc.)"), 23 | startStrokeCap: z.string().optional().nullish().describe("Start stroke cap style"), 24 | endStrokeCap: z.string().optional().nullish().describe("End stroke cap style") 25 | }).optional().nullish().describe("Style configuration of the connector") 26 | }, 27 | fn: async ({ boardId, startItem, endItem, style }) => { 28 | try { 29 | if (!boardId) { 30 | return ServerResponse.error("Board ID is required"); 31 | } 32 | 33 | const connectorData = new ConnectorCreationData(); 34 | connectorData.startItem = startItem; 35 | connectorData.endItem = endItem; 36 | 37 | if (style) { 38 | connectorData.style = style; 39 | } 40 | 41 | const result = await MiroClient.getApi().createConnector(boardId, connectorData); 42 | return ServerResponse.text(JSON.stringify(result, null, 2)); 43 | } catch (error) { 44 | return ServerResponse.error(error); 45 | } 46 | } 47 | } 48 | 49 | export default createConnectorTool; ``` -------------------------------------------------------------------------------- /src/tools/createDocumentItem.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 { DocumentCreateRequest } from '@mirohq/miro-api/dist/model/documentCreateRequest.js'; 7 | import { DocumentUrlData } from '@mirohq/miro-api/dist/model/documentUrlData.js'; 8 | 9 | const createDocumentItemTool: ToolSchema = { 10 | name: "create-document-item", 11 | description: "Create a new document item on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the document will be created"), 14 | data: z.object({ 15 | url: z.string().describe("URL of the document to be added to the board"), 16 | title: z.string().optional().nullish().describe("Title of the document") 17 | }).describe("The content and configuration of the document"), 18 | position: z.object({ 19 | x: z.number().describe("X coordinate of the document"), 20 | y: z.number().describe("Y coordinate of the document") 21 | }).describe("Position of the document on the board"), 22 | geometry: z.object({ 23 | width: z.number().optional().nullish().describe("Width of the document"), 24 | height: z.number().optional().nullish().describe("Height of the document") 25 | }).optional().nullish().describe("Dimensions of the document") 26 | }, 27 | fn: async ({ boardId, data, position, geometry }) => { 28 | try { 29 | if (!boardId) { 30 | return ServerResponse.error("Board ID is required"); 31 | } 32 | 33 | const createRequest = new DocumentCreateRequest(); 34 | 35 | const documentData = new DocumentUrlData(); 36 | documentData.url = data.url; 37 | 38 | if (data.title !== undefined) documentData.title = data.title; 39 | 40 | createRequest.data = documentData; 41 | createRequest.position = position; 42 | 43 | if (geometry) { 44 | createRequest.geometry = geometry; 45 | } 46 | 47 | const result = await MiroClient.getApi().createDocumentItemUsingUrl(boardId, createRequest); 48 | return ServerResponse.text(JSON.stringify(result, null, 2)); 49 | } catch (error) { 50 | return ServerResponse.error(error); 51 | } 52 | } 53 | } 54 | 55 | export default createDocumentItemTool; ``` -------------------------------------------------------------------------------- /src/tools/createTextItem.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 { TextCreateRequest } from '@mirohq/miro-api/dist/model/textCreateRequest.js'; 7 | import { TextData } from '@mirohq/miro-api/dist/model/textData.js'; 8 | 9 | const createTextItemTool: ToolSchema = { 10 | name: "create-text-item", 11 | description: "Create a new text item on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the text will be created"), 14 | data: z.object({ 15 | content: z.string().describe("Text content of the text item") 16 | }).describe("The content of the text item"), 17 | position: z.object({ 18 | x: z.number().describe("X coordinate of the text item"), 19 | y: z.number().describe("Y coordinate of the text item") 20 | }).describe("Position of the text item on the board"), 21 | geometry: z.object({ 22 | width: z.number().optional().nullish().describe("Width of the text item") 23 | }).optional().nullish().describe("Dimensions of the text item"), 24 | style: z.object({ 25 | color: z.string().optional().nullish().describe("Color of the text"), 26 | fontSize: z.number().optional().nullish().describe("Font size of the text"), 27 | textAlign: z.string().optional().nullish().describe("Alignment of the text (left, center, right)") 28 | }).optional().nullish().describe("Style configuration of the text item") 29 | }, 30 | fn: async ({ boardId, data, position, geometry, style }) => { 31 | try { 32 | if (!boardId) { 33 | return ServerResponse.error("Board ID is required"); 34 | } 35 | 36 | const createRequest = new TextCreateRequest(); 37 | 38 | const textData = new TextData(); 39 | textData.content = data.content; 40 | 41 | createRequest.data = textData; 42 | createRequest.position = position; 43 | 44 | if (geometry) { 45 | createRequest.geometry = geometry; 46 | } 47 | 48 | if (style) { 49 | createRequest.style = style; 50 | } 51 | 52 | const result = await MiroClient.getApi().createTextItem(boardId, createRequest); 53 | return ServerResponse.text(JSON.stringify(result, null, 2)); 54 | } catch (error) { 55 | return ServerResponse.error(error); 56 | } 57 | } 58 | } 59 | 60 | export default createTextItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateConnector.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 { ConnectorChangesData } from '@mirohq/miro-api/dist/model/connectorChangesData.js'; 7 | 8 | const updateConnectorTool: ToolSchema = { 9 | name: "update-connector", 10 | description: "Update an existing connector on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the connector"), 13 | connectorId: z.string().describe("Unique identifier (ID) of the connector that you want to update"), 14 | startItem: z.object({ 15 | id: z.string().describe("ID of the item at the start of the connector") 16 | }).optional().nullish().describe("Start item of the connector"), 17 | endItem: z.object({ 18 | id: z.string().describe("ID of the item at the end of the connector") 19 | }).optional().nullish().describe("End item of the connector"), 20 | style: z.object({ 21 | strokeColor: z.string().optional().nullish().describe("Updated color of the connector stroke"), 22 | strokeWidth: z.number().optional().nullish().describe("Updated width of the connector stroke"), 23 | strokeStyle: z.string().optional().nullish().describe("Updated style of the connector stroke (normal, dashed, etc.)"), 24 | startStrokeCap: z.string().optional().nullish().describe("Updated start stroke cap style"), 25 | endStrokeCap: z.string().optional().nullish().describe("Updated end stroke cap style") 26 | }).optional().nullish().describe("Updated style configuration of the connector") 27 | }, 28 | fn: async ({ boardId, connectorId, startItem, endItem, style }) => { 29 | try { 30 | if (!boardId) { 31 | return ServerResponse.error("Board ID is required"); 32 | } 33 | 34 | if (!connectorId) { 35 | return ServerResponse.error("Connector ID is required"); 36 | } 37 | 38 | const changes = new ConnectorChangesData(); 39 | 40 | if (startItem) { 41 | changes.startItem = startItem; 42 | } 43 | 44 | if (endItem) { 45 | changes.endItem = endItem; 46 | } 47 | 48 | if (style) { 49 | changes.style = style; 50 | } 51 | 52 | const result = await MiroClient.getApi().updateConnector(boardId, connectorId, changes); 53 | return ServerResponse.text(JSON.stringify(result, null, 2)); 54 | } catch (error) { 55 | return ServerResponse.error(error); 56 | } 57 | } 58 | } 59 | 60 | export default updateConnectorTool; ``` -------------------------------------------------------------------------------- /src/tools/updateImageItem.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 { ImageUpdateRequest } from '@mirohq/miro-api/dist/model/imageUpdateRequest.js'; 7 | 8 | const updateImageItemTool: ToolSchema = { 9 | name: "update-image-item", 10 | description: "Update an existing image item on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board where you want to update the item"), 13 | itemId: z.string().describe("Unique identifier (ID) of the image that you want to update"), 14 | data: z.object({ 15 | title: z.string().optional().nullish().describe("Updated title of the image") 16 | }).optional().nullish().describe("The updated content of the image"), 17 | position: z.object({ 18 | x: z.number().optional().nullish().describe("Updated X coordinate of the image"), 19 | y: z.number().optional().nullish().describe("Updated Y coordinate of the image"), 20 | origin: z.string().optional().nullish().describe("Updated origin of the image (center, top-left, etc.)"), 21 | relativeTo: z.string().optional().nullish().describe("Updated reference point (canvas_center, etc.)") 22 | }).optional().nullish().describe("Updated position of the image on the board"), 23 | geometry: z.object({ 24 | width: z.number().optional().nullish().describe("Updated width of the image"), 25 | height: z.number().optional().nullish().describe("Updated height of the image") 26 | }).optional().nullish().describe("Updated dimensions of the image") 27 | }, 28 | fn: async ({ boardId, itemId, data, position, geometry }) => { 29 | try { 30 | if (!boardId) { 31 | return ServerResponse.error("Board ID is required"); 32 | } 33 | 34 | if (!itemId) { 35 | return ServerResponse.error("Item ID is required"); 36 | } 37 | 38 | const updateRequest = new ImageUpdateRequest(); 39 | 40 | if (data) { 41 | updateRequest.data = {}; 42 | 43 | if (data.title !== undefined) { 44 | updateRequest.data.title = data.title; 45 | } 46 | } 47 | 48 | if (position) { 49 | updateRequest.position = position; 50 | } 51 | 52 | if (geometry) { 53 | updateRequest.geometry = geometry; 54 | } 55 | 56 | const result = await MiroClient.getApi().updateImageItemUsingUrl(boardId, itemId, updateRequest); 57 | return ServerResponse.text(JSON.stringify(result, null, 2)); 58 | } catch (error) { 59 | return ServerResponse.error(error); 60 | } 61 | } 62 | } 63 | 64 | export default updateImageItemTool; ``` -------------------------------------------------------------------------------- /src/tools/createImageItemUsingUrl.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 { ImageCreateRequest } from '@mirohq/miro-api/dist/model/imageCreateRequest.js'; 7 | import { ImageUrlData } from '@mirohq/miro-api/dist/model/imageUrlData.js'; 8 | 9 | const createImageItemUsingUrlTool: ToolSchema = { 10 | name: "create-image-item-using-url", 11 | description: "Create a new image item on a Miro board using a URL", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the image will be created"), 14 | data: z.object({ 15 | url: z.string().describe("URL of the image to be added to the board"), 16 | title: z.string().optional().nullish().describe("Title of the image") 17 | }).describe("The content and configuration of the image"), 18 | position: z.object({ 19 | x: z.number().describe("X coordinate of the image"), 20 | y: z.number().describe("Y coordinate of the image"), 21 | origin: z.string().optional().nullish().describe("Origin of the image (center, top-left, etc.)"), 22 | relativeTo: z.string().optional().nullish().describe("Reference point (canvas_center, etc.)") 23 | }).describe("Position of the image on the board"), 24 | geometry: z.object({ 25 | width: z.number().optional().nullish().describe("Width of the image"), 26 | height: z.number().optional().nullish().describe("Height of the image") 27 | }).optional().nullish().describe("Dimensions of the image") 28 | }, 29 | fn: async ({ boardId, data, position, geometry }) => { 30 | try { 31 | if (!boardId) { 32 | return ServerResponse.error("Board ID is required"); 33 | } 34 | 35 | const createRequest = new ImageCreateRequest(); 36 | 37 | const imageData = new ImageUrlData(); 38 | imageData.url = data.url; 39 | 40 | if (data.title !== undefined) { 41 | imageData.title = data.title; 42 | } 43 | 44 | createRequest.data = imageData; 45 | 46 | const completePosition = { 47 | ...position, 48 | origin: position.origin || "center", 49 | relativeTo: position.relativeTo || "canvas_center" 50 | }; 51 | 52 | createRequest.position = completePosition; 53 | 54 | if (geometry) { 55 | createRequest.geometry = geometry; 56 | } 57 | 58 | const result = await MiroClient.getApi().createImageItemUsingUrl(boardId, createRequest); 59 | return ServerResponse.text(JSON.stringify(result, null, 2)); 60 | } catch (error) { 61 | return ServerResponse.error(error); 62 | } 63 | } 64 | } 65 | 66 | export default createImageItemUsingUrlTool; ``` -------------------------------------------------------------------------------- /src/tools/createImageItemUsingFileFromDevice.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 | import { UploadFileFromDeviceData } from '@mirohq/miro-api/dist/model/uploadFileFromDeviceData.js'; 6 | import { RequestFile } from '@mirohq/miro-api/dist/model/models.js'; 7 | 8 | const createImageItemUsingFileFromDeviceTool: ToolSchema = { 9 | name: "create-image-item-using-file", 10 | description: "Create a new image item on a Miro board using file from device or from chat", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board where the image will be created"), 13 | imageData: z.string().describe("Base64 encoded image data from the chat"), 14 | position: z.object({ 15 | x: z.number().describe("X coordinate of the image"), 16 | y: z.number().describe("Y coordinate of the image"), 17 | origin: z.string().optional().nullish().describe("Origin of the image (center, top-left, etc.)"), 18 | relativeTo: z.string().optional().nullish().describe("Reference point (canvas_center, etc.)") 19 | }).describe("Position of the image on the board"), 20 | title: z.string().optional().nullish().describe("Title of the image") 21 | }, 22 | fn: async ({ boardId, imageData, position, title }) => { 23 | try { 24 | if (!boardId) { 25 | return ServerResponse.error("Board ID is required"); 26 | } 27 | 28 | if (!imageData) { 29 | return ServerResponse.error("Image data is required"); 30 | } 31 | 32 | let imageBuffer; 33 | try { 34 | const base64Data = imageData.replace(/^data:image\/\w+;base64,/, ''); 35 | imageBuffer = Buffer.from(base64Data, 'base64'); 36 | } catch (error) { 37 | return ServerResponse.error(`Error decoding Base64 image data: ${error.message}`); 38 | } 39 | 40 | try { 41 | const metadata = {}; 42 | 43 | if (position) { 44 | metadata['position'] = { 45 | ...position, 46 | origin: position.origin || 'center', 47 | relativeTo: position.relativeTo || 'canvas_center' 48 | }; 49 | } 50 | 51 | if (title) { 52 | metadata['title'] = title; 53 | } 54 | 55 | const result = await MiroClient.getApi().createImageItemUsingLocalFile(boardId, imageBuffer, metadata); 56 | return ServerResponse.text(JSON.stringify(result, null, 2)); 57 | } catch (error) { 58 | return ServerResponse.error(`Error uploading image to Miro: ${error.message}`); 59 | } 60 | } catch (error) { 61 | return ServerResponse.error(error); 62 | } 63 | } 64 | }; 65 | 66 | export default createImageItemUsingFileFromDeviceTool; ``` -------------------------------------------------------------------------------- /src/tools/updateEmbedItem.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 | const updateEmbedItemTool: ToolSchema = { 7 | name: "update-embed-item", 8 | description: "Update an existing embed item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the embed"), 11 | itemId: z.string().describe("Unique identifier (ID) of the embed that you want to update"), 12 | data: z.object({ 13 | mode: z.string().optional().nullish().describe("Updated mode of the embed (normal, inline, etc.)") 14 | }).optional().nullish().describe("The updated configuration of the embed"), 15 | position: z.object({ 16 | x: z.number().describe("Updated X coordinate of the embed"), 17 | y: z.number().describe("Updated Y coordinate of the embed"), 18 | origin: z.string().optional().nullish().describe("Origin of the embed (center, top-left, etc.)"), 19 | relativeTo: z.string().optional().nullish().describe("Reference point (canvas_center, etc.)") 20 | }).optional().nullish().describe("Updated position of the embed on the board"), 21 | geometry: z.object({ 22 | width: z.number().optional().nullish().describe("Updated width of the embed"), 23 | height: z.number().optional().nullish().describe("Updated height of the embed") 24 | }) 25 | .optional().nullish() 26 | .refine(data => !data || data.width !== undefined || data.height !== undefined, { 27 | message: "Either width or height must be provided if geometry is set" 28 | }) 29 | .refine(data => !data || !(data.width !== undefined && data.height !== undefined), { 30 | message: "Only one of width or height should be provided for items with fixed aspect ratio" 31 | }) 32 | .describe("Updated dimensions of the embed") 33 | }, 34 | fn: async ({ boardId, itemId, data, position, geometry }) => { 35 | try { 36 | if (!boardId) { 37 | return ServerResponse.error("Board ID is required"); 38 | } 39 | 40 | if (!itemId) { 41 | return ServerResponse.error("Item ID is required"); 42 | } 43 | 44 | const updateRequest: Record<string, any> = {}; 45 | 46 | if (data) { 47 | updateRequest.data = data; 48 | } 49 | 50 | if (position) { 51 | updateRequest.position = position; 52 | } 53 | 54 | if (geometry) { 55 | updateRequest.geometry = geometry; 56 | } 57 | 58 | const result = await MiroClient.getApi().updateEmbedItem(boardId, itemId, updateRequest); 59 | return ServerResponse.text(JSON.stringify(result, null, 2)); 60 | } catch (error) { 61 | return ServerResponse.error(error); 62 | } 63 | } 64 | } 65 | 66 | export default updateEmbedItemTool; ``` -------------------------------------------------------------------------------- /src/tools/createEmbedItem.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 { EmbedCreateRequest } from '@mirohq/miro-api/dist/model/embedCreateRequest.js'; 7 | import { EmbedUrlData } from '@mirohq/miro-api/dist/model/embedUrlData.js'; 8 | 9 | const createEmbedItemTool: ToolSchema = { 10 | name: "create-embed-item", 11 | description: "Create a new embed item on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the embed will be created"), 14 | data: z.object({ 15 | url: z.string().describe("URL to be embedded on the board"), 16 | mode: z.string().optional().nullish().describe("Mode of the embed (normal, inline, etc.)") 17 | }).describe("The content and configuration of the embed"), 18 | position: z.object({ 19 | x: z.number().describe("X coordinate of the embed"), 20 | y: z.number().describe("Y coordinate of the embed"), 21 | origin: z.string().optional().nullish().describe("Origin of the embed (center, top-left, etc.)"), 22 | relativeTo: z.string().optional().nullish().describe("Reference point (canvas_center, etc.)") 23 | }).describe("Position of the embed on the board"), 24 | geometry: z.object({ 25 | width: z.number().optional().nullish().describe("Width of the embed"), 26 | height: z.number().optional().nullish().describe("Height of the embed") 27 | }) 28 | .refine(data => data.width !== undefined || data.height !== undefined, { 29 | message: "Either width or height must be provided" 30 | }) 31 | .refine(data => !(data.width !== undefined && data.height !== undefined), { 32 | message: "Only one of width or height should be provided for items with fixed aspect ratio" 33 | }) 34 | .describe("Dimensions of the embed") 35 | }, 36 | fn: async ({ boardId, data, position, geometry }) => { 37 | try { 38 | if (!boardId) { 39 | return ServerResponse.error("Board ID is required"); 40 | } 41 | 42 | const createRequest = new EmbedCreateRequest(); 43 | 44 | const embedData = new EmbedUrlData(); 45 | embedData.url = data.url; 46 | 47 | if (data.mode !== undefined) { 48 | embedData.mode = data.mode; 49 | } 50 | 51 | createRequest.data = embedData; 52 | 53 | const completePosition = { 54 | ...position, 55 | origin: position.origin || "center", 56 | relativeTo: position.relativeTo || "canvas_center" 57 | }; 58 | 59 | createRequest.position = completePosition; 60 | createRequest.geometry = geometry; 61 | 62 | const result = await MiroClient.getApi().createEmbedItem(boardId, createRequest); 63 | return ServerResponse.text(JSON.stringify(result, null, 2)); 64 | } catch (error) { 65 | return ServerResponse.error(error); 66 | } 67 | } 68 | } 69 | 70 | export default createEmbedItemTool; ``` -------------------------------------------------------------------------------- /src/tools/createCardItem.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 { CardCreateRequest } from '@mirohq/miro-api/dist/model/cardCreateRequest.js'; 7 | import { CardData } from '@mirohq/miro-api/dist/model/cardData.js'; 8 | 9 | const createCardItemTool: ToolSchema = { 10 | name: "create-card-item", 11 | description: "Create a new card item on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the card will be created"), 14 | data: z.object({ 15 | title: z.string().describe("Title of the card"), 16 | description: z.string().optional().nullish().describe("Description of the card"), 17 | assigneeId: z.string().optional().nullish().describe("User ID of the assignee"), 18 | dueDate: z.string().optional().nullish().describe("Due date for the card (ISO 8601 format)") 19 | }).describe("The content and configuration of the card"), 20 | position: z.object({ 21 | x: z.number().describe("X coordinate of the card"), 22 | y: z.number().describe("Y coordinate of the card") 23 | }).describe("Position of the card on the board"), 24 | geometry: z.object({ 25 | width: z.number().optional().nullish().describe("Width of the card"), 26 | height: z.number().optional().nullish().describe("Height of the card"), 27 | rotation: z.number().optional().nullish().describe("Rotation angle of the card") 28 | }).optional().nullish().describe("Dimensions of the card"), 29 | style: z.object({ 30 | cardTheme: z.string().optional().nullish().describe("Color of the card") 31 | }).optional().nullish().describe("Style configuration of the card") 32 | }, 33 | fn: async ({ boardId, data, position, geometry, style }) => { 34 | try { 35 | if (!boardId) { 36 | return ServerResponse.error("Board ID is required"); 37 | } 38 | 39 | const createRequest = new CardCreateRequest(); 40 | 41 | const cardData = new CardData(); 42 | cardData.title = data.title; 43 | 44 | if (data.description !== undefined) cardData.description = data.description; 45 | if (data.assigneeId !== undefined) cardData.assigneeId = data.assigneeId; 46 | 47 | if (data.dueDate !== undefined) { 48 | cardData.dueDate = new Date(data.dueDate); 49 | } 50 | 51 | createRequest.data = cardData; 52 | createRequest.position = position; 53 | 54 | if (geometry) { 55 | createRequest.geometry = geometry; 56 | } 57 | 58 | if (style) { 59 | createRequest.style = style; 60 | } 61 | 62 | const result = await MiroClient.getApi().createCardItem(boardId, createRequest); 63 | return ServerResponse.text(JSON.stringify(result, null, 2)); 64 | } catch (error) { 65 | return ServerResponse.error(error); 66 | } 67 | } 68 | } 69 | 70 | export default createCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateDocumentItem.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 | import { DocumentUpdateRequest } from '@mirohq/miro-api/dist/model/documentUpdateRequest.js'; 6 | import { DocumentUrlData } from '@mirohq/miro-api/dist/model/documentUrlData.js'; 7 | 8 | const updateDocumentItemTool: ToolSchema = { 9 | name: "update-document-item", 10 | description: "Update an existing document item on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the document"), 13 | itemId: z.string().describe("Unique identifier (ID) of the document that you want to update"), 14 | data: z.object({ 15 | url: z.string().optional().nullish().describe("Updated URL of the document"), 16 | title: z.string().optional().nullish().describe("Updated title of the document") 17 | }).optional().nullish().describe("The updated content and configuration of the document"), 18 | position: z.object({ 19 | x: z.number().optional().nullish().describe("Updated X coordinate of the document"), 20 | y: z.number().optional().nullish().describe("Updated Y coordinate of the document") 21 | }).optional().nullish().describe("Updated position of the document on the board"), 22 | geometry: z.object({ 23 | width: z.number().optional().nullish().describe("Updated width of the document"), 24 | height: z.number().optional().nullish().describe("Updated height of the document") 25 | }).optional().nullish().describe("Updated dimensions of the document") 26 | }, 27 | fn: async ({ boardId, itemId, data, position, geometry }) => { 28 | try { 29 | if (!boardId) { 30 | return ServerResponse.error("Board ID is required"); 31 | } 32 | 33 | if (!itemId) { 34 | return ServerResponse.error("Item ID is required"); 35 | } 36 | 37 | const updateRequest = new DocumentUpdateRequest(); 38 | 39 | if (data) { 40 | const documentData = new DocumentUrlData(); 41 | 42 | if (data.url !== undefined) documentData.url = data.url; 43 | if (data.title !== undefined) documentData.title = data.title; 44 | 45 | if (Object.keys(documentData).length > 0) { 46 | updateRequest.data = documentData; 47 | } 48 | } 49 | 50 | if (position) { 51 | updateRequest.position = position; 52 | } 53 | 54 | if (geometry) { 55 | updateRequest.geometry = geometry; 56 | } 57 | 58 | if (Object.keys(updateRequest).length === 0) { 59 | return ServerResponse.error("No data provided for update"); 60 | } 61 | 62 | const result = await MiroClient.getApi().updateDocumentItemUsingUrl(boardId, itemId, updateRequest); 63 | return ServerResponse.text(JSON.stringify(result, null, 2)); 64 | } catch (error) { 65 | return ServerResponse.error(error); 66 | } 67 | } 68 | } 69 | 70 | export default updateDocumentItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateTextItem.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 | import { TextUpdateRequest } from '@mirohq/miro-api/dist/model/textUpdateRequest.js'; 6 | import { TextData } from '@mirohq/miro-api/dist/model/textData.js'; 7 | 8 | const updateTextItemTool: ToolSchema = { 9 | name: "update-text-item", 10 | description: "Update an existing text item on a Miro board", 11 | args: { 12 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the text item"), 13 | itemId: z.string().describe("Unique identifier (ID) of the text item that you want to update"), 14 | data: z.object({ 15 | content: z.string().optional().nullish().describe("Updated text content of the text item") 16 | }).optional().nullish().describe("The updated content of the text item"), 17 | position: z.object({ 18 | x: z.number().optional().nullish().describe("Updated X coordinate of the text item"), 19 | y: z.number().optional().nullish().describe("Updated Y coordinate of the text item") 20 | }).optional().nullish().describe("Updated position of the text item on the board"), 21 | geometry: z.object({ 22 | width: z.number().optional().nullish().describe("Updated width of the text item") 23 | }).optional().nullish().describe("Updated dimensions of the text item"), 24 | style: z.object({ 25 | color: z.string().optional().nullish().describe("Updated color of the text"), 26 | fontSize: z.number().optional().nullish().describe("Updated font size of the text"), 27 | textAlign: z.string().optional().nullish().describe("Updated alignment of the text (left, center, right)") 28 | }).optional().nullish().describe("Updated style configuration of the text item") 29 | }, 30 | fn: async ({ boardId, itemId, data, position, geometry, style }) => { 31 | try { 32 | if (!boardId) { 33 | return ServerResponse.error("Board ID is required"); 34 | } 35 | 36 | if (!itemId) { 37 | return ServerResponse.error("Item ID is required"); 38 | } 39 | 40 | const updateRequest = new TextUpdateRequest(); 41 | 42 | if (data) { 43 | const textData = new TextData(); 44 | 45 | if (data.content !== undefined) textData.content = data.content; 46 | 47 | if (Object.keys(textData).length > 0) { 48 | updateRequest.data = textData; 49 | } 50 | } 51 | 52 | if (position) { 53 | updateRequest.position = position; 54 | } 55 | 56 | if (geometry) { 57 | updateRequest.geometry = geometry; 58 | } 59 | 60 | if (style) { 61 | updateRequest.style = style; 62 | } 63 | 64 | if (Object.keys(updateRequest).length === 0) { 65 | return ServerResponse.error("No data provided for update"); 66 | } 67 | 68 | const result = await MiroClient.getApi().updateTextItem(boardId, itemId, updateRequest); 69 | return ServerResponse.text(JSON.stringify(result, null, 2)); 70 | } catch (error) { 71 | return ServerResponse.error(error); 72 | } 73 | } 74 | } 75 | 76 | export default updateTextItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateImageItemUsingFileFromDevice.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 | import { UploadFileFromDeviceData } from '@mirohq/miro-api/dist/model/uploadFileFromDeviceData.js'; 6 | import { RequestFile } from '@mirohq/miro-api/dist/model/models.js'; 7 | import * as fs from 'fs'; 8 | import FormData from 'form-data'; 9 | 10 | const updateImageItemUsingFileFromDeviceTool: ToolSchema = { 11 | name: "update-image-item-using-file", 12 | description: "Update an existing image item on a Miro board using file from device", 13 | args: { 14 | boardId: z.string().describe("Unique identifier (ID) of the board where you want to update the item"), 15 | itemId: z.string().describe("Unique identifier (ID) of the image that you want to update"), 16 | filePath: z.string().describe("Path to the new image file on the device"), 17 | title: z.string().optional().nullish().describe("Updated title of the image"), 18 | position: z.object({ 19 | x: z.number().optional().nullish().describe("Updated X coordinate of the image"), 20 | y: z.number().optional().nullish().describe("Updated Y coordinate of the image"), 21 | origin: z.string().optional().nullish().describe("Updated origin of the image (center, top-left, etc.)"), 22 | relativeTo: z.string().optional().nullish().describe("Updated reference point (canvas_center, etc.)") 23 | }).optional().nullish().describe("Updated position of the image on the board") 24 | }, 25 | fn: async ({ boardId, itemId, filePath, title, position }) => { 26 | try { 27 | if (!boardId) { 28 | return ServerResponse.error("Board ID is required"); 29 | } 30 | 31 | if (!itemId) { 32 | return ServerResponse.error("Item ID is required"); 33 | } 34 | 35 | if (!filePath) { 36 | return ServerResponse.error("File path is required"); 37 | } 38 | 39 | // Implementacja aktualizacji obrazu z pliku lokalnego nie jest możliwa bezpośrednio w MCP 40 | // ponieważ wymaga dostępu do systemu plików klienta oraz konstruktora FormData 41 | 42 | const instructionsForUser = { 43 | message: "Aktualizacja obrazu z pliku lokalnego nie jest obecnie obsługiwana bezpośrednio w MCP Miro.", 44 | alternatives: [ 45 | "1. Użyj funkcji 'update-image-item' z URL do obrazu.", 46 | "2. Alternatywnie, możesz użyć bezpośrednio API Miro przez SDK lub żądania HTTP:", 47 | " - Utwórz formularz FormData", 48 | " - Dodaj plik do formularza", 49 | " - Wyślij żądanie PATCH do https://api.miro.com/v2/boards/{boardId}/images/{itemId}", 50 | " - Dodaj nagłówek 'Content-Type: multipart/form-data' oraz token autoryzacyjny" 51 | ], 52 | documentation: "Więcej informacji znajdziesz w dokumentacji API Miro: https://developers.miro.com/reference/update-image-item-using-file-from-device" 53 | }; 54 | 55 | return ServerResponse.text(JSON.stringify(instructionsForUser, null, 2)); 56 | } catch (error) { 57 | return ServerResponse.error(error); 58 | } 59 | } 60 | } 61 | 62 | export default updateImageItemUsingFileFromDeviceTool; ``` -------------------------------------------------------------------------------- /src/tools/createFrameItem.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 { FrameCreateRequest } from '@mirohq/miro-api/dist/model/frameCreateRequest.js'; 7 | import { FrameChanges } from '@mirohq/miro-api/dist/model/frameChanges.js'; 8 | 9 | const createFrameItemTool: ToolSchema = { 10 | name: "create-frame", 11 | description: "Create a new frame on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the frame will be created"), 14 | data: z.object({ 15 | title: z.string().optional().nullish().describe("Title of the frame. This title appears at the top of the frame."), 16 | format: z.string().optional().nullish().describe("Format of the frame. Only 'custom' is supported currently."), 17 | type: z.string().optional().nullish().describe("Type of the frame. Only 'freeform' is supported currently."), 18 | showContent: z.boolean().optional().nullish().describe("Hide or reveal the content inside a frame (Enterprise plan only).") 19 | }).describe("The content and configuration of the frame"), 20 | position: z.object({ 21 | x: z.number().describe("X coordinate of the frame"), 22 | y: z.number().describe("Y coordinate of the frame") 23 | }).describe("Position of the frame on the board"), 24 | geometry: z.object({ 25 | width: z.number().optional().nullish().describe("Width of the frame"), 26 | height: z.number().optional().nullish().describe("Height of the frame") 27 | }).optional().nullish().describe("Dimensions of the frame"), 28 | style: z.object({ 29 | fillColor: z.string().optional().nullish().describe("Fill color for the frame. Hex values like #f5f6f8, #d5f692, etc.") 30 | }).optional().nullish().describe("Style configuration of the frame") 31 | }, 32 | fn: async ({ boardId, data, position, geometry, style }: { 33 | boardId: string, 34 | data: { 35 | title?: string, 36 | format?: string, 37 | type?: string, 38 | showContent?: boolean 39 | }, 40 | position: { 41 | x: number, 42 | y: number 43 | }, 44 | geometry?: { 45 | width?: number, 46 | height?: number 47 | }, 48 | style?: { 49 | fillColor?: string 50 | } 51 | }) => { 52 | try { 53 | if (!boardId) { 54 | return ServerResponse.error("Board ID is required"); 55 | } 56 | 57 | const createRequest = new FrameCreateRequest(); 58 | 59 | const frameData = new FrameChanges(); 60 | 61 | if (data.title !== undefined) frameData.title = data.title; 62 | if (data.format !== undefined) frameData.format = data.format; 63 | if (data.type !== undefined) frameData.type = data.type; 64 | if (data.showContent !== undefined) frameData.showContent = data.showContent; 65 | 66 | createRequest.data = frameData; 67 | createRequest.position = position; 68 | 69 | if (geometry) { 70 | createRequest.geometry = geometry; 71 | } 72 | 73 | if (style) { 74 | createRequest.style = style; 75 | } 76 | 77 | const result = await MiroClient.getApi().createFrameItem(boardId, createRequest); 78 | return ServerResponse.text(JSON.stringify(result, null, 2)); 79 | } catch (error) { 80 | return ServerResponse.error(error); 81 | } 82 | } 83 | } 84 | 85 | export default createFrameItemTool; ``` -------------------------------------------------------------------------------- /src/tools/createAppCardItem.ts: -------------------------------------------------------------------------------- ```typescript 1 | import MiroClient from '../client.js'; 2 | import { z } from 'zod'; 3 | import { ServerResponse } from '../server-response.js'; 4 | import { AppCardCreateRequest } from '@mirohq/miro-api/dist/model/appCardCreateRequest.js'; 5 | import { AppCardDataChanges } from '@mirohq/miro-api/dist/model/appCardDataChanges.js'; 6 | import { CustomField } from '@mirohq/miro-api/dist/model/customField.js'; 7 | import { ToolSchema } from '../tool.js'; 8 | 9 | const createAppCardItemTool: ToolSchema = { 10 | name: "create-app-card-item", 11 | description: "Create a new app card item on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where the app card will be created"), 14 | data: z.object({ 15 | title: z.string().describe("Title of the app card"), 16 | description: z.string().optional().nullish().describe("Description of the app card"), 17 | status: z.string().optional().nullish().describe("Status text of the app card"), 18 | fields: z.array(z.object({ 19 | value: z.string().describe("Value of the field"), 20 | iconShape: z.string().optional().nullish().describe("Shape of the icon"), 21 | fillColor: z.string().optional().nullish().describe("Fill color of the field"), 22 | textColor: z.string().optional().nullish().describe("Color of the text"), 23 | })).optional().nullish().describe("Custom fields to display on the app card") 24 | }).describe("The content and configuration of the app card"), 25 | position: z.object({ 26 | x: z.number().describe("X coordinate of the app card"), 27 | y: z.number().describe("Y coordinate of the app card") 28 | }).describe("Position of the app card on the board"), 29 | geometry: z.object({ 30 | width: z.number().optional().nullish().describe("Width of the app card"), 31 | height: z.number().optional().nullish().describe("Height of the app card") 32 | }).optional().nullish().describe("Dimensions of the app card") 33 | }, 34 | fn: async ({boardId, data, position, geometry}) => { 35 | try { 36 | if (!boardId) { 37 | return ServerResponse.error("Board ID is required"); 38 | } 39 | 40 | const createRequest = new AppCardCreateRequest(); 41 | 42 | const appCardData = new AppCardDataChanges(); 43 | appCardData.title = data.title; 44 | 45 | if (data.description !== undefined) appCardData.description = data.description; 46 | if (data.status !== undefined) appCardData.status = data.status; 47 | 48 | if (data.fields) { 49 | appCardData.fields = data.fields.map(field => { 50 | const customField = new CustomField(); 51 | customField.value = field.value; 52 | if (field.iconShape) customField.iconShape = field.iconShape; 53 | if (field.fillColor) customField.fillColor = field.fillColor; 54 | if (field.textColor) customField.textColor = field.textColor; 55 | return customField; 56 | }); 57 | } 58 | 59 | createRequest.data = appCardData; 60 | createRequest.position = position; 61 | 62 | if (geometry) { 63 | createRequest.geometry = geometry; 64 | } 65 | 66 | const result = await MiroClient.getApi().createAppCardItem(boardId, createRequest); 67 | return ServerResponse.text(JSON.stringify(result, null, 2)); 68 | } catch (error) { 69 | return ServerResponse.error(error); 70 | } 71 | } 72 | } 73 | 74 | export default createAppCardItemTool; 75 | ``` -------------------------------------------------------------------------------- /src/tools/updateCardItem.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 { CardUpdateRequest } from '@mirohq/miro-api/dist/model/cardUpdateRequest.js'; 7 | import { CardData } from '@mirohq/miro-api/dist/model/cardData.js'; 8 | 9 | const updateCardItemTool: ToolSchema = { 10 | name: "update-card-item", 11 | description: "Update an existing card item on a Miro board", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the card"), 14 | itemId: z.string().describe("Unique identifier (ID) of the card that you want to update"), 15 | data: z.object({ 16 | title: z.string().optional().nullish().describe("Updated title of the card"), 17 | description: z.string().optional().nullish().describe("Updated description of the card"), 18 | assigneeId: z.string().optional().nullish().describe("Updated user ID of the assignee"), 19 | dueDate: z.string().optional().nullish().describe("Updated due date for the card (ISO 8601 format)") 20 | }).optional().nullish().describe("The updated content and configuration of the card"), 21 | position: z.object({ 22 | x: z.number().describe("Updated X coordinate of the card"), 23 | y: z.number().describe("Updated Y coordinate of the card") 24 | }).optional().nullish().describe("Updated position of the card on the board"), 25 | geometry: z.object({ 26 | width: z.number().optional().nullish().describe("Updated width of the card"), 27 | height: z.number().optional().nullish().describe("Updated height of the card"), 28 | rotation: z.number().optional().nullish().describe("Updated rotation angle of the card") 29 | }).optional().nullish().describe("Updated dimensions of the card"), 30 | style: z.object({ 31 | cardTheme: z.string().optional().nullish().describe("Updated color of the card") 32 | }).optional().nullish().describe("Updated style configuration of the card") 33 | }, 34 | fn: async ({ boardId, itemId, data, position, geometry, style }) => { 35 | try { 36 | if (!boardId) { 37 | return ServerResponse.error("Board ID is required"); 38 | } 39 | 40 | if (!itemId) { 41 | return ServerResponse.error("Item ID is required"); 42 | } 43 | 44 | const updateData = new CardUpdateRequest(); 45 | 46 | if (data) { 47 | const cardData = new CardData(); 48 | 49 | if (data.title !== undefined) cardData.title = data.title; 50 | if (data.description !== undefined) cardData.description = data.description; 51 | if (data.assigneeId !== undefined) cardData.assigneeId = data.assigneeId; 52 | 53 | if (data.dueDate !== undefined) { 54 | cardData.dueDate = new Date(data.dueDate); 55 | } 56 | 57 | updateData.data = cardData; 58 | } 59 | 60 | if (position) { 61 | updateData.position = position; 62 | } 63 | 64 | if (geometry) { 65 | updateData.geometry = geometry; 66 | } 67 | 68 | if (style) { 69 | updateData.style = style; 70 | } 71 | 72 | const result = await MiroClient.getApi().updateCardItem(boardId, itemId, updateData); 73 | 74 | return ServerResponse.text(JSON.stringify(result, null, 2)); 75 | } catch (error) { 76 | return ServerResponse.error(error); 77 | } 78 | } 79 | } 80 | 81 | export default updateCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateShapeItem.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 | const updateShapeItemTool: ToolSchema = { 7 | name: "update-shape-item", 8 | description: "Update an existing shape item on a Miro board", 9 | args: { 10 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the shape"), 11 | itemId: z.string().describe("Unique identifier (ID) of the shape that you want to update"), 12 | data: z.object({ 13 | shape: z.string().optional().nullish().describe("Updated type of the shape (rectangle, circle, triangle, etc.)"), 14 | content: z.string().optional().nullish().describe("Updated text content to display inside the shape") 15 | }).optional().nullish().describe("The updated content and configuration of the shape"), 16 | position: z.object({ 17 | x: z.number().describe("Updated X coordinate of the shape"), 18 | y: z.number().describe("Updated Y coordinate of the shape") 19 | }).optional().nullish().describe("Updated position of the shape on the board"), 20 | geometry: z.object({ 21 | width: z.number().optional().nullish().describe("Updated width of the shape"), 22 | height: z.number().optional().nullish().describe("Updated height of the shape"), 23 | rotation: z.number().optional().nullish().describe("Rotation angle of the shape") 24 | }).optional().nullish().describe("Updated dimensions of the shape"), 25 | style: z.object({ 26 | borderColor: z.string().optional().nullish().describe("Updated color of the shape border (hex format, e.g. #000000)"), 27 | borderWidth: z.number().optional().nullish().describe("Updated width of the shape border"), 28 | borderStyle: z.string().optional().nullish().describe("Updated style of the shape border (normal, dashed, etc.)"), 29 | borderOpacity: z.number().optional().nullish().describe("Updated opacity of the shape border (0-1)"), 30 | fillColor: z.string().optional().nullish().describe("Updated fill color of the shape (hex format, e.g. #000000)"), 31 | fillOpacity: z.number().optional().nullish().describe("Updated opacity of the shape fill (0-1)"), 32 | color: z.string().optional().nullish().describe("Updated color of the text in the shape (hex format, e.g. #000000)") 33 | }).optional().nullish().describe("Updated style configuration of the shape") 34 | }, 35 | fn: async ({ boardId, itemId, data, position, geometry, style }) => { 36 | try { 37 | if (!boardId) { 38 | return ServerResponse.error("Board ID is required"); 39 | } 40 | 41 | if (!itemId) { 42 | return ServerResponse.error("Item ID is required"); 43 | } 44 | 45 | const updateRequest: Record<string, any> = {}; 46 | 47 | if (data) { 48 | updateRequest.data = data; 49 | } 50 | 51 | if (position) { 52 | updateRequest.position = position; 53 | } 54 | 55 | if (geometry) { 56 | updateRequest.geometry = geometry; 57 | } 58 | 59 | if (style) { 60 | updateRequest.style = style; 61 | } 62 | 63 | const result = await MiroClient.getApi().updateShapeItem(boardId, itemId, updateRequest); 64 | return ServerResponse.text(JSON.stringify(result, null, 2)); 65 | } catch (error) { 66 | return ServerResponse.error(error); 67 | } 68 | } 69 | } 70 | 71 | export default updateShapeItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateFrameItem.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 { FrameUpdateRequest } from '@mirohq/miro-api/dist/model/frameUpdateRequest.js'; 7 | import { FrameChanges } from '@mirohq/miro-api/dist/model/frameChanges.js'; 8 | 9 | const updateFrameItemTool: ToolSchema = { 10 | name: "update-frame-item", 11 | description: "Update a frame on a Miro board based on the data, style, or geometry properties provided in the request body", 12 | args: { 13 | boardId: z.string().describe("Unique identifier (ID) of the board where you want to update the frame"), 14 | itemId: z.string().describe("Unique identifier (ID) of the frame that you want to update"), 15 | data: z.object({ 16 | title: z.string().optional().nullish().describe("Title of the frame. This title appears at the top of the frame."), 17 | format: z.string().optional().nullish().describe("Format of the frame. Only 'custom' is supported currently."), 18 | type: z.string().optional().nullish().describe("Type of the frame. Only 'freeform' is supported currently."), 19 | showContent: z.boolean().optional().nullish().describe("Hide or reveal the content inside a frame (Enterprise plan only).") 20 | }).optional().nullish().describe("The updated content and configuration of the frame"), 21 | position: z.object({ 22 | x: z.number().describe("X coordinate of the frame"), 23 | y: z.number().describe("Y coordinate of the frame") 24 | }).optional().nullish().describe("Updated position of the frame on the board"), 25 | geometry: z.object({ 26 | width: z.number().optional().nullish().describe("Width of the frame"), 27 | height: z.number().optional().nullish().describe("Height of the frame") 28 | }).optional().nullish().describe("Updated dimensions of the frame"), 29 | style: z.object({ 30 | fillColor: z.string().optional().nullish().describe("Fill color for the frame. Hex values like #f5f6f8, #d5f692, etc.") 31 | }).optional().nullish().describe("Updated style configuration of the frame") 32 | }, 33 | fn: async ({ boardId, itemId, data, position, geometry, style }: { 34 | boardId: string, 35 | itemId: string, 36 | data?: { 37 | title?: string, 38 | format?: string, 39 | type?: string, 40 | showContent?: boolean 41 | }, 42 | position?: { 43 | x: number, 44 | y: number 45 | }, 46 | geometry?: { 47 | width?: number, 48 | height?: number 49 | }, 50 | style?: { 51 | fillColor?: string 52 | } 53 | }) => { 54 | try { 55 | if (!boardId) { 56 | return ServerResponse.error("Board ID is required"); 57 | } 58 | 59 | if (!itemId) { 60 | return ServerResponse.error("Item ID is required"); 61 | } 62 | 63 | const updateRequest = new FrameUpdateRequest(); 64 | 65 | if (data) { 66 | const frameData = new FrameChanges(); 67 | 68 | if (data.title !== undefined) frameData.title = data.title; 69 | if (data.format !== undefined) frameData.format = data.format; 70 | if (data.type !== undefined) frameData.type = data.type; 71 | if (data.showContent !== undefined) frameData.showContent = data.showContent; 72 | 73 | updateRequest.data = frameData; 74 | } 75 | 76 | if (position) { 77 | updateRequest.position = position; 78 | } 79 | 80 | if (geometry) { 81 | updateRequest.geometry = geometry; 82 | } 83 | 84 | if (style) { 85 | updateRequest.style = style; 86 | } 87 | 88 | const result = await MiroClient.getApi().updateFrameItem(boardId, itemId, updateRequest); 89 | return ServerResponse.text(JSON.stringify(result, null, 2)); 90 | } catch (error) { 91 | return ServerResponse.error(error); 92 | } 93 | } 94 | } 95 | 96 | export default updateFrameItemTool; ``` -------------------------------------------------------------------------------- /src/tools/updateAppCardItem.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 { AppCardUpdateRequest } from '@mirohq/miro-api/dist/model/appCardUpdateRequest.js'; 7 | import { AppCardDataChanges } from '@mirohq/miro-api/dist/model/appCardDataChanges.js'; 8 | import { CustomField } from '@mirohq/miro-api/dist/model/customField.js'; 9 | 10 | const updateAppCardItemTool: ToolSchema = { 11 | name: "update-app-card-item", 12 | description: "Update an existing app card item on a Miro board", 13 | args: { 14 | boardId: z.string().describe("Unique identifier (ID) of the board that contains the app card"), 15 | itemId: z.string().describe("Unique identifier (ID) of the app card that you want to update"), 16 | data: z.object({ 17 | title: z.string().optional().nullish().describe("Updated title of the app card"), 18 | description: z.string().optional().nullish().describe("Updated description of the app card"), 19 | status: z.string().optional().nullish().describe("Updated status text of the app card"), 20 | fields: z.array(z.object({ 21 | value: z.string().describe("Value of the field"), 22 | iconShape: z.string().optional().nullish().describe("Shape of the icon"), 23 | fillColor: z.string().optional().nullish().describe("Fill color of the field"), 24 | textColor: z.string().optional().nullish().describe("Color of the text"), 25 | })).optional().nullish().describe("Updated custom fields to display on the app card") 26 | }).optional().nullish().describe("The updated content and configuration of the app card"), 27 | position: z.object({ 28 | x: z.number().describe("Updated X coordinate of the app card"), 29 | y: z.number().describe("Updated Y coordinate of the app card") 30 | }).optional().nullish().describe("Updated position of the app card on the board"), 31 | geometry: z.object({ 32 | width: z.number().optional().nullish().describe("Updated width of the app card"), 33 | height: z.number().optional().nullish().describe("Updated height of the app card") 34 | }).optional().nullish().describe("Updated dimensions of the app card") 35 | }, 36 | fn: async ({ boardId, itemId, data, position, geometry }) => { 37 | try { 38 | if (!boardId) { 39 | return ServerResponse.error("Board ID is required"); 40 | } 41 | 42 | if (!itemId) { 43 | return ServerResponse.error("Item ID is required"); 44 | } 45 | 46 | const updateRequest = new AppCardUpdateRequest(); 47 | 48 | if (data) { 49 | const appCardData = new AppCardDataChanges(); 50 | 51 | if (data.title !== undefined) appCardData.title = data.title; 52 | if (data.description !== undefined) appCardData.description = data.description; 53 | if (data.status !== undefined) appCardData.status = data.status; 54 | 55 | if (data.fields) { 56 | appCardData.fields = data.fields.map(field => { 57 | const customField = new CustomField(); 58 | customField.value = field.value; 59 | if (field.iconShape) customField.iconShape = field.iconShape; 60 | if (field.fillColor) customField.fillColor = field.fillColor; 61 | if (field.textColor) customField.textColor = field.textColor; 62 | return customField; 63 | }); 64 | } 65 | 66 | updateRequest.data = appCardData; 67 | } 68 | 69 | if (position) { 70 | updateRequest.position = position; 71 | } 72 | 73 | if (geometry) { 74 | updateRequest.geometry = geometry; 75 | } 76 | 77 | const result = await MiroClient.getApi().updateAppCardItem(boardId, itemId, updateRequest); 78 | 79 | return ServerResponse.text(JSON.stringify(result, null, 2)); 80 | } catch (error) { 81 | return ServerResponse.error(error); 82 | } 83 | } 84 | } 85 | 86 | export default updateAppCardItemTool; ``` -------------------------------------------------------------------------------- /src/tools/createShapeItem.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 { ShapeCreateRequest } from '@mirohq/miro-api/dist/model/shapeCreateRequest.js'; 7 | import { ShapeData } from '@mirohq/miro-api/dist/model/shapeData.js'; 8 | 9 | const validShapeTypes = ['rectangle', 'round_rectangle', 'circle', 'triangle', 'rhombus', 'parallelogram', 'trapezoid', 'pentagon', 'hexagon', 'octagon', 'wedge_round_rectangle_callout', 'star', 'flow_chart_predefined_process', 'cloud', 'cross', 'can', 'right_arrow', 'left_arrow', 'left_right_arrow', 'left_brace', 'right_brace']; 10 | 11 | const createShapeItemTool: ToolSchema = { 12 | name: "create-shape-item", 13 | description: "Create a new shape item on a Miro board", 14 | args: { 15 | boardId: z.string().describe("Unique identifier (ID) of the board where the shape will be created"), 16 | data: z.object({ 17 | shape: z.string().describe("Type of the shape (rectangle, circle, triangle, etc.)"), 18 | content: z.string().optional().nullish().describe("Text content to display inside the shape") 19 | }).describe("The content and configuration of the shape"), 20 | position: z.object({ 21 | x: z.number().describe("X coordinate of the shape"), 22 | y: z.number().describe("Y coordinate of the shape") 23 | }).describe("Position of the shape on the board"), 24 | geometry: z.object({ 25 | width: z.number().describe("Width of the shape"), 26 | height: z.number().describe("Height of the shape"), 27 | rotation: z.number().optional().nullish().describe("Rotation angle of the shape") 28 | }).describe("Dimensions of the shape"), 29 | style: z.object({ 30 | borderColor: z.string().optional().nullish().describe("Color of the shape border (hex format, e.g. #000000)"), 31 | borderWidth: z.number().optional().nullish().describe("Width of the shape border"), 32 | borderStyle: z.string().optional().nullish().describe("Style of the shape border (normal, dashed, etc.)"), 33 | borderOpacity: z.number().optional().nullish().describe("Opacity of the shape border (0-1)"), 34 | fillColor: z.string().optional().nullish().describe("Fill color of the shape (hex format, e.g. #000000)"), 35 | fillOpacity: z.number().optional().nullish().describe("Opacity of the shape fill (0-1)"), 36 | color: z.string().optional().nullish().describe("Color of the text in the shape (hex format, e.g. #000000)") 37 | }).optional().nullish().describe("Style configuration of the shape") 38 | }, 39 | fn: async ({ boardId, data, position, geometry, style }) => { 40 | try { 41 | if (!boardId) { 42 | return ServerResponse.error("Board ID is required"); 43 | } 44 | 45 | if (!validShapeTypes.includes(data.type)) { 46 | return ServerResponse.error("Invalid shape type. Valid types are: " + validShapeTypes.join(", ")); 47 | } 48 | 49 | const createRequest = new ShapeCreateRequest(); 50 | 51 | const shapeData = new ShapeData(); 52 | // Set shape type via property assignment 53 | (shapeData as any).type = data.type; 54 | 55 | if (data.content !== undefined) { 56 | shapeData.content = data.content; 57 | } 58 | 59 | createRequest.data = shapeData; 60 | 61 | const completePosition = { 62 | ...position, 63 | origin: position.origin || "center", 64 | relativeTo: position.relativeTo || "canvas_center" 65 | }; 66 | 67 | createRequest.position = completePosition; 68 | createRequest.geometry = geometry; 69 | 70 | if (style) { 71 | createRequest.style = style as Record<string, any>; 72 | } 73 | 74 | const result = await MiroClient.getApi().createShapeItem(boardId, createRequest); 75 | return ServerResponse.text(JSON.stringify(result, null, 2)); 76 | } catch (error) { 77 | return ServerResponse.error(error); 78 | } 79 | } 80 | } 81 | 82 | export default createShapeItemTool; ```