#
tokens: 2166/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── Dockerfile
├── docs
│   └── mcp_demo.png
├── package.json
├── README.md
├── smithery.yaml
├── src
│   └── index.ts
├── tsconfig.json
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules
2 | dist
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Whimsical MCP Server
 2 | [![smithery badge](https://smithery.ai/badge/@BrockReece/whimsical-mcp-server)](https://smithery.ai/server/@BrockReece/whimsical-mcp-server)
 3 | 
 4 | A Model Context Protocol (MCP) server that enables the creation of Whimsical diagrams programmatically. This server integrates with Whimsical's API to generate diagrams from Mermaid markup.
 5 | 
 6 | ## Demo
 7 | 
 8 | Here's an example of a complex system architecture diagram created using this MCP server and Claude - it shows the Model Context Protocol (MCP) architecture itself:
 9 | 
10 | ![MCP Architecture](./docs/mcp_demo.png)
11 | 
12 | 
13 | ## Features
14 | 
15 | - Create Whimsical diagrams using Mermaid markup generated by the MCP Client (Claude, Windsurf, etc.)
16 | - Returns both the Whimsical diagram URL and a base64 encoded image to allow the Client to iterate on it's original markup
17 | 
18 | ## Installation
19 | 
20 | ### Installing via Smithery
21 | 
22 | To install Whimsical MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/BrockReece/whimsical-mcp-server):
23 | 
24 | ```bash
25 | npx -y @smithery/cli install BrockReece/whimsical-mcp-server --client claude
26 | ```
27 | 
28 | ### Manual Installation
29 | ```bash
30 | # Clone the repository
31 | git clone https://github.com/BrockReece/whimsical-mcp-server.git
32 | 
33 | # Install dependencies
34 | yarn install
35 | 
36 | # Build the project
37 | yarn build
38 | ```
39 | 
40 | ### Integration with MCP Client
41 | Update the MCP Client's config to point to this repository's dist folder
42 | eg:
43 | ```json
44 |     {
45 |         "mcpServers": {
46 |             "whimsical": {
47 |                 "command": "node",
48 |                 "args": [
49 |                     "/path/to/this/repo/whimsical-mcp-server/dist/index.js"
50 |                 ]
51 |             }
52 |         }
53 |     }
54 | ```
55 | ## License
56 | 
57 | This project is licensed under the MIT License.
58 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "es2022",
 4 |     "module": "es2022",
 5 |     "outDir": "./dist",
 6 |     "rootDir": "./src",
 7 |     "strict": true,
 8 |     "esModuleInterop": true,
 9 |     "skipLibCheck": true,
10 |     "forceConsistentCasingInFileNames": true,
11 |     "moduleResolution": "node"
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules"]
15 | }
16 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
 2 | FROM node:lts-alpine
 3 | 
 4 | WORKDIR /app
 5 | 
 6 | # Install dependencies
 7 | COPY package.json yarn.lock ./
 8 | RUN yarn install --frozen-lockfile --ignore-scripts
 9 | 
10 | # Copy all source files
11 | COPY . .
12 | 
13 | # Build the project
14 | RUN yarn build
15 | 
16 | # By default, run the server as specified by the smithery.yaml configuration
17 | CMD ["node", "dist/index.js"]
18 | 
```

--------------------------------------------------------------------------------
/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 |     properties: {}
10 |   commandFunction:
11 |     # A function that produces the CLI command to start the MCP on stdio.
12 |     |-
13 |     (config) => ({command:'node',args:['dist/index.js'],env:{}})
14 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "whimsical-mcp-server",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP server for creating Whimsical diagrams",
 5 |   "main": "dist/index.js",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "start": "node dist/index.js",
10 |     "dev": "ts-node src/index.ts"
11 |   },
12 |   "dependencies": {
13 |     "@modelcontextprotocol/sdk": "^1.5.0",
14 |     "@playwright/test": "^1.41.1",
15 |     "@types/node": "^20.0.0",
16 |     "node-fetch": "^3.3.2",
17 |     "ts-node": "^10.9.1",
18 |     "typescript": "^5.0.0"
19 |   }
20 | }
21 | 
```

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

```typescript
 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 3 | import { z } from "zod";
 4 | import fetch from "node-fetch"; 
 5 | 
 6 | interface WhimsicalResponse {
 7 |   fileURL: string;
 8 |   imageURL: string;
 9 | }
10 | 
11 | // Create an MCP server
12 | const server = new McpServer({
13 |   name: "Whimsical Diagram Generator",
14 |   version: "1.0.0"
15 | });
16 | 
17 | 
18 | /**
19 |  * Get base64 encoded image
20 |  * 
21 |  * @param imageUrl URL to the image we wish to convert to base64
22 |  * @returns Details of the image, including the base64 encoded image and the mime type
23 |  */
24 | export async function getBase64Image(imageUrl: string): Promise<{ data: string; mimeType: string }> {
25 |   const response = await fetch(imageUrl);
26 |   const buffer = await response.arrayBuffer();
27 |   const mimeType = response.headers.get('content-type') || 'image/png';
28 |   return {
29 |     data: Buffer.from(buffer).toString('base64'),
30 |     mimeType
31 |   };
32 | }
33 | 
34 | /**
35 |  * Creates a new Whimsical diagram
36 |  * 
37 |  * @param mermaid_markup The mermaid markup for the diagram
38 |  * @param title The title of the Whimsical diagram
39 |  * 
40 |  * @returns [
41 |  *  The url of the Whimsical diagram,
42 |  *  The base64 encoded image of the Whimsical diagram
43 |  * ]
44 |  */
45 | server.tool("create_whimsical_diagram", 
46 |   {
47 |     mermaid_markup: z.string().describe("The mermaid markup for the diagram"),
48 |     title: z.string().describe("The title of the Whimsical diagram") },
49 |     async ({ mermaid_markup, title }) => {      
50 |       const response = await fetch("https://whimsical.com/api/ai.chatgpt.render-flowchart", {
51 |         method: "POST",
52 |         headers: {
53 |           'Content-Type': 'application/json'
54 |         },
55 |         body: JSON.stringify({
56 |           mermaid: mermaid_markup,
57 |           title
58 |         })
59 |       });
60 |             
61 |       const responseData = await response.json() as WhimsicalResponse;
62 |       
63 |       // Get base64 encoded image
64 |       const { data, mimeType } = await getBase64Image(responseData.imageURL);
65 |       
66 |       return {
67 |         content: [
68 |           { type: "text", text: responseData.fileURL },
69 |           {
70 |             type: "image",
71 |             data,
72 |             mimeType
73 |           },
74 |           { 
75 |             type: "resource",
76 |             resource: {
77 |               uri: responseData.fileURL,
78 |               text: "Whimsical Link"
79 |             }
80 |           }
81 |         ]
82 |       };
83 |   });
84 | 
85 | // Start receiving messages on stdin and sending messages on stdout
86 | const transport = new StdioServerTransport();
87 | await server.connect(transport);
```