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 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ý và deploy các trang HTML tĩnh lên GearVN Pages.
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, 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")
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 hiện tại.
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 hoặc cao hơn
52
- - npm hoặc pnpm
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: Thêm vào Claude Desktop config
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
- "/absolute/path/to/backend-webpage-go/mcp-server"
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
- Sau khi thêm config, restart Claude Desktop để load MCP server.
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. Mở Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
148
- 2. "Preferences: Open User Settings (JSON)"
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
- **Cách 1: Sử dụng npx (Khuyến nghị)**
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
- "/absolute/path/to/backend-webpage-go/mcp-server"
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
- Reload window để Cline load MCP server.
110
+ ## dụ sử dụng trong Claude
194
111
 
195
- ## Test MCP Server
112
+ ### dụ 1: Tạo website quản lý thu chi cá nhân
196
113
 
197
- Bạn thể test MCP server bằng cách chạy trực tiếp:
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
- ```bash
200
- export API_KEY="sk-your-api-key-here"
201
- npm start
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
- Server sẽ chạy trên stdio chờ nhận input theo MCP protocol.
140
+ ### dụ 2: Landing page bán hàng
205
141
 
206
- ## dụ sử dụng trong Claude
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
- Sau khi cấu hình xong, bạn có thể sử dụng trong Claude:
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
- ### Deploy một trang mới
157
+ ### dụ 3: Portfolio cá nhân
211
158
 
159
+ **Prompt của user:**
212
160
  ```
213
- Hãy deploy một trang HTML với:
214
- - Title: "Test Page"
215
- - Content: Một trang HTML hoàn chỉnh với TailwindCSS và một nút button
216
- - Slug: "test-page-1"
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
- Claude sẽ tự động tạo HTML document hoàn chỉnh với TailwindCSS CDN, sử dụng tool `deploy-page` và trả về URL của trang.
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
- **Lưu ý:** AI Agent sẽ tự động convert nội dung thành HTML chuẩn với:
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 các pages đã tạo
228
176
 
229
177
  ```
230
- Hãy liệt kê tất cả các pages của tôi
178
+ Hãy liệt kê tất cả các trang web tôi đã deploy
231
179
  ```
232
180
 
233
- Claude sẽ sử dụng tool `list-pages`hiển thị danh sách pages.
181
+ Claude sẽ hiển thị danh sách pages với title, slug thời gian tạo/cập nhật.
234
182
 
235
- ## Development
183
+ ## Technical Requirements
236
184
 
237
- ### Watch mode
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
- ```bash
240
- npm run watch
241
- # hoặc
242
- pnpm watch
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 trong Claude
213
+ ### MCP server không xuất hiện
250
214
 
251
- 1. Kiểm tra file config đã đúng format JSON
252
- 2. Kiểm tra đường dẫn đến `dist/index.js` là absolute path
253
- 3. Kiểm tra đã build project: `npm run build`
254
- 4. Restart Claude Desktop
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
- 1. Kiểm tra API key đã được set đúng trong env với key `API_KEY`
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,2 @@
1
+ export declare function callAPI<T>(endpoint: string, method?: string, body?: unknown): Promise<T>;
2
+ //# sourceMappingURL=api.d.ts.map
@@ -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
@@ -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
- // Configuration
6
- const API_BASE_URL = "https://pages.react.uat.gearvn.xyz";
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
- const { title, content, slug } = args;
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
- // Call the list pages API
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":"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"}
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,9 @@
1
+ import { Tool } from "@modelcontextprotocol/sdk/types.js";
2
+ export declare const LIST_PAGES_TOOL: Tool;
3
+ export declare function handleListPages(): Promise<{
4
+ content: {
5
+ type: "text";
6
+ text: string;
7
+ }[];
8
+ }>;
9
+ //# sourceMappingURL=list-pages.d.ts.map
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gearvn-pages-mcp-server",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "MCP Server for GearVN Pages deployment and management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
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
- // Configuration
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
- const { title, content, slug } = args as unknown as DeployPageArgs;
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
- // Call the list pages API
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
+ }