@dynamic-mockups/mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +204 -0
- package/package.json +45 -0
- package/src/index.js +713 -0
- package/src/response-formatter.js +104 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dynamic Mockups
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Dynamic Mockups MCP Server
|
|
2
|
+
|
|
3
|
+
Official MCP (Model Context Protocol) server for the [Dynamic Mockups API](https://dynamicmockups.com). Generate product mockups directly from AI assistants like Claude, Cursor, Windsurf, and more.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Quick Start with npx
|
|
8
|
+
|
|
9
|
+
No installation required - just configure your MCP client:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"dynamic-mockups": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "@dynamic-mockups/mcp"],
|
|
17
|
+
"env": {
|
|
18
|
+
"DYNAMIC_MOCKUPS_API_KEY": "your_api_key_here"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Get Your API Key
|
|
26
|
+
|
|
27
|
+
1. Go to [Dynamic Mockups Dashboard](https://app.dynamicmockups.com/account/api-keys)
|
|
28
|
+
2. Create a new API key
|
|
29
|
+
3. Add it to your MCP client configuration
|
|
30
|
+
|
|
31
|
+
## Configuration by Client
|
|
32
|
+
|
|
33
|
+
### Claude Desktop
|
|
34
|
+
|
|
35
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"dynamic-mockups": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": ["-y", "@dynamic-mockups/mcp"],
|
|
43
|
+
"env": {
|
|
44
|
+
"DYNAMIC_MOCKUPS_API_KEY": "your_api_key_here"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Claude Code (CLI)
|
|
52
|
+
|
|
53
|
+
Add to `.mcp.json` in your project root:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"dynamic-mockups": {
|
|
59
|
+
"command": "npx",
|
|
60
|
+
"args": ["-y", "@dynamic-mockups/mcp"],
|
|
61
|
+
"env": {
|
|
62
|
+
"DYNAMIC_MOCKUPS_API_KEY": "your_api_key_here"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Cursor
|
|
70
|
+
|
|
71
|
+
Add to `.cursor/mcp.json` in your project:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mcpServers": {
|
|
76
|
+
"dynamic-mockups": {
|
|
77
|
+
"command": "npx",
|
|
78
|
+
"args": ["-y", "@dynamic-mockups/mcp"],
|
|
79
|
+
"env": {
|
|
80
|
+
"DYNAMIC_MOCKUPS_API_KEY": "your_api_key_here"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Windsurf
|
|
88
|
+
|
|
89
|
+
Add to `~/.codeium/windsurf/mcp_config.json`:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"dynamic-mockups": {
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": ["-y", "@dynamic-mockups/mcp"],
|
|
97
|
+
"env": {
|
|
98
|
+
"DYNAMIC_MOCKUPS_API_KEY": "your_api_key_here"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Available Tools
|
|
106
|
+
|
|
107
|
+
### API Information
|
|
108
|
+
- **`get_api_info`** - Get API knowledge base (billing, rate limits, formats, best practices, support)
|
|
109
|
+
|
|
110
|
+
### Catalogs
|
|
111
|
+
- **`get_catalogs`** - Retrieve all available catalogs
|
|
112
|
+
|
|
113
|
+
### Collections
|
|
114
|
+
- **`get_collections`** - Retrieve collections (optionally filter by catalog)
|
|
115
|
+
- **`create_collection`** - Create a new collection
|
|
116
|
+
|
|
117
|
+
### Mockups
|
|
118
|
+
- **`get_mockups`** - Get list of available mockups with optional filters
|
|
119
|
+
- **`get_mockup_by_uuid`** - Retrieve a specific mockup by UUID
|
|
120
|
+
|
|
121
|
+
### Rendering
|
|
122
|
+
- **`create_render`** - Create a single mockup render with design assets
|
|
123
|
+
- **`create_batch_render`** - Render multiple mockups in one request
|
|
124
|
+
- **`export_print_files`** - Export print files for smart objects
|
|
125
|
+
|
|
126
|
+
### PSD Files
|
|
127
|
+
- **`upload_psd`** - Upload a PSD file with optional mockup template creation
|
|
128
|
+
- **`delete_psd`** - Delete a PSD file with optional related mockups deletion
|
|
129
|
+
|
|
130
|
+
## Usage Examples
|
|
131
|
+
|
|
132
|
+
### Get Your Catalogs
|
|
133
|
+
|
|
134
|
+
Ask your AI assistant:
|
|
135
|
+
> "Get my Dynamic Mockups catalogs"
|
|
136
|
+
|
|
137
|
+
### Get Mockups from a Collection
|
|
138
|
+
|
|
139
|
+
> "Show me all mockups in my T-shirt collection"
|
|
140
|
+
|
|
141
|
+
### Create a Render
|
|
142
|
+
|
|
143
|
+
> "Create a mockup render using mockup UUID abc123 with my logo from https://example.com/logo.png"
|
|
144
|
+
|
|
145
|
+
### Batch Render
|
|
146
|
+
|
|
147
|
+
> "Render my design on all mockups in the Summer collection"
|
|
148
|
+
|
|
149
|
+
## Development
|
|
150
|
+
|
|
151
|
+
### Local Installation
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
git clone https://github.com/dynamicmockups/mcp-server.git
|
|
155
|
+
cd mcp-server
|
|
156
|
+
npm install
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Run Locally
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
DYNAMIC_MOCKUPS_API_KEY=your_key npm start
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Development Mode (with auto-reload)
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
DYNAMIC_MOCKUPS_API_KEY=your_key npm run dev
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Use Local Version in MCP Client
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"mcpServers": {
|
|
176
|
+
"dynamic-mockups": {
|
|
177
|
+
"command": "node",
|
|
178
|
+
"args": ["/path/to/mcp-server/src/index.js"],
|
|
179
|
+
"env": {
|
|
180
|
+
"DYNAMIC_MOCKUPS_API_KEY": "your_api_key_here"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Error Handling
|
|
188
|
+
|
|
189
|
+
The server returns clear error messages for common issues:
|
|
190
|
+
|
|
191
|
+
- **API key not configured** - Set `DYNAMIC_MOCKUPS_API_KEY` in your environment
|
|
192
|
+
- **Invalid UUID** - Ensure UUIDs are in correct format
|
|
193
|
+
- **API errors** - Check the returned message for details
|
|
194
|
+
|
|
195
|
+
## Links
|
|
196
|
+
|
|
197
|
+
- [Dynamic Mockups Website](https://dynamicmockups.com)
|
|
198
|
+
- [API Documentation](https://docs.dynamicmockups.com)
|
|
199
|
+
- [Get API Key](https://app.dynamicmockups.com/account/api-keys)
|
|
200
|
+
- [GitHub Issues](https://github.com/dynamicmockups/mcp-server/issues)
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dynamic-mockups/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Dynamic Mockups MCP Server - Generate product mockups with AI assistants",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dynamic-mockups-mcp": "src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/index.js",
|
|
12
|
+
"dev": "node --watch src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"dynamic-mockups",
|
|
18
|
+
"mockups",
|
|
19
|
+
"ai",
|
|
20
|
+
"claude",
|
|
21
|
+
"anthropic",
|
|
22
|
+
"design",
|
|
23
|
+
"product-mockups"
|
|
24
|
+
],
|
|
25
|
+
"author": "Dynamic Mockups <support@dynamicmockups.com>",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/dynamic-mockups/mcp.git"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://dynamicmockups.com",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/dynamic-mockups/mcp/issues"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
37
|
+
"axios": "^1.6.0"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"src/**/*"
|
|
44
|
+
]
|
|
45
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dynamic Mockups MCP Server
|
|
5
|
+
* Official MCP server for the Dynamic Mockups API
|
|
6
|
+
* https://dynamicmockups.com
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
10
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import axios from "axios";
|
|
13
|
+
import { ResponseFormatter } from "./response-formatter.js";
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Configuration
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
const API_BASE_URL = "https://app.dynamicmockups.com/api/v1";
|
|
20
|
+
const API_KEY = process.env.DYNAMIC_MOCKUPS_API_KEY;
|
|
21
|
+
const SERVER_NAME = "dynamic-mockups-mcp";
|
|
22
|
+
const SERVER_VERSION = "1.0.0";
|
|
23
|
+
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// API Knowledge Base
|
|
26
|
+
// =============================================================================
|
|
27
|
+
|
|
28
|
+
const API_KNOWLEDGE_BASE = {
|
|
29
|
+
overview: "Dynamic Mockups API allows you to generate product mockups programmatically.",
|
|
30
|
+
|
|
31
|
+
billing: {
|
|
32
|
+
credits_per_image: 1,
|
|
33
|
+
free_credits: 50,
|
|
34
|
+
free_tier_watermark: true,
|
|
35
|
+
pro_subscription_removes_watermark: true,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
rate_limits: {
|
|
39
|
+
requests_per_minute: 300,
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
rendered_images: {
|
|
43
|
+
availability_hours: 24,
|
|
44
|
+
note: "Rendered image links expire after 24 hours. Contact support to extend.",
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
supported_formats: {
|
|
48
|
+
input: ["jpg", "jpeg", "png", "webp", "gif"],
|
|
49
|
+
output: ["jpg", "png", "webp"],
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
asset_upload: {
|
|
53
|
+
methods: ["URL", "binary file (form-data)"],
|
|
54
|
+
note: "Binary files must be sent as multipart/form-data in render requests.",
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
best_practices: [
|
|
58
|
+
"Use create_batch_render for multiple images (more efficient than single renders)",
|
|
59
|
+
"Always include Accept: application/json header (handled automatically by this MCP)",
|
|
60
|
+
"Store rendered image URLs promptly as they expire in 24 hours",
|
|
61
|
+
],
|
|
62
|
+
|
|
63
|
+
support: {
|
|
64
|
+
email: "support@dynamicmockups.com",
|
|
65
|
+
tutorials: "https://docs.dynamicmockups.com/knowledge-base/tutorials",
|
|
66
|
+
api_docs: "https://docs.dynamicmockups.com",
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// =============================================================================
|
|
71
|
+
// Server Initialization
|
|
72
|
+
// =============================================================================
|
|
73
|
+
|
|
74
|
+
const server = new Server(
|
|
75
|
+
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
76
|
+
{ capabilities: { tools: {} } }
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// HTTP Client
|
|
81
|
+
// =============================================================================
|
|
82
|
+
|
|
83
|
+
function createApiClient() {
|
|
84
|
+
return axios.create({
|
|
85
|
+
baseURL: API_BASE_URL,
|
|
86
|
+
headers: {
|
|
87
|
+
"Accept": "application/json",
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
"x-api-key": API_KEY || "",
|
|
90
|
+
},
|
|
91
|
+
timeout: 60000, // 60 second timeout for render operations
|
|
92
|
+
validateStatus: (status) => status < 500, // Only throw on 5xx errors
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function validateApiKey() {
|
|
97
|
+
if (!API_KEY) {
|
|
98
|
+
return ResponseFormatter.error(
|
|
99
|
+
"API key not configured",
|
|
100
|
+
{
|
|
101
|
+
solution: "Set the DYNAMIC_MOCKUPS_API_KEY environment variable in your MCP client configuration.",
|
|
102
|
+
get_key_at: "https://app.dynamicmockups.com/account/api-keys",
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// =============================================================================
|
|
110
|
+
// Tool Definitions
|
|
111
|
+
// =============================================================================
|
|
112
|
+
|
|
113
|
+
const tools = [
|
|
114
|
+
// Knowledge Base Tool
|
|
115
|
+
{
|
|
116
|
+
name: "get_api_info",
|
|
117
|
+
description: "Get Dynamic Mockups API information including billing, rate limits, supported formats, and best practices. Use this to understand API capabilities and constraints.",
|
|
118
|
+
inputSchema: {
|
|
119
|
+
type: "object",
|
|
120
|
+
properties: {
|
|
121
|
+
topic: {
|
|
122
|
+
type: "string",
|
|
123
|
+
enum: ["all", "billing", "rate_limits", "formats", "best_practices", "support"],
|
|
124
|
+
description: "Specific topic to get info about, or 'all' for complete knowledge base",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// Catalog Tools
|
|
131
|
+
{
|
|
132
|
+
name: "get_catalogs",
|
|
133
|
+
description: "Retrieve all available catalogs. Catalogs are top-level containers for organizing collections and mockups.",
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: {},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// Collection Tools
|
|
141
|
+
{
|
|
142
|
+
name: "get_collections",
|
|
143
|
+
description: "Retrieve collections, optionally filtered by catalog. Collections group related mockups together.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
catalog_uuid: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "Filter collections by catalog UUID",
|
|
150
|
+
},
|
|
151
|
+
include_all_catalogs: {
|
|
152
|
+
type: "boolean",
|
|
153
|
+
description: "Include collections from all catalogs (default: false, returns only default catalog)",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "create_collection",
|
|
160
|
+
description: "Create a new collection to organize mockups",
|
|
161
|
+
inputSchema: {
|
|
162
|
+
type: "object",
|
|
163
|
+
properties: {
|
|
164
|
+
name: {
|
|
165
|
+
type: "string",
|
|
166
|
+
description: "Name for the new collection",
|
|
167
|
+
},
|
|
168
|
+
catalog_uuid: {
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "Catalog UUID to create collection in (uses default catalog if not specified)",
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
required: ["name"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// Mockup Tools
|
|
178
|
+
{
|
|
179
|
+
name: "get_mockups",
|
|
180
|
+
description: "Retrieve mockups from My Templates with optional filtering. Returns mockup UUIDs needed for rendering.",
|
|
181
|
+
inputSchema: {
|
|
182
|
+
type: "object",
|
|
183
|
+
properties: {
|
|
184
|
+
catalog_uuid: {
|
|
185
|
+
type: "string",
|
|
186
|
+
description: "Filter by catalog UUID",
|
|
187
|
+
},
|
|
188
|
+
collection_uuid: {
|
|
189
|
+
type: "string",
|
|
190
|
+
description: "Filter by collection UUID",
|
|
191
|
+
},
|
|
192
|
+
include_all_catalogs: {
|
|
193
|
+
type: "boolean",
|
|
194
|
+
description: "Include mockups from all catalogs (default: false)",
|
|
195
|
+
},
|
|
196
|
+
name: {
|
|
197
|
+
type: "string",
|
|
198
|
+
description: "Filter mockups by name (partial match)",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "get_mockup_by_uuid",
|
|
205
|
+
description: "Get detailed information about a specific mockup including its smart objects and configuration",
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
uuid: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "The mockup UUID",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
required: ["uuid"],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// Render Tools
|
|
219
|
+
{
|
|
220
|
+
name: "create_render",
|
|
221
|
+
description: "Render a single mockup with design assets. Costs 1 credit per render. For multiple renders, use create_batch_render instead.",
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {
|
|
225
|
+
mockup_uuid: {
|
|
226
|
+
type: "string",
|
|
227
|
+
description: "UUID of the mockup template to render",
|
|
228
|
+
},
|
|
229
|
+
smart_objects: {
|
|
230
|
+
type: "array",
|
|
231
|
+
description: "Smart objects configuration with design assets",
|
|
232
|
+
items: {
|
|
233
|
+
type: "object",
|
|
234
|
+
properties: {
|
|
235
|
+
uuid: {
|
|
236
|
+
type: "string",
|
|
237
|
+
description: "Smart object UUID (get from mockup details)",
|
|
238
|
+
},
|
|
239
|
+
asset: {
|
|
240
|
+
type: "object",
|
|
241
|
+
description: "Design asset configuration",
|
|
242
|
+
properties: {
|
|
243
|
+
url: {
|
|
244
|
+
type: "string",
|
|
245
|
+
description: "URL to the design image (jpg, jpeg, png, webp, gif)",
|
|
246
|
+
},
|
|
247
|
+
fit: {
|
|
248
|
+
type: "string",
|
|
249
|
+
enum: ["stretch", "contain", "cover"],
|
|
250
|
+
description: "How to fit the asset in the smart object area",
|
|
251
|
+
},
|
|
252
|
+
size: {
|
|
253
|
+
type: "object",
|
|
254
|
+
properties: {
|
|
255
|
+
width: { type: "integer" },
|
|
256
|
+
height: { type: "integer" },
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
position: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
top: { type: "integer" },
|
|
263
|
+
left: { type: "integer" },
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
rotate: {
|
|
267
|
+
type: "number",
|
|
268
|
+
description: "Rotation angle in degrees",
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
color: {
|
|
273
|
+
type: "string",
|
|
274
|
+
description: "Color overlay in hex format (e.g., #FF0000)",
|
|
275
|
+
},
|
|
276
|
+
print_area_preset_uuid: {
|
|
277
|
+
type: "string",
|
|
278
|
+
description: "Print area preset UUID for automatic positioning",
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
export_label: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description: "Label for the exported image (appears in filename)",
|
|
286
|
+
},
|
|
287
|
+
export_options: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
image_format: {
|
|
291
|
+
type: "string",
|
|
292
|
+
enum: ["jpg", "png", "webp"],
|
|
293
|
+
description: "Output image format (default: jpg)",
|
|
294
|
+
},
|
|
295
|
+
image_size: {
|
|
296
|
+
type: "integer",
|
|
297
|
+
description: "Output image size in pixels (default: 1000)",
|
|
298
|
+
},
|
|
299
|
+
mode: {
|
|
300
|
+
type: "string",
|
|
301
|
+
enum: ["view", "download"],
|
|
302
|
+
description: "URL mode - 'view' for browser display, 'download' for attachment",
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
text_layers: {
|
|
307
|
+
type: "array",
|
|
308
|
+
description: "Text layer customizations",
|
|
309
|
+
items: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
uuid: { type: "string", description: "Text layer UUID" },
|
|
313
|
+
text: { type: "string", description: "Text content" },
|
|
314
|
+
font_family: { type: "string" },
|
|
315
|
+
font_size: { type: "number" },
|
|
316
|
+
font_color: { type: "string", description: "Hex color code" },
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
required: ["mockup_uuid", "smart_objects"],
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: "create_batch_render",
|
|
326
|
+
description: "Render multiple mockups in a single request. RECOMMENDED for rendering more than one image - more efficient and faster than individual renders. Costs 1 credit per image.",
|
|
327
|
+
inputSchema: {
|
|
328
|
+
type: "object",
|
|
329
|
+
properties: {
|
|
330
|
+
renders: {
|
|
331
|
+
type: "array",
|
|
332
|
+
description: "Array of render configurations",
|
|
333
|
+
items: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
mockup_uuid: {
|
|
337
|
+
type: "string",
|
|
338
|
+
description: "UUID of the mockup template",
|
|
339
|
+
},
|
|
340
|
+
smart_objects: {
|
|
341
|
+
type: "array",
|
|
342
|
+
description: "Smart objects configuration (same as create_render)",
|
|
343
|
+
},
|
|
344
|
+
text_layers: {
|
|
345
|
+
type: "array",
|
|
346
|
+
description: "Text layer customizations",
|
|
347
|
+
},
|
|
348
|
+
export_label: {
|
|
349
|
+
type: "string",
|
|
350
|
+
description: "Label for this specific render",
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
required: ["mockup_uuid", "smart_objects"],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
export_options: {
|
|
357
|
+
type: "object",
|
|
358
|
+
description: "Export options applied to all renders in the batch",
|
|
359
|
+
properties: {
|
|
360
|
+
image_format: {
|
|
361
|
+
type: "string",
|
|
362
|
+
enum: ["jpg", "png", "webp"],
|
|
363
|
+
},
|
|
364
|
+
image_size: {
|
|
365
|
+
type: "integer",
|
|
366
|
+
},
|
|
367
|
+
mode: {
|
|
368
|
+
type: "string",
|
|
369
|
+
enum: ["view", "download"],
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
required: ["renders"],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: "export_print_files",
|
|
379
|
+
description: "Export high-resolution print files for production. Supports custom DPI settings.",
|
|
380
|
+
inputSchema: {
|
|
381
|
+
type: "object",
|
|
382
|
+
properties: {
|
|
383
|
+
mockup_uuid: {
|
|
384
|
+
type: "string",
|
|
385
|
+
description: "UUID of the mockup template",
|
|
386
|
+
},
|
|
387
|
+
smart_objects: {
|
|
388
|
+
type: "array",
|
|
389
|
+
description: "Smart objects configuration",
|
|
390
|
+
},
|
|
391
|
+
text_layers: {
|
|
392
|
+
type: "array",
|
|
393
|
+
description: "Text layer customizations",
|
|
394
|
+
},
|
|
395
|
+
export_label: {
|
|
396
|
+
type: "string",
|
|
397
|
+
description: "Label for the export",
|
|
398
|
+
},
|
|
399
|
+
export_options: {
|
|
400
|
+
type: "object",
|
|
401
|
+
properties: {
|
|
402
|
+
image_format: { type: "string", enum: ["jpg", "png", "webp"] },
|
|
403
|
+
image_size: { type: "integer" },
|
|
404
|
+
image_dpi: { type: "integer", description: "DPI for print (e.g., 300)" },
|
|
405
|
+
mode: { type: "string", enum: ["view", "download"] },
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
required: ["mockup_uuid", "smart_objects"],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
|
|
413
|
+
// PSD Management Tools
|
|
414
|
+
{
|
|
415
|
+
name: "upload_psd",
|
|
416
|
+
description: "Upload a PSD file to create custom mockup templates. The PSD should contain smart object layers.",
|
|
417
|
+
inputSchema: {
|
|
418
|
+
type: "object",
|
|
419
|
+
properties: {
|
|
420
|
+
psd_file_url: {
|
|
421
|
+
type: "string",
|
|
422
|
+
description: "Public URL to the PSD file",
|
|
423
|
+
},
|
|
424
|
+
psd_name: {
|
|
425
|
+
type: "string",
|
|
426
|
+
description: "Name for the uploaded PSD",
|
|
427
|
+
},
|
|
428
|
+
psd_category_id: {
|
|
429
|
+
type: "integer",
|
|
430
|
+
description: "Category ID for organization",
|
|
431
|
+
},
|
|
432
|
+
mockup_template: {
|
|
433
|
+
type: "object",
|
|
434
|
+
description: "Automatically create a mockup template from the PSD",
|
|
435
|
+
properties: {
|
|
436
|
+
create_after_upload: {
|
|
437
|
+
type: "boolean",
|
|
438
|
+
description: "Create mockup template after upload",
|
|
439
|
+
},
|
|
440
|
+
collections: {
|
|
441
|
+
type: "array",
|
|
442
|
+
items: { type: "string" },
|
|
443
|
+
description: "Collection UUIDs to add the mockup to",
|
|
444
|
+
},
|
|
445
|
+
catalog_uuid: {
|
|
446
|
+
type: "string",
|
|
447
|
+
description: "Catalog UUID for the mockup",
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
required: ["psd_file_url"],
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: "delete_psd",
|
|
457
|
+
description: "Delete a PSD file and optionally all mockups created from it",
|
|
458
|
+
inputSchema: {
|
|
459
|
+
type: "object",
|
|
460
|
+
properties: {
|
|
461
|
+
psd_uuid: {
|
|
462
|
+
type: "string",
|
|
463
|
+
description: "UUID of the PSD to delete",
|
|
464
|
+
},
|
|
465
|
+
delete_related_mockups: {
|
|
466
|
+
type: "boolean",
|
|
467
|
+
description: "Also delete all mockups created from this PSD (default: false)",
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
required: ["psd_uuid"],
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
];
|
|
474
|
+
|
|
475
|
+
// =============================================================================
|
|
476
|
+
// Tool Handlers
|
|
477
|
+
// =============================================================================
|
|
478
|
+
|
|
479
|
+
async function handleGetApiInfo(args) {
|
|
480
|
+
const topic = args?.topic || "all";
|
|
481
|
+
|
|
482
|
+
const topicMap = {
|
|
483
|
+
billing: { billing: API_KNOWLEDGE_BASE.billing },
|
|
484
|
+
rate_limits: { rate_limits: API_KNOWLEDGE_BASE.rate_limits },
|
|
485
|
+
formats: { supported_formats: API_KNOWLEDGE_BASE.supported_formats, asset_upload: API_KNOWLEDGE_BASE.asset_upload },
|
|
486
|
+
best_practices: { best_practices: API_KNOWLEDGE_BASE.best_practices },
|
|
487
|
+
support: { support: API_KNOWLEDGE_BASE.support },
|
|
488
|
+
all: API_KNOWLEDGE_BASE,
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
return ResponseFormatter.ok(topicMap[topic] || API_KNOWLEDGE_BASE);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function handleGetCatalogs() {
|
|
495
|
+
const error = validateApiKey();
|
|
496
|
+
if (error) return error;
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
const response = await createApiClient().get("/catalogs");
|
|
500
|
+
return ResponseFormatter.fromApiResponse(response);
|
|
501
|
+
} catch (err) {
|
|
502
|
+
return ResponseFormatter.fromError(err, "Failed to get catalogs");
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
async function handleGetCollections(args = {}) {
|
|
507
|
+
const error = validateApiKey();
|
|
508
|
+
if (error) return error;
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
const params = new URLSearchParams();
|
|
512
|
+
if (args.catalog_uuid) params.append("catalog_uuid", args.catalog_uuid);
|
|
513
|
+
if (args.include_all_catalogs !== undefined) {
|
|
514
|
+
params.append("include_all_catalogs", args.include_all_catalogs);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const response = await createApiClient().get(`/collections?${params}`);
|
|
518
|
+
return ResponseFormatter.fromApiResponse(response);
|
|
519
|
+
} catch (err) {
|
|
520
|
+
return ResponseFormatter.fromError(err, "Failed to get collections");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async function handleCreateCollection(args) {
|
|
525
|
+
const error = validateApiKey();
|
|
526
|
+
if (error) return error;
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
const payload = { name: args.name };
|
|
530
|
+
if (args.catalog_uuid) payload.catalog_uuid = args.catalog_uuid;
|
|
531
|
+
|
|
532
|
+
const response = await createApiClient().post("/collections", payload);
|
|
533
|
+
return ResponseFormatter.fromApiResponse(response, `Collection "${args.name}" created`);
|
|
534
|
+
} catch (err) {
|
|
535
|
+
return ResponseFormatter.fromError(err, "Failed to create collection");
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function handleGetMockups(args = {}) {
|
|
540
|
+
const error = validateApiKey();
|
|
541
|
+
if (error) return error;
|
|
542
|
+
|
|
543
|
+
try {
|
|
544
|
+
const params = new URLSearchParams();
|
|
545
|
+
if (args.catalog_uuid) params.append("catalog_uuid", args.catalog_uuid);
|
|
546
|
+
if (args.collection_uuid) params.append("collection_uuid", args.collection_uuid);
|
|
547
|
+
if (args.include_all_catalogs !== undefined) {
|
|
548
|
+
params.append("include_all_catalogs", args.include_all_catalogs);
|
|
549
|
+
}
|
|
550
|
+
if (args.name) params.append("name", args.name);
|
|
551
|
+
|
|
552
|
+
const response = await createApiClient().get(`/mockups?${params}`);
|
|
553
|
+
return ResponseFormatter.fromApiResponse(response);
|
|
554
|
+
} catch (err) {
|
|
555
|
+
return ResponseFormatter.fromError(err, "Failed to get mockups");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async function handleGetMockupByUuid(args) {
|
|
560
|
+
const error = validateApiKey();
|
|
561
|
+
if (error) return error;
|
|
562
|
+
|
|
563
|
+
try {
|
|
564
|
+
const response = await createApiClient().get(`/mockup/${args.uuid}`);
|
|
565
|
+
return ResponseFormatter.fromApiResponse(response);
|
|
566
|
+
} catch (err) {
|
|
567
|
+
return ResponseFormatter.fromError(err, "Failed to get mockup");
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
async function handleCreateRender(args) {
|
|
572
|
+
const error = validateApiKey();
|
|
573
|
+
if (error) return error;
|
|
574
|
+
|
|
575
|
+
try {
|
|
576
|
+
const payload = {
|
|
577
|
+
mockup_uuid: args.mockup_uuid,
|
|
578
|
+
smart_objects: args.smart_objects,
|
|
579
|
+
};
|
|
580
|
+
if (args.export_label) payload.export_label = args.export_label;
|
|
581
|
+
if (args.export_options) payload.export_options = args.export_options;
|
|
582
|
+
if (args.text_layers) payload.text_layers = args.text_layers;
|
|
583
|
+
|
|
584
|
+
const response = await createApiClient().post("/renders", payload);
|
|
585
|
+
return ResponseFormatter.fromApiResponse(response, "Render created (1 credit used)");
|
|
586
|
+
} catch (err) {
|
|
587
|
+
return ResponseFormatter.fromError(err, "Failed to create render");
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async function handleCreateBatchRender(args) {
|
|
592
|
+
const error = validateApiKey();
|
|
593
|
+
if (error) return error;
|
|
594
|
+
|
|
595
|
+
try {
|
|
596
|
+
const payload = { renders: args.renders };
|
|
597
|
+
if (args.export_options) payload.export_options = args.export_options;
|
|
598
|
+
|
|
599
|
+
const response = await createApiClient().post("/renders/batch", payload);
|
|
600
|
+
const count = args.renders?.length || 0;
|
|
601
|
+
return ResponseFormatter.fromApiResponse(response, `Batch render complete (${count} credits used)`);
|
|
602
|
+
} catch (err) {
|
|
603
|
+
return ResponseFormatter.fromError(err, "Failed to create batch render");
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
async function handleExportPrintFiles(args) {
|
|
608
|
+
const error = validateApiKey();
|
|
609
|
+
if (error) return error;
|
|
610
|
+
|
|
611
|
+
try {
|
|
612
|
+
const payload = {
|
|
613
|
+
mockup_uuid: args.mockup_uuid,
|
|
614
|
+
smart_objects: args.smart_objects,
|
|
615
|
+
};
|
|
616
|
+
if (args.export_label) payload.export_label = args.export_label;
|
|
617
|
+
if (args.export_options) payload.export_options = args.export_options;
|
|
618
|
+
if (args.text_layers) payload.text_layers = args.text_layers;
|
|
619
|
+
|
|
620
|
+
const response = await createApiClient().post("/renders/print-files", payload);
|
|
621
|
+
return ResponseFormatter.fromApiResponse(response, "Print files exported");
|
|
622
|
+
} catch (err) {
|
|
623
|
+
return ResponseFormatter.fromError(err, "Failed to export print files");
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
async function handleUploadPsd(args) {
|
|
628
|
+
const error = validateApiKey();
|
|
629
|
+
if (error) return error;
|
|
630
|
+
|
|
631
|
+
try {
|
|
632
|
+
const payload = { psd_file_url: args.psd_file_url };
|
|
633
|
+
if (args.psd_name) payload.psd_name = args.psd_name;
|
|
634
|
+
if (args.psd_category_id) payload.psd_category_id = args.psd_category_id;
|
|
635
|
+
if (args.mockup_template) payload.mockup_template = args.mockup_template;
|
|
636
|
+
|
|
637
|
+
const response = await createApiClient().post("/psd/upload", payload);
|
|
638
|
+
return ResponseFormatter.fromApiResponse(response, "PSD uploaded successfully");
|
|
639
|
+
} catch (err) {
|
|
640
|
+
return ResponseFormatter.fromError(err, "Failed to upload PSD");
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
async function handleDeletePsd(args) {
|
|
645
|
+
const error = validateApiKey();
|
|
646
|
+
if (error) return error;
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
const payload = { psd_uuid: args.psd_uuid };
|
|
650
|
+
if (args.delete_related_mockups !== undefined) {
|
|
651
|
+
payload.delete_related_mockups = args.delete_related_mockups;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const response = await createApiClient().post("/psd/delete", payload);
|
|
655
|
+
return ResponseFormatter.fromApiResponse(response, "PSD deleted successfully");
|
|
656
|
+
} catch (err) {
|
|
657
|
+
return ResponseFormatter.fromError(err, "Failed to delete PSD");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// =============================================================================
|
|
662
|
+
// Tool Router
|
|
663
|
+
// =============================================================================
|
|
664
|
+
|
|
665
|
+
const toolHandlers = {
|
|
666
|
+
get_api_info: handleGetApiInfo,
|
|
667
|
+
get_catalogs: handleGetCatalogs,
|
|
668
|
+
get_collections: handleGetCollections,
|
|
669
|
+
create_collection: handleCreateCollection,
|
|
670
|
+
get_mockups: handleGetMockups,
|
|
671
|
+
get_mockup_by_uuid: handleGetMockupByUuid,
|
|
672
|
+
create_render: handleCreateRender,
|
|
673
|
+
create_batch_render: handleCreateBatchRender,
|
|
674
|
+
export_print_files: handleExportPrintFiles,
|
|
675
|
+
upload_psd: handleUploadPsd,
|
|
676
|
+
delete_psd: handleDeletePsd,
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// =============================================================================
|
|
680
|
+
// MCP Request Handlers
|
|
681
|
+
// =============================================================================
|
|
682
|
+
|
|
683
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
684
|
+
|
|
685
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
686
|
+
const { name, arguments: args } = request.params;
|
|
687
|
+
|
|
688
|
+
const handler = toolHandlers[name];
|
|
689
|
+
if (!handler) {
|
|
690
|
+
return ResponseFormatter.error(`Unknown tool: ${name}`);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
return await handler(args || {});
|
|
695
|
+
} catch (err) {
|
|
696
|
+
return ResponseFormatter.fromError(err, `Error executing ${name}`);
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
// =============================================================================
|
|
701
|
+
// Server Startup
|
|
702
|
+
// =============================================================================
|
|
703
|
+
|
|
704
|
+
async function main() {
|
|
705
|
+
const transport = new StdioServerTransport();
|
|
706
|
+
await server.connect(transport);
|
|
707
|
+
console.error(`Dynamic Mockups MCP Server v${SERVER_VERSION} running`);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
main().catch((err) => {
|
|
711
|
+
console.error("Fatal error:", err);
|
|
712
|
+
process.exit(1);
|
|
713
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Formatter for Dynamic Mockups MCP Server
|
|
3
|
+
* Provides consistent MCP-compliant response formatting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class ResponseFormatter {
|
|
7
|
+
/**
|
|
8
|
+
* Create a successful MCP response with data
|
|
9
|
+
* @param {any} data - Data to include in response
|
|
10
|
+
* @returns {object} MCP-compliant response
|
|
11
|
+
*/
|
|
12
|
+
static ok(data) {
|
|
13
|
+
const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: "text", text }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create an error MCP response
|
|
21
|
+
* @param {string} message - Error message
|
|
22
|
+
* @param {object} details - Additional error details
|
|
23
|
+
* @returns {object} MCP-compliant error response
|
|
24
|
+
*/
|
|
25
|
+
static error(message, details = {}) {
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: "text",
|
|
30
|
+
text: JSON.stringify({ error: { message, ...details } }, null, 2),
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
isError: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Format an API response, handling both success and error cases
|
|
39
|
+
* @param {object} response - Axios response object
|
|
40
|
+
* @param {string} successMessage - Optional success message prefix
|
|
41
|
+
* @returns {object} MCP-compliant response
|
|
42
|
+
*/
|
|
43
|
+
static fromApiResponse(response, successMessage = null) {
|
|
44
|
+
const { status, data } = response;
|
|
45
|
+
|
|
46
|
+
// Handle API errors (4xx responses)
|
|
47
|
+
if (status >= 400) {
|
|
48
|
+
const errorMessage = data?.message || data?.error || `API error (${status})`;
|
|
49
|
+
return this.error(errorMessage, {
|
|
50
|
+
status,
|
|
51
|
+
details: data,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Handle successful responses
|
|
56
|
+
if (successMessage) {
|
|
57
|
+
return this.ok({
|
|
58
|
+
message: successMessage,
|
|
59
|
+
...data,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return this.ok(data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Format an error from a caught exception
|
|
68
|
+
* @param {Error} error - The caught error
|
|
69
|
+
* @param {string} context - Context description for the error
|
|
70
|
+
* @returns {object} MCP-compliant error response
|
|
71
|
+
*/
|
|
72
|
+
static fromError(error, context = "Operation failed") {
|
|
73
|
+
// Handle Axios errors
|
|
74
|
+
if (error.response) {
|
|
75
|
+
return this.error(context, {
|
|
76
|
+
status: error.response.status,
|
|
77
|
+
message: error.response.data?.message || error.message,
|
|
78
|
+
details: error.response.data,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle network errors
|
|
83
|
+
if (error.code === "ECONNABORTED") {
|
|
84
|
+
return this.error("Request timeout", {
|
|
85
|
+
context,
|
|
86
|
+
suggestion: "The API request took too long. Try again or use smaller batch sizes.",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (error.code === "ENOTFOUND" || error.code === "ECONNREFUSED") {
|
|
91
|
+
return this.error("Network error", {
|
|
92
|
+
context,
|
|
93
|
+
suggestion: "Unable to reach the API. Check your internet connection.",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handle generic errors
|
|
98
|
+
return this.error(context, {
|
|
99
|
+
message: error.message,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default ResponseFormatter;
|