gearvn-pages-mcp-server 1.0.0 → 1.1.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/README.md +104 -164
- package/dist/api.d.ts +2 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +26 -0
- package/dist/api.js.map +1 -0
- package/dist/index.js +4 -95
- package/dist/index.js.map +1 -1
- package/dist/tools/deploy-page.d.ts +9 -0
- package/dist/tools/deploy-page.d.ts.map +1 -0
- package/dist/tools/deploy-page.js +49 -0
- package/dist/tools/deploy-page.js.map +1 -0
- package/dist/tools/list-pages.d.ts +9 -0
- package/dist/tools/list-pages.d.ts.map +1 -0
- package/dist/tools/list-pages.js +26 -0
- package/dist/tools/list-pages.js.map +1 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -1
- package/src/api.ts +35 -0
- package/src/index.ts +4 -148
- package/src/tools/deploy-page.ts +65 -0
- package/src/tools/list-pages.ts +35 -0
- package/src/types.ts +22 -0
package/README.md
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
# GearVN Pages MCP Server
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) Server cho việc quản lý
|
|
3
|
+
MCP (Model Context Protocol) Server cho việc deploy và quản lý các trang HTML tĩnh lên GearVN Pages.
|
|
4
4
|
|
|
5
5
|
## Tính năng
|
|
6
6
|
|
|
7
|
-
MCP server này cung cấp 2 tools:
|
|
8
|
-
|
|
9
7
|
### 1. deploy-page
|
|
10
8
|
Deploy hoặc update một trang HTML tĩnh.
|
|
11
9
|
|
|
12
10
|
**Parameters:**
|
|
13
|
-
- `title` (string
|
|
14
|
-
- `content` (string
|
|
15
|
-
- `slug` (string
|
|
11
|
+
- `title` (string): Tiêu đề của trang - **AI Agent tự động generate dựa trên nội dung**
|
|
12
|
+
- `content` (string): HTML document đầy đủ với TailwindCSS CDN. JavaScript libraries được phép qua CDN (Alpine.js, Chart.js, GSAP, etc.)
|
|
13
|
+
- `slug` (string): URL slug - **AI Agent tự động generate theo format kebab-case từ title hoặc nội dung**
|
|
14
|
+
|
|
15
|
+
**Lưu ý quan trọng:**
|
|
16
|
+
- User **KHÔNG cần** cung cấp `title` và `slug`
|
|
17
|
+
- AI Agent sẽ tự động generate dựa trên yêu cầu của user
|
|
18
|
+
- Ví dụ: "tạo website quản lý thu chi" → title: "Quản Lý Thu Chi", slug: "quan-ly-thu-chi"
|
|
16
19
|
|
|
17
20
|
**Response:**
|
|
18
21
|
```json
|
|
@@ -24,9 +27,7 @@ Deploy hoặc update một trang HTML tĩnh.
|
|
|
24
27
|
```
|
|
25
28
|
|
|
26
29
|
### 2. list-pages
|
|
27
|
-
Liệt kê tất cả các pages của API key
|
|
28
|
-
|
|
29
|
-
**Parameters:** Không có
|
|
30
|
+
Liệt kê tất cả các pages của API key.
|
|
30
31
|
|
|
31
32
|
**Response:**
|
|
32
33
|
```json
|
|
@@ -48,57 +49,17 @@ Liệt kê tất cả các pages của API key hiện tại.
|
|
|
48
49
|
## Cài đặt
|
|
49
50
|
|
|
50
51
|
### Prerequisites
|
|
51
|
-
- Node.js 18
|
|
52
|
-
-
|
|
53
|
-
- API key từ GearVN Pages
|
|
54
|
-
|
|
55
|
-
### Bước 1: Cài đặt dependencies
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
cd mcp-server
|
|
59
|
-
npm install
|
|
60
|
-
# hoặc
|
|
61
|
-
pnpm install
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Bước 2: Build TypeScript
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
npm run build
|
|
68
|
-
# hoặc
|
|
69
|
-
pnpm build
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Cấu hình
|
|
73
|
-
|
|
74
|
-
MCP server cần 1 environment variable:
|
|
75
|
-
|
|
76
|
-
- `API_KEY` (required): API key của bạn để authenticate với GearVN Pages API
|
|
77
|
-
|
|
78
|
-
### Cách lấy API Key
|
|
79
|
-
|
|
80
|
-
Sử dụng API để tạo API key:
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
curl -X POST https://pages.react.uat.gearvn.xyz/v1/api-keys \
|
|
84
|
-
-H "Content-Type: application/json" \
|
|
85
|
-
-d '{"name": "My MCP Key"}'
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Response sẽ trả về API key, lưu lại để sử dụng.
|
|
52
|
+
- Node.js 18+
|
|
53
|
+
- API key từ GearVN Pages (liên hệ admin để lấy)
|
|
89
54
|
|
|
90
55
|
## Sử dụng với Claude Desktop
|
|
91
56
|
|
|
92
|
-
### Bước 1:
|
|
93
|
-
|
|
94
|
-
Mở file config của Claude Desktop:
|
|
57
|
+
### Bước 1: Mở file config
|
|
95
58
|
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
96
59
|
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
97
60
|
|
|
98
61
|
### Bước 2: Thêm MCP server config
|
|
99
62
|
|
|
100
|
-
**Cách 1: Sử dụng npx (Khuyến nghị)**
|
|
101
|
-
|
|
102
63
|
```json
|
|
103
64
|
{
|
|
104
65
|
"mcpServers": {
|
|
@@ -106,7 +67,7 @@ Mở file config của Claude Desktop:
|
|
|
106
67
|
"command": "npx",
|
|
107
68
|
"args": [
|
|
108
69
|
"-y",
|
|
109
|
-
"
|
|
70
|
+
"gearvn-pages-mcp-server"
|
|
110
71
|
],
|
|
111
72
|
"env": {
|
|
112
73
|
"API_KEY": "sk-your-api-key-here"
|
|
@@ -116,42 +77,16 @@ Mở file config của Claude Desktop:
|
|
|
116
77
|
}
|
|
117
78
|
```
|
|
118
79
|
|
|
119
|
-
**Cách 2: Chạy trực tiếp với node**
|
|
120
|
-
|
|
121
|
-
```json
|
|
122
|
-
{
|
|
123
|
-
"mcpServers": {
|
|
124
|
-
"gearvn-pages": {
|
|
125
|
-
"command": "node",
|
|
126
|
-
"args": [
|
|
127
|
-
"/absolute/path/to/backend-webpage-go/mcp-server/dist/index.js"
|
|
128
|
-
],
|
|
129
|
-
"env": {
|
|
130
|
-
"API_KEY": "sk-your-api-key-here"
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Lưu ý:** Thay `/absolute/path/to/` bằng đường dẫn tuyệt đối đến folder mcp-server của bạn.
|
|
138
|
-
|
|
139
80
|
### Bước 3: Restart Claude Desktop
|
|
140
81
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
## Sử dụng với Cline (VS Code Extension)
|
|
82
|
+
## Sử dụng với Cline (VS Code)
|
|
144
83
|
|
|
145
84
|
### Bước 1: Mở VS Code Settings
|
|
146
85
|
|
|
147
|
-
1.
|
|
148
|
-
2.
|
|
149
|
-
|
|
150
|
-
### Bước 2: Thêm MCP server config
|
|
151
|
-
|
|
152
|
-
Tìm section `cline.mcpServers` và thêm:
|
|
86
|
+
1. Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
|
|
87
|
+
2. "Preferences: Open User Settings (JSON)"
|
|
153
88
|
|
|
154
|
-
|
|
89
|
+
### Bước 2: Thêm config
|
|
155
90
|
|
|
156
91
|
```json
|
|
157
92
|
{
|
|
@@ -160,25 +95,7 @@ Tìm section `cline.mcpServers` và thêm:
|
|
|
160
95
|
"command": "npx",
|
|
161
96
|
"args": [
|
|
162
97
|
"-y",
|
|
163
|
-
"
|
|
164
|
-
],
|
|
165
|
-
"env": {
|
|
166
|
-
"API_KEY": "sk-your-api-key-here"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
**Cách 2: Chạy trực tiếp với node**
|
|
174
|
-
|
|
175
|
-
```json
|
|
176
|
-
{
|
|
177
|
-
"cline.mcpServers": {
|
|
178
|
-
"gearvn-pages": {
|
|
179
|
-
"command": "node",
|
|
180
|
-
"args": [
|
|
181
|
-
"/absolute/path/to/backend-webpage-go/mcp-server/dist/index.js"
|
|
98
|
+
"gearvn-pages-mcp-server"
|
|
182
99
|
],
|
|
183
100
|
"env": {
|
|
184
101
|
"API_KEY": "sk-your-api-key-here"
|
|
@@ -190,96 +107,119 @@ Tìm section `cline.mcpServers` và thêm:
|
|
|
190
107
|
|
|
191
108
|
### Bước 3: Reload VS Code
|
|
192
109
|
|
|
193
|
-
|
|
110
|
+
## Ví dụ sử dụng trong Claude
|
|
194
111
|
|
|
195
|
-
|
|
112
|
+
### Ví dụ 1: Tạo website quản lý thu chi cá nhân
|
|
196
113
|
|
|
197
|
-
|
|
114
|
+
**Prompt của user (đơn giản):**
|
|
115
|
+
```
|
|
116
|
+
Tạo cho tôi một trang web quản lý thu chi cá nhân với các tính năng:
|
|
117
|
+
- Form thêm giao dịch (loại: thu/chi, số tiền, mô tả, ngày)
|
|
118
|
+
- Hiển thị danh sách giao dịch dưới dạng bảng
|
|
119
|
+
- Tổng thu, tổng chi, số dư
|
|
120
|
+
- Biểu đồ cột thu chi theo tháng (dùng Chart.js)
|
|
121
|
+
- Lưu dữ liệu vào localStorage
|
|
122
|
+
- Có thể xóa và sửa giao dịch
|
|
123
|
+
- Responsive mobile
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**AI Agent sẽ tự động:**
|
|
127
|
+
1. ✅ Generate title: "Quản Lý Thu Chi Cá Nhân"
|
|
128
|
+
2. ✅ Generate slug: "quan-ly-thu-chi-ca-nhan"
|
|
129
|
+
3. ✅ Tạo complete HTML document với TailwindCSS CDN
|
|
130
|
+
4. ✅ Thêm Chart.js CDN để vẽ biểu đồ
|
|
131
|
+
5. ✅ Code vanilla JavaScript để xử lý logic
|
|
132
|
+
6. ✅ Deploy và trả về URL
|
|
198
133
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
134
|
+
**Response:**
|
|
135
|
+
```
|
|
136
|
+
✅ Đã deploy thành công!
|
|
137
|
+
URL: https://pages.react.uat.gearvn.xyz/pages/quan-ly-thu-chi-ca-nhan
|
|
202
138
|
```
|
|
203
139
|
|
|
204
|
-
|
|
140
|
+
### Ví dụ 2: Landing page bán hàng
|
|
205
141
|
|
|
206
|
-
|
|
142
|
+
**Prompt của user:**
|
|
143
|
+
```
|
|
144
|
+
Tạo landing page bán khóa học lập trình với:
|
|
145
|
+
- Hero section với CTA button
|
|
146
|
+
- Sections: Lợi ích, Nội dung khóa học, Giảng viên, Testimonials
|
|
147
|
+
- Pricing table với 3 gói
|
|
148
|
+
- Form đăng ký
|
|
149
|
+
- Footer
|
|
150
|
+
```
|
|
207
151
|
|
|
208
|
-
|
|
152
|
+
**AI Agent tự động:**
|
|
153
|
+
- Title: "Landing Page Khóa Học Lập Trình"
|
|
154
|
+
- Slug: "landing-page-khoa-hoc-lap-trinh"
|
|
155
|
+
- Deploy và trả về URL
|
|
209
156
|
|
|
210
|
-
###
|
|
157
|
+
### Ví dụ 3: Portfolio cá nhân
|
|
211
158
|
|
|
159
|
+
**Prompt của user:**
|
|
212
160
|
```
|
|
213
|
-
|
|
214
|
-
-
|
|
215
|
-
-
|
|
216
|
-
-
|
|
161
|
+
Tạo portfolio website cho developer với:
|
|
162
|
+
- About me section
|
|
163
|
+
- Skills với progress bars
|
|
164
|
+
- Projects showcase với images
|
|
165
|
+
- Contact form
|
|
166
|
+
- Dark mode toggle (dùng Alpine.js)
|
|
217
167
|
```
|
|
218
168
|
|
|
219
|
-
|
|
169
|
+
**AI Agent tự động:**
|
|
170
|
+
- Title: "Portfolio Developer"
|
|
171
|
+
- Slug: "portfolio-developer"
|
|
172
|
+
- Thêm Alpine.js CDN cho dark mode
|
|
173
|
+
- Deploy và trả về URL
|
|
220
174
|
|
|
221
|
-
|
|
222
|
-
- TailwindCSS CDN link trong `<head>`
|
|
223
|
-
- TailwindCSS utility classes cho styling
|
|
224
|
-
- JavaScript libraries qua CDN nếu cần (Alpine.js, Chart.js, etc.)
|
|
225
|
-
- Vanilla JavaScript cũng được (không dùng SPA frameworks như React/Vue/Angular)
|
|
226
|
-
|
|
227
|
-
### List các pages
|
|
175
|
+
### Liệt kê các pages đã tạo
|
|
228
176
|
|
|
229
177
|
```
|
|
230
|
-
Hãy liệt kê tất cả các
|
|
178
|
+
Hãy liệt kê tất cả các trang web mà tôi đã deploy
|
|
231
179
|
```
|
|
232
180
|
|
|
233
|
-
Claude sẽ
|
|
181
|
+
Claude sẽ hiển thị danh sách pages với title, slug và thời gian tạo/cập nhật.
|
|
234
182
|
|
|
235
|
-
##
|
|
183
|
+
## Technical Requirements
|
|
236
184
|
|
|
237
|
-
|
|
185
|
+
**HTML Structure:**
|
|
186
|
+
- Complete HTML5 document
|
|
187
|
+
- TailwindCSS CDN: `https://cdn.tailwindcss.com`
|
|
188
|
+
- JavaScript libraries via CDN only (Alpine.js, Chart.js, GSAP, etc.)
|
|
189
|
+
- No build tools, npm packages, or SPA frameworks
|
|
238
190
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
191
|
+
**Example HTML Template:**
|
|
192
|
+
```html
|
|
193
|
+
<!DOCTYPE html>
|
|
194
|
+
<html lang="vi">
|
|
195
|
+
<head>
|
|
196
|
+
<meta charset="UTF-8">
|
|
197
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
198
|
+
<title>Your Title</title>
|
|
199
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
200
|
+
<!-- Additional CDN libraries if needed -->
|
|
201
|
+
</head>
|
|
202
|
+
<body class="bg-gray-100">
|
|
203
|
+
<!-- Your content with Tailwind classes -->
|
|
204
|
+
<script>
|
|
205
|
+
// Your vanilla JavaScript
|
|
206
|
+
</script>
|
|
207
|
+
</body>
|
|
208
|
+
</html>
|
|
243
209
|
```
|
|
244
210
|
|
|
245
|
-
Sẽ tự động rebuild khi có thay đổi trong source code.
|
|
246
|
-
|
|
247
211
|
## Troubleshooting
|
|
248
212
|
|
|
249
|
-
### MCP server không xuất hiện
|
|
213
|
+
### MCP server không xuất hiện
|
|
250
214
|
|
|
251
|
-
1. Kiểm tra
|
|
252
|
-
2.
|
|
253
|
-
3.
|
|
254
|
-
4.
|
|
215
|
+
1. Kiểm tra config JSON format đúng
|
|
216
|
+
2. Verify API_KEY trong env
|
|
217
|
+
3. Restart Claude Desktop / VS Code
|
|
218
|
+
4. Check console logs: Claude Desktop → View → Toggle Developer Tools
|
|
255
219
|
|
|
256
220
|
### API Key không hoạt động
|
|
257
221
|
|
|
258
|
-
|
|
259
|
-
2. Kiểm tra API key còn valid bằng cách call API trực tiếp:
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
curl -X GET https://pages.react.uat.gearvn.xyz/v1/pages \
|
|
263
|
-
-H "X-API-Key: your-api-key"
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### Permission denied khi chạy
|
|
267
|
-
|
|
268
|
-
```bash
|
|
269
|
-
chmod +x dist/index.js
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
## Cấu trúc project
|
|
273
|
-
|
|
274
|
-
```
|
|
275
|
-
mcp-server/
|
|
276
|
-
├── src/
|
|
277
|
-
│ └── index.ts # MCP server implementation
|
|
278
|
-
├── dist/ # Compiled JavaScript (generated)
|
|
279
|
-
├── package.json
|
|
280
|
-
├── tsconfig.json
|
|
281
|
-
└── README.md
|
|
282
|
-
```
|
|
222
|
+
Liên hệ admin để verify API key còn active.
|
|
283
223
|
|
|
284
224
|
## License
|
|
285
225
|
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAQA,wBAAsB,OAAO,CAAC,CAAC,EAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAc,EACtB,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAsBZ"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const API_BASE_URL = "https://pages.react.uat.gearvn.xyz";
|
|
2
|
+
const API_KEY = process.env.API_KEY;
|
|
3
|
+
if (!API_KEY) {
|
|
4
|
+
console.error("Error: API_KEY environment variable is required");
|
|
5
|
+
process.exit(1);
|
|
6
|
+
}
|
|
7
|
+
export async function callAPI(endpoint, method = "GET", body) {
|
|
8
|
+
const url = `${API_BASE_URL}${endpoint}`;
|
|
9
|
+
const headers = {
|
|
10
|
+
"X-API-Key": API_KEY,
|
|
11
|
+
};
|
|
12
|
+
if (body) {
|
|
13
|
+
headers["Content-Type"] = "application/json";
|
|
14
|
+
}
|
|
15
|
+
const response = await fetch(url, {
|
|
16
|
+
method,
|
|
17
|
+
headers,
|
|
18
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const errorText = await response.text();
|
|
22
|
+
throw new Error(`API request failed: ${response.status} ${errorText}`);
|
|
23
|
+
}
|
|
24
|
+
return response.json();
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,oCAAoC,CAAC;AAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;AAEpC,IAAI,CAAC,OAAO,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACjE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,QAAgB,EAChB,SAAiB,KAAK,EACtB,IAAc;IAEd,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,QAAQ,EAAE,CAAC;IACzC,MAAM,OAAO,GAA2B;QACtC,WAAW,EAAE,OAAQ;KACtB,CAAC;IAEF,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;IAC/C,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAChC,MAAM;QACN,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9C,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;AACvC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,64 +2,8 @@
|
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const API_KEY = process.env.API_KEY;
|
|
8
|
-
if (!API_KEY) {
|
|
9
|
-
console.error("Error: API_KEY environment variable is required");
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
// API helper function
|
|
13
|
-
async function callAPI(endpoint, method = "GET", body) {
|
|
14
|
-
const url = `${API_BASE_URL}${endpoint}`;
|
|
15
|
-
const headers = {
|
|
16
|
-
"X-API-Key": API_KEY,
|
|
17
|
-
};
|
|
18
|
-
if (body) {
|
|
19
|
-
headers["Content-Type"] = "application/json";
|
|
20
|
-
}
|
|
21
|
-
const response = await fetch(url, {
|
|
22
|
-
method,
|
|
23
|
-
headers,
|
|
24
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
25
|
-
});
|
|
26
|
-
if (!response.ok) {
|
|
27
|
-
const errorText = await response.text();
|
|
28
|
-
throw new Error(`API request failed: ${response.status} ${errorText}`);
|
|
29
|
-
}
|
|
30
|
-
return response.json();
|
|
31
|
-
}
|
|
32
|
-
// Tool definitions
|
|
33
|
-
const DEPLOY_PAGE_TOOL = {
|
|
34
|
-
name: "deploy-page",
|
|
35
|
-
description: "Deploy or update a static HTML page. If the slug already exists, the page will be updated with the new content and title. IMPORTANT: The content must be a complete, valid HTML document with TailwindCSS for styling. JavaScript libraries are allowed but MUST be loaded via CDN (e.g., Alpine.js, Chart.js, GSAP, etc.). No build tools or npm packages. No React, Vue, or Angular frameworks allowed.",
|
|
36
|
-
inputSchema: {
|
|
37
|
-
type: "object",
|
|
38
|
-
properties: {
|
|
39
|
-
title: {
|
|
40
|
-
type: "string",
|
|
41
|
-
description: "The title of the page",
|
|
42
|
-
},
|
|
43
|
-
content: {
|
|
44
|
-
type: "string",
|
|
45
|
-
description: "The complete HTML content of the page. Must include <!DOCTYPE html>, <html>, <head> with TailwindCSS CDN link (https://cdn.tailwindcss.com), and <body> tags. Use TailwindCSS utility classes for all styling. JavaScript libraries are allowed but must be imported via CDN links in <script> tags (e.g., Alpine.js from CDN, Chart.js from CDN). Vanilla JavaScript is also acceptable. NO build tools, npm packages, or SPA frameworks (React/Vue/Angular).",
|
|
46
|
-
},
|
|
47
|
-
slug: {
|
|
48
|
-
type: "string",
|
|
49
|
-
description: "The URL slug for the page (e.g., 'my-page-123'). This will be used in the URL as /pages/{slug}",
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
required: ["title", "content", "slug"],
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
const LIST_PAGES_TOOL = {
|
|
56
|
-
name: "list-pages",
|
|
57
|
-
description: "List all pages associated with your API key. Returns a summary of each page including id, slug, title, and timestamps. Does not include page content.",
|
|
58
|
-
inputSchema: {
|
|
59
|
-
type: "object",
|
|
60
|
-
properties: {},
|
|
61
|
-
},
|
|
62
|
-
};
|
|
5
|
+
import { DEPLOY_PAGE_TOOL, handleDeployPage } from "./tools/deploy-page.js";
|
|
6
|
+
import { LIST_PAGES_TOOL, handleListPages } from "./tools/list-pages.js";
|
|
63
7
|
// Create MCP server
|
|
64
8
|
const server = new Server({
|
|
65
9
|
name: "gearvn-pages-mcp-server",
|
|
@@ -80,45 +24,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
80
24
|
const { name, arguments: args } = request.params;
|
|
81
25
|
try {
|
|
82
26
|
if (name === "deploy-page") {
|
|
83
|
-
|
|
84
|
-
// Validate inputs
|
|
85
|
-
if (!title || !content || !slug) {
|
|
86
|
-
throw new Error("Missing required parameters: title, content, and slug are required");
|
|
87
|
-
}
|
|
88
|
-
// Call the deploy API
|
|
89
|
-
const response = await callAPI("/v1/deploy", "POST", {
|
|
90
|
-
title,
|
|
91
|
-
content,
|
|
92
|
-
slug,
|
|
93
|
-
});
|
|
94
|
-
return {
|
|
95
|
-
content: [
|
|
96
|
-
{
|
|
97
|
-
type: "text",
|
|
98
|
-
text: JSON.stringify({
|
|
99
|
-
success: true,
|
|
100
|
-
message: "Page deployed successfully",
|
|
101
|
-
url: response.url,
|
|
102
|
-
}, null, 2),
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
};
|
|
27
|
+
return await handleDeployPage(args);
|
|
106
28
|
}
|
|
107
29
|
else if (name === "list-pages") {
|
|
108
|
-
|
|
109
|
-
const response = await callAPI("/v1/pages", "GET");
|
|
110
|
-
return {
|
|
111
|
-
content: [
|
|
112
|
-
{
|
|
113
|
-
type: "text",
|
|
114
|
-
text: JSON.stringify({
|
|
115
|
-
success: true,
|
|
116
|
-
count: response.count,
|
|
117
|
-
pages: response.pages,
|
|
118
|
-
}, null, 2),
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
};
|
|
30
|
+
return await handleListPages();
|
|
122
31
|
}
|
|
123
32
|
else {
|
|
124
33
|
throw new Error(`Unknown tool: ${name}`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzE,oBAAoB;AACpB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,yBAAyB;IAC/B,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,sCAAsC;AACtC,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;IAC1D,OAAO;QACL,KAAK,EAAE,CAAC,gBAAgB,EAAE,eAAe,CAAC;KAC3C,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,yBAAyB;AACzB,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAEjD,IAAI,CAAC;QACH,IAAI,IAAI,KAAK,aAAa,EAAE,CAAC;YAC3B,OAAO,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YACjC,OAAO,MAAM,eAAe,EAAE,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;wBACE,OAAO,EAAE,KAAK;wBACd,KAAK,EAAE,YAAY;qBACpB,EACD,IAAI,EACJ,CAAC,CACF;iBACF;aACF;YACD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,mBAAmB;AACnB,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;AAC5D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export declare const DEPLOY_PAGE_TOOL: Tool;
|
|
3
|
+
export declare function handleDeployPage(args: unknown): Promise<{
|
|
4
|
+
content: {
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
}[];
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=deploy-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-page.d.ts","sourceRoot":"","sources":["../../src/tools/deploy-page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAI1D,eAAO,MAAM,gBAAgB,EAAE,IAyB9B,CAAC;AAEF,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,OAAO;;;;;GAiCnD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { callAPI } from "../api.js";
|
|
2
|
+
export const DEPLOY_PAGE_TOOL = {
|
|
3
|
+
name: "deploy-page",
|
|
4
|
+
description: "Deploy or update a static HTML page. If the slug already exists, the page will be updated with the new content and title. IMPORTANT: You (AI Agent) must automatically generate appropriate 'title' and 'slug' based on the user's request - user does NOT provide these. The content must be a complete, valid HTML document with TailwindCSS for styling. JavaScript libraries are allowed but MUST be loaded via CDN (e.g., Alpine.js, Chart.js, GSAP, etc.). No build tools or npm packages. No React, Vue, or Angular frameworks allowed.",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {
|
|
8
|
+
title: {
|
|
9
|
+
type: "string",
|
|
10
|
+
description: "The title of the page. AI Agent should generate this automatically based on user's request.",
|
|
11
|
+
},
|
|
12
|
+
content: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "The complete HTML content of the page. Must include <!DOCTYPE html>, <html>, <head> with TailwindCSS CDN link (https://cdn.tailwindcss.com), and <body> tags. Use TailwindCSS utility classes for all styling. JavaScript libraries are allowed but must be imported via CDN links in <script> tags (e.g., Alpine.js from CDN, Chart.js from CDN). Vanilla JavaScript is also acceptable. NO build tools, npm packages, or SPA frameworks (React/Vue/Angular).",
|
|
15
|
+
},
|
|
16
|
+
slug: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "The URL slug for the page. AI Agent should generate this automatically in kebab-case format based on the title or user's request (e.g., 'quan-ly-thu-chi-ca-nhan', 'landing-page-khoa-hoc'). This will be used in the URL as /pages/{slug}",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
required: ["title", "content", "slug"],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export async function handleDeployPage(args) {
|
|
25
|
+
const { title, content, slug } = args;
|
|
26
|
+
// Validate inputs
|
|
27
|
+
if (!title || !content || !slug) {
|
|
28
|
+
throw new Error("Missing required parameters: title, content, and slug are required");
|
|
29
|
+
}
|
|
30
|
+
// Call the deploy API
|
|
31
|
+
const response = await callAPI("/v1/deploy", "POST", {
|
|
32
|
+
title,
|
|
33
|
+
content,
|
|
34
|
+
slug,
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: JSON.stringify({
|
|
41
|
+
success: true,
|
|
42
|
+
message: "Page deployed successfully",
|
|
43
|
+
url: response.url,
|
|
44
|
+
}, null, 2),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=deploy-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy-page.js","sourceRoot":"","sources":["../../src/tools/deploy-page.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,ghBAAghB;IAClhB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,6FAA6F;aAChG;YACD,OAAO,EAAE;gBACP,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,gcAAgc;aACnc;YACD,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,4OAA4O;aAC/O;SACF;QACD,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC;KACvC;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAa;IAClD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,IAAsB,CAAC;IAExD,kBAAkB;IAClB,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAqB,YAAY,EAAE,MAAM,EAAE;QACvE,KAAK;QACL,OAAO;QACP,IAAI;KACL,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;oBACE,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE,4BAA4B;oBACrC,GAAG,EAAE,QAAQ,CAAC,GAAG;iBAClB,EACD,IAAI,EACJ,CAAC,CACF;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-pages.d.ts","sourceRoot":"","sources":["../../src/tools/list-pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAI1D,eAAO,MAAM,eAAe,EAAE,IAQ7B,CAAC;AAEF,wBAAsB,eAAe;;;;;GAoBpC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { callAPI } from "../api.js";
|
|
2
|
+
export const LIST_PAGES_TOOL = {
|
|
3
|
+
name: "list-pages",
|
|
4
|
+
description: "List all pages associated with your API key. Returns a summary of each page including id, slug, title, and timestamps. Does not include page content.",
|
|
5
|
+
inputSchema: {
|
|
6
|
+
type: "object",
|
|
7
|
+
properties: {},
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
export async function handleListPages() {
|
|
11
|
+
// Call the list pages API
|
|
12
|
+
const response = await callAPI("/v1/pages", "GET");
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: JSON.stringify({
|
|
18
|
+
success: true,
|
|
19
|
+
count: response.count,
|
|
20
|
+
pages: response.pages,
|
|
21
|
+
}, null, 2),
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=list-pages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-pages.js","sourceRoot":"","sources":["../../src/tools/list-pages.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,CAAC,MAAM,eAAe,GAAS;IACnC,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,uJAAuJ;IACzJ,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE;KACf;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAoB,WAAW,EAAE,KAAK,CAAC,CAAC;IAEtE,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;oBACE,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,QAAQ,CAAC,KAAK;oBACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;iBACtB,EACD,IAAI,EACJ,CAAC,CACF;aACF;SACF;KACF,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface DeployPageArgs {
|
|
2
|
+
title: string;
|
|
3
|
+
content: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
}
|
|
6
|
+
export interface DeployPageResponse {
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PageSummary {
|
|
10
|
+
id: number;
|
|
11
|
+
slug: string;
|
|
12
|
+
title: string;
|
|
13
|
+
created_at: string;
|
|
14
|
+
updated_at: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ListPagesResponse {
|
|
17
|
+
count: number;
|
|
18
|
+
pages: PageSummary[];
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
package/src/api.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const API_BASE_URL = "https://pages.react.uat.gearvn.xyz";
|
|
2
|
+
const API_KEY = process.env.API_KEY;
|
|
3
|
+
|
|
4
|
+
if (!API_KEY) {
|
|
5
|
+
console.error("Error: API_KEY environment variable is required");
|
|
6
|
+
process.exit(1);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function callAPI<T>(
|
|
10
|
+
endpoint: string,
|
|
11
|
+
method: string = "GET",
|
|
12
|
+
body?: unknown
|
|
13
|
+
): Promise<T> {
|
|
14
|
+
const url = `${API_BASE_URL}${endpoint}`;
|
|
15
|
+
const headers: Record<string, string> = {
|
|
16
|
+
"X-API-Key": API_KEY!,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (body) {
|
|
20
|
+
headers["Content-Type"] = "application/json";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const response = await fetch(url, {
|
|
24
|
+
method,
|
|
25
|
+
headers,
|
|
26
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
const errorText = await response.text();
|
|
31
|
+
throw new Error(`API request failed: ${response.status} ${errorText}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return response.json() as Promise<T>;
|
|
35
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,106 +5,9 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
5
5
|
import {
|
|
6
6
|
CallToolRequestSchema,
|
|
7
7
|
ListToolsRequestSchema,
|
|
8
|
-
Tool,
|
|
9
8
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const API_BASE_URL = "https://pages.react.uat.gearvn.xyz";
|
|
13
|
-
const API_KEY = process.env.API_KEY;
|
|
14
|
-
|
|
15
|
-
if (!API_KEY) {
|
|
16
|
-
console.error("Error: API_KEY environment variable is required");
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Types
|
|
21
|
-
interface DeployPageArgs {
|
|
22
|
-
title: string;
|
|
23
|
-
content: string;
|
|
24
|
-
slug: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface DeployPageResponse {
|
|
28
|
-
url: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface PageSummary {
|
|
32
|
-
id: number;
|
|
33
|
-
slug: string;
|
|
34
|
-
title: string;
|
|
35
|
-
created_at: string;
|
|
36
|
-
updated_at: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
interface ListPagesResponse {
|
|
40
|
-
count: number;
|
|
41
|
-
pages: PageSummary[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// API helper function
|
|
45
|
-
async function callAPI<T>(
|
|
46
|
-
endpoint: string,
|
|
47
|
-
method: string = "GET",
|
|
48
|
-
body?: unknown
|
|
49
|
-
): Promise<T> {
|
|
50
|
-
const url = `${API_BASE_URL}${endpoint}`;
|
|
51
|
-
const headers: Record<string, string> = {
|
|
52
|
-
"X-API-Key": API_KEY!,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
if (body) {
|
|
56
|
-
headers["Content-Type"] = "application/json";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const response = await fetch(url, {
|
|
60
|
-
method,
|
|
61
|
-
headers,
|
|
62
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
if (!response.ok) {
|
|
66
|
-
const errorText = await response.text();
|
|
67
|
-
throw new Error(`API request failed: ${response.status} ${errorText}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return response.json() as Promise<T>;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Tool definitions
|
|
74
|
-
const DEPLOY_PAGE_TOOL: Tool = {
|
|
75
|
-
name: "deploy-page",
|
|
76
|
-
description:
|
|
77
|
-
"Deploy or update a static HTML page. If the slug already exists, the page will be updated with the new content and title. IMPORTANT: The content must be a complete, valid HTML document with TailwindCSS for styling. JavaScript libraries are allowed but MUST be loaded via CDN (e.g., Alpine.js, Chart.js, GSAP, etc.). No build tools or npm packages. No React, Vue, or Angular frameworks allowed.",
|
|
78
|
-
inputSchema: {
|
|
79
|
-
type: "object",
|
|
80
|
-
properties: {
|
|
81
|
-
title: {
|
|
82
|
-
type: "string",
|
|
83
|
-
description: "The title of the page",
|
|
84
|
-
},
|
|
85
|
-
content: {
|
|
86
|
-
type: "string",
|
|
87
|
-
description: "The complete HTML content of the page. Must include <!DOCTYPE html>, <html>, <head> with TailwindCSS CDN link (https://cdn.tailwindcss.com), and <body> tags. Use TailwindCSS utility classes for all styling. JavaScript libraries are allowed but must be imported via CDN links in <script> tags (e.g., Alpine.js from CDN, Chart.js from CDN). Vanilla JavaScript is also acceptable. NO build tools, npm packages, or SPA frameworks (React/Vue/Angular).",
|
|
88
|
-
},
|
|
89
|
-
slug: {
|
|
90
|
-
type: "string",
|
|
91
|
-
description:
|
|
92
|
-
"The URL slug for the page (e.g., 'my-page-123'). This will be used in the URL as /pages/{slug}",
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
required: ["title", "content", "slug"],
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const LIST_PAGES_TOOL: Tool = {
|
|
100
|
-
name: "list-pages",
|
|
101
|
-
description:
|
|
102
|
-
"List all pages associated with your API key. Returns a summary of each page including id, slug, title, and timestamps. Does not include page content.",
|
|
103
|
-
inputSchema: {
|
|
104
|
-
type: "object",
|
|
105
|
-
properties: {},
|
|
106
|
-
},
|
|
107
|
-
};
|
|
9
|
+
import { DEPLOY_PAGE_TOOL, handleDeployPage } from "./tools/deploy-page.js";
|
|
10
|
+
import { LIST_PAGES_TOOL, handleListPages } from "./tools/list-pages.js";
|
|
108
11
|
|
|
109
12
|
// Create MCP server
|
|
110
13
|
const server = new Server(
|
|
@@ -132,56 +35,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
132
35
|
|
|
133
36
|
try {
|
|
134
37
|
if (name === "deploy-page") {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Validate inputs
|
|
138
|
-
if (!title || !content || !slug) {
|
|
139
|
-
throw new Error("Missing required parameters: title, content, and slug are required");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Call the deploy API
|
|
143
|
-
const response = await callAPI<DeployPageResponse>("/v1/deploy", "POST", {
|
|
144
|
-
title,
|
|
145
|
-
content,
|
|
146
|
-
slug,
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
return {
|
|
150
|
-
content: [
|
|
151
|
-
{
|
|
152
|
-
type: "text",
|
|
153
|
-
text: JSON.stringify(
|
|
154
|
-
{
|
|
155
|
-
success: true,
|
|
156
|
-
message: "Page deployed successfully",
|
|
157
|
-
url: response.url,
|
|
158
|
-
},
|
|
159
|
-
null,
|
|
160
|
-
2
|
|
161
|
-
),
|
|
162
|
-
},
|
|
163
|
-
],
|
|
164
|
-
};
|
|
38
|
+
return await handleDeployPage(args);
|
|
165
39
|
} else if (name === "list-pages") {
|
|
166
|
-
|
|
167
|
-
const response = await callAPI<ListPagesResponse>("/v1/pages", "GET");
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
content: [
|
|
171
|
-
{
|
|
172
|
-
type: "text",
|
|
173
|
-
text: JSON.stringify(
|
|
174
|
-
{
|
|
175
|
-
success: true,
|
|
176
|
-
count: response.count,
|
|
177
|
-
pages: response.pages,
|
|
178
|
-
},
|
|
179
|
-
null,
|
|
180
|
-
2
|
|
181
|
-
),
|
|
182
|
-
},
|
|
183
|
-
],
|
|
184
|
-
};
|
|
40
|
+
return await handleListPages();
|
|
185
41
|
} else {
|
|
186
42
|
throw new Error(`Unknown tool: ${name}`);
|
|
187
43
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { callAPI } from "../api.js";
|
|
3
|
+
import { DeployPageArgs, DeployPageResponse } from "../types.js";
|
|
4
|
+
|
|
5
|
+
export const DEPLOY_PAGE_TOOL: Tool = {
|
|
6
|
+
name: "deploy-page",
|
|
7
|
+
description:
|
|
8
|
+
"Deploy or update a static HTML page. If the slug already exists, the page will be updated with the new content and title. IMPORTANT: You (AI Agent) must automatically generate appropriate 'title' and 'slug' based on the user's request - user does NOT provide these. The content must be a complete, valid HTML document with TailwindCSS for styling. JavaScript libraries are allowed but MUST be loaded via CDN (e.g., Alpine.js, Chart.js, GSAP, etc.). No build tools or npm packages. No React, Vue, or Angular frameworks allowed.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
title: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description:
|
|
15
|
+
"The title of the page. AI Agent should generate this automatically based on user's request.",
|
|
16
|
+
},
|
|
17
|
+
content: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description:
|
|
20
|
+
"The complete HTML content of the page. Must include <!DOCTYPE html>, <html>, <head> with TailwindCSS CDN link (https://cdn.tailwindcss.com), and <body> tags. Use TailwindCSS utility classes for all styling. JavaScript libraries are allowed but must be imported via CDN links in <script> tags (e.g., Alpine.js from CDN, Chart.js from CDN). Vanilla JavaScript is also acceptable. NO build tools, npm packages, or SPA frameworks (React/Vue/Angular).",
|
|
21
|
+
},
|
|
22
|
+
slug: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description:
|
|
25
|
+
"The URL slug for the page. AI Agent should generate this automatically in kebab-case format based on the title or user's request (e.g., 'quan-ly-thu-chi-ca-nhan', 'landing-page-khoa-hoc'). This will be used in the URL as /pages/{slug}",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ["title", "content", "slug"],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export async function handleDeployPage(args: unknown) {
|
|
33
|
+
const { title, content, slug } = args as DeployPageArgs;
|
|
34
|
+
|
|
35
|
+
// Validate inputs
|
|
36
|
+
if (!title || !content || !slug) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"Missing required parameters: title, content, and slug are required"
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Call the deploy API
|
|
43
|
+
const response = await callAPI<DeployPageResponse>("/v1/deploy", "POST", {
|
|
44
|
+
title,
|
|
45
|
+
content,
|
|
46
|
+
slug,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: "text" as const,
|
|
53
|
+
text: JSON.stringify(
|
|
54
|
+
{
|
|
55
|
+
success: true,
|
|
56
|
+
message: "Page deployed successfully",
|
|
57
|
+
url: response.url,
|
|
58
|
+
},
|
|
59
|
+
null,
|
|
60
|
+
2
|
|
61
|
+
),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { callAPI } from "../api.js";
|
|
3
|
+
import { ListPagesResponse } from "../types.js";
|
|
4
|
+
|
|
5
|
+
export const LIST_PAGES_TOOL: Tool = {
|
|
6
|
+
name: "list-pages",
|
|
7
|
+
description:
|
|
8
|
+
"List all pages associated with your API key. Returns a summary of each page including id, slug, title, and timestamps. Does not include page content.",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {},
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export async function handleListPages() {
|
|
16
|
+
// Call the list pages API
|
|
17
|
+
const response = await callAPI<ListPagesResponse>("/v1/pages", "GET");
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text" as const,
|
|
23
|
+
text: JSON.stringify(
|
|
24
|
+
{
|
|
25
|
+
success: true,
|
|
26
|
+
count: response.count,
|
|
27
|
+
pages: response.pages,
|
|
28
|
+
},
|
|
29
|
+
null,
|
|
30
|
+
2
|
|
31
|
+
),
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface DeployPageArgs {
|
|
2
|
+
title: string;
|
|
3
|
+
content: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface DeployPageResponse {
|
|
8
|
+
url: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface PageSummary {
|
|
12
|
+
id: number;
|
|
13
|
+
slug: string;
|
|
14
|
+
title: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
updated_at: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ListPagesResponse {
|
|
20
|
+
count: number;
|
|
21
|
+
pages: PageSummary[];
|
|
22
|
+
}
|