gearvn-pages-mcp-server 1.0.0 → 1.1.1
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 +134 -194
- package/dist/api.d.ts +2 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +39 -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 +7 -8
- package/src/api.ts +52 -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,103 +1,20 @@
|
|
|
1
1
|
# GearVN Pages MCP Server
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) Server cho việc quản lý
|
|
4
|
-
|
|
5
|
-
## Tính năng
|
|
6
|
-
|
|
7
|
-
MCP server này cung cấp 2 tools:
|
|
8
|
-
|
|
9
|
-
### 1. deploy-page
|
|
10
|
-
Deploy hoặc update một trang HTML tĩnh.
|
|
11
|
-
|
|
12
|
-
**Parameters:**
|
|
13
|
-
- `title` (string, required): Tiêu đề của trang
|
|
14
|
-
- `content` (string, required): Nội dung HTML đầy đủ của trang. Phải bao gồm complete HTML document với TailwindCSS CDN. JavaScript libraries được phép nhưng phải import qua CDN (ví dụ: Alpine.js, Chart.js, GSAP). Không dùng React, Vue, Angular hoặc build tools
|
|
15
|
-
- `slug` (string, required): URL slug cho trang (ví dụ: "my-page-123")
|
|
16
|
-
|
|
17
|
-
**Response:**
|
|
18
|
-
```json
|
|
19
|
-
{
|
|
20
|
-
"success": true,
|
|
21
|
-
"message": "Page deployed successfully",
|
|
22
|
-
"url": "https://pages.react.uat.gearvn.xyz/pages/my-page-123"
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
### 2. list-pages
|
|
27
|
-
Liệt kê tất cả các pages của API key hiện tại.
|
|
28
|
-
|
|
29
|
-
**Parameters:** Không có
|
|
30
|
-
|
|
31
|
-
**Response:**
|
|
32
|
-
```json
|
|
33
|
-
{
|
|
34
|
-
"success": true,
|
|
35
|
-
"count": 2,
|
|
36
|
-
"pages": [
|
|
37
|
-
{
|
|
38
|
-
"id": 1,
|
|
39
|
-
"slug": "my-page-123",
|
|
40
|
-
"title": "My Page",
|
|
41
|
-
"created_at": "2025-01-20T10:00:00Z",
|
|
42
|
-
"updated_at": "2025-01-20T10:00:00Z"
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
```
|
|
3
|
+
MCP (Model Context Protocol) Server cho việc deploy và quản lý các trang HTML tĩnh lên GearVN Pages.
|
|
47
4
|
|
|
48
5
|
## Cài đặt
|
|
49
6
|
|
|
50
7
|
### 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
|
|
8
|
+
- Node.js 18+
|
|
9
|
+
- API key từ GearVN Pages (liên hệ admin để lấy)
|
|
79
10
|
|
|
80
|
-
Sử dụng
|
|
11
|
+
### Sử dụng với Claude Desktop
|
|
81
12
|
|
|
82
|
-
|
|
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.
|
|
89
|
-
|
|
90
|
-
## Sử dụng với Claude Desktop
|
|
91
|
-
|
|
92
|
-
### Bước 1: Thêm vào Claude Desktop config
|
|
93
|
-
|
|
94
|
-
Mở file config của Claude Desktop:
|
|
13
|
+
**Bước 1: Mở file config**
|
|
95
14
|
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
96
15
|
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
97
16
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
**Cách 1: Sử dụng npx (Khuyến nghị)**
|
|
17
|
+
**Bước 2: Thêm MCP server config**
|
|
101
18
|
|
|
102
19
|
```json
|
|
103
20
|
{
|
|
@@ -106,7 +23,7 @@ Mở file config của Claude Desktop:
|
|
|
106
23
|
"command": "npx",
|
|
107
24
|
"args": [
|
|
108
25
|
"-y",
|
|
109
|
-
"
|
|
26
|
+
"gearvn-pages-mcp-server"
|
|
110
27
|
],
|
|
111
28
|
"env": {
|
|
112
29
|
"API_KEY": "sk-your-api-key-here"
|
|
@@ -116,15 +33,25 @@ Mở file config của Claude Desktop:
|
|
|
116
33
|
}
|
|
117
34
|
```
|
|
118
35
|
|
|
119
|
-
**
|
|
36
|
+
**Bước 3: Restart Claude Desktop**
|
|
37
|
+
|
|
38
|
+
### Sử dụng với Cline (VS Code)
|
|
39
|
+
|
|
40
|
+
**Bước 1: Mở VS Code Settings**
|
|
41
|
+
|
|
42
|
+
1. Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
|
|
43
|
+
2. "Preferences: Open User Settings (JSON)"
|
|
44
|
+
|
|
45
|
+
**Bước 2: Thêm config**
|
|
120
46
|
|
|
121
47
|
```json
|
|
122
48
|
{
|
|
123
|
-
"mcpServers": {
|
|
49
|
+
"cline.mcpServers": {
|
|
124
50
|
"gearvn-pages": {
|
|
125
|
-
"command": "
|
|
51
|
+
"command": "npx",
|
|
126
52
|
"args": [
|
|
127
|
-
"
|
|
53
|
+
"-y",
|
|
54
|
+
"gearvn-pages-mcp-server"
|
|
128
55
|
],
|
|
129
56
|
"env": {
|
|
130
57
|
"API_KEY": "sk-your-api-key-here"
|
|
@@ -134,152 +61,165 @@ Mở file config của Claude Desktop:
|
|
|
134
61
|
}
|
|
135
62
|
```
|
|
136
63
|
|
|
137
|
-
**
|
|
138
|
-
|
|
139
|
-
### Bước 3: Restart Claude Desktop
|
|
64
|
+
**Bước 3: Reload VS Code**
|
|
140
65
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
## Sử dụng với Cline (VS Code Extension)
|
|
144
|
-
|
|
145
|
-
### Bước 1: Mở VS Code Settings
|
|
146
|
-
|
|
147
|
-
1. Mở Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
|
|
148
|
-
2. Gõ "Preferences: Open User Settings (JSON)"
|
|
66
|
+
## Tính năng
|
|
149
67
|
|
|
150
|
-
###
|
|
68
|
+
### 1. deploy-page
|
|
69
|
+
Deploy hoặc update một trang HTML tĩnh.
|
|
151
70
|
|
|
152
|
-
|
|
71
|
+
**Parameters:**
|
|
72
|
+
- `title` (string): Tiêu đề của trang - **AI Agent tự động generate dựa trên nội dung**
|
|
73
|
+
- `content` (string): HTML document đầy đủ với TailwindCSS CDN. JavaScript libraries được phép qua CDN (Alpine.js, Chart.js, GSAP, etc.)
|
|
74
|
+
- `slug` (string): URL slug - **AI Agent tự động generate theo format kebab-case từ title hoặc nội dung**
|
|
153
75
|
|
|
154
|
-
**
|
|
76
|
+
**Lưu ý quan trọng:**
|
|
77
|
+
- User **KHÔNG cần** cung cấp `title` và `slug`
|
|
78
|
+
- AI Agent sẽ tự động generate dựa trên yêu cầu của user
|
|
79
|
+
- Ví dụ: "tạo website quản lý thu chi" → title: "Quản Lý Thu Chi", slug: "quan-ly-thu-chi"
|
|
155
80
|
|
|
81
|
+
**Response:**
|
|
156
82
|
```json
|
|
157
83
|
{
|
|
158
|
-
"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"args": [
|
|
162
|
-
"-y",
|
|
163
|
-
"/absolute/path/to/backend-webpage-go/mcp-server"
|
|
164
|
-
],
|
|
165
|
-
"env": {
|
|
166
|
-
"API_KEY": "sk-your-api-key-here"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
84
|
+
"success": true,
|
|
85
|
+
"message": "Page deployed successfully",
|
|
86
|
+
"url": "https://pages.react.uat.gearvn.xyz/pages/my-page-123"
|
|
170
87
|
}
|
|
171
88
|
```
|
|
172
89
|
|
|
173
|
-
|
|
90
|
+
### 2. list-pages
|
|
91
|
+
Liệt kê tất cả các pages của API key.
|
|
174
92
|
|
|
93
|
+
**Response:**
|
|
175
94
|
```json
|
|
176
95
|
{
|
|
177
|
-
"
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
"
|
|
184
|
-
|
|
185
|
-
|
|
96
|
+
"success": true,
|
|
97
|
+
"count": 2,
|
|
98
|
+
"pages": [
|
|
99
|
+
{
|
|
100
|
+
"id": 1,
|
|
101
|
+
"slug": "my-page-123",
|
|
102
|
+
"title": "My Page",
|
|
103
|
+
"created_at": "2025-01-20T10:00:00Z",
|
|
104
|
+
"updated_at": "2025-01-20T10:00:00Z"
|
|
186
105
|
}
|
|
187
|
-
|
|
106
|
+
]
|
|
188
107
|
}
|
|
189
108
|
```
|
|
190
109
|
|
|
191
|
-
|
|
110
|
+
## Ví dụ sử dụng trong Claude
|
|
192
111
|
|
|
193
|
-
|
|
112
|
+
### Ví dụ 1: Tạo website quản lý thu chi cá nhân
|
|
194
113
|
|
|
195
|
-
|
|
114
|
+
**Prompt của user (đơn giản):**
|
|
115
|
+
```
|
|
116
|
+
Sử dụng gearvn-pages để: 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
|
+
```
|
|
196
125
|
|
|
197
|
-
|
|
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
|
+
Sử dụng gearvn-pages để: 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
|
+
Sử dụng gearvn-pages để: 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
|
-
|
|
178
|
+
Sử dụng gearvn-pages để: 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,CAuCZ"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
// Get response text first
|
|
21
|
+
const responseText = await response.text();
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
// Check if response is HTML (error page)
|
|
24
|
+
if (responseText.trim().startsWith("<!DOCTYPE") || responseText.trim().startsWith("<html")) {
|
|
25
|
+
throw new Error(`API request failed with status ${response.status}. Server returned HTML instead of JSON. ` +
|
|
26
|
+
`This usually means: 1) Invalid API endpoint, 2) API key authentication failed, or 3) Server error. ` +
|
|
27
|
+
`URL: ${url}`);
|
|
28
|
+
}
|
|
29
|
+
throw new Error(`API request failed: ${response.status} ${responseText}`);
|
|
30
|
+
}
|
|
31
|
+
// Try to parse as JSON
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(responseText);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
throw new Error(`API returned invalid JSON. Response: ${responseText.substring(0, 200)}...`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# 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,0BAA0B;IAC1B,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAE3C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,yCAAyC;QACzC,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,KAAK,CACb,kCAAkC,QAAQ,CAAC,MAAM,0CAA0C;gBAC3F,qGAAqG;gBACrG,QAAQ,GAAG,EAAE,CACd,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,uBAAuB;IACvB,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAM,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,wCAAwC,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAC5E,CAAC;IACJ,CAAC;AACH,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. CRITICAL: When passing HTML content as JSON string, ensure all special characters are properly escaped (double quotes \", backslashes \\, newlines \\n, etc.) to prevent JSON syntax errors.",
|
|
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). IMPORTANT: Properly escape all special characters in HTML for JSON (\" for double quotes, \\\\ for backslashes, \\n for newlines, etc.). Example: <div class=\"container\"> should be passed as <div class=\\\"container\\\">",
|
|
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,6sBAA6sB;IAC/sB,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,8pBAA8pB;aACjqB;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
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gearvn-pages-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "MCP Server for GearVN Pages deployment and management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"gearvn-pages-mcp": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
12
|
-
"watch": "tsc --watch",
|
|
13
|
-
"start": "node dist/index.js",
|
|
14
|
-
"prepublishOnly": "npm run build"
|
|
15
|
-
},
|
|
16
10
|
"keywords": [
|
|
17
11
|
"mcp",
|
|
18
12
|
"model-context-protocol",
|
|
@@ -26,5 +20,10 @@
|
|
|
26
20
|
"devDependencies": {
|
|
27
21
|
"@types/node": "^22.10.2",
|
|
28
22
|
"typescript": "^5.7.2"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"watch": "tsc --watch",
|
|
27
|
+
"start": "node dist/index.js"
|
|
29
28
|
}
|
|
30
|
-
}
|
|
29
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
// Get response text first
|
|
30
|
+
const responseText = await response.text();
|
|
31
|
+
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
// Check if response is HTML (error page)
|
|
34
|
+
if (responseText.trim().startsWith("<!DOCTYPE") || responseText.trim().startsWith("<html")) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`API request failed with status ${response.status}. Server returned HTML instead of JSON. ` +
|
|
37
|
+
`This usually means: 1) Invalid API endpoint, 2) API key authentication failed, or 3) Server error. ` +
|
|
38
|
+
`URL: ${url}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`API request failed: ${response.status} ${responseText}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Try to parse as JSON
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(responseText) as T;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`API returned invalid JSON. Response: ${responseText.substring(0, 200)}...`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
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. CRITICAL: When passing HTML content as JSON string, ensure all special characters are properly escaped (double quotes \", backslashes \\, newlines \\n, etc.) to prevent JSON syntax errors.",
|
|
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). IMPORTANT: Properly escape all special characters in HTML for JSON (\" for double quotes, \\\\ for backslashes, \\n for newlines, etc.). Example: <div class=\"container\"> should be passed as <div class=\\\"container\\\">",
|
|
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
|
+
}
|