@dichvuright/mcp-hosting-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -0
- package/dist/server.js +217 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# DichVuRight Admin MCP Server
|
|
2
|
+
|
|
3
|
+
Kết nối AI Agent (Claude, Cursor, Antigravity...) vào hệ thống quản trị DichVuRight.
|
|
4
|
+
|
|
5
|
+
## Kiến trúc bảo mật
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
AI Agent
|
|
9
|
+
|
|
|
10
|
+
MCP Server (chỉ có: API_URL + API_KEY + API_SECRET)
|
|
11
|
+
|
|
|
12
|
+
POST /api/mcp/tools/call ← HMAC-SHA256 signed
|
|
13
|
+
|
|
|
14
|
+
Next.js API (verify HMAC + scope mcp:admin)
|
|
15
|
+
|
|
|
16
|
+
MongoDB / Redis ← MCP không bao giờ kết nối thẳng vào đây
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**MCP server không bao giờ biết:** JWT_SECRET, MongoDB URI, Redis password.
|
|
20
|
+
|
|
21
|
+
## Cài đặt
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd D:/nextjs/hosting/mcp
|
|
25
|
+
npm install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Tạo API Key
|
|
29
|
+
|
|
30
|
+
1. Vào **Admin → API Keys** (hoặc trang quản lý ApiClient)
|
|
31
|
+
2. Tạo key mới với scope **`mcp:admin`**
|
|
32
|
+
3. Copy `API Key` và `Secret`
|
|
33
|
+
|
|
34
|
+
## Cấu hình
|
|
35
|
+
|
|
36
|
+
Điền vào `mcp/.env`:
|
|
37
|
+
|
|
38
|
+
```env
|
|
39
|
+
MCP_API_URL=http://localhost:3000
|
|
40
|
+
MCP_API_KEY=mc_xxxxxxxxxxxx
|
|
41
|
+
MCP_API_SECRET=sk_xxxxxxxxxxxx
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Chỉ 3 biến. Không có secret nào của hệ thống ở đây.
|
|
45
|
+
|
|
46
|
+
## Kết nối Antigravity / Cursor / Cline
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"dichvuright": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["tsx", "D:/nextjs/hosting/mcp/server.ts"],
|
|
54
|
+
"cwd": "D:/nextjs/hosting/mcp"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Kết nối Claude Desktop
|
|
61
|
+
|
|
62
|
+
Mở `%APPDATA%\Claude\claude_desktop_config.json`:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"dichvuright": {
|
|
68
|
+
"command": "npx",
|
|
69
|
+
"args": ["tsx", "D:/nextjs/hosting/mcp/server.ts"],
|
|
70
|
+
"cwd": "D:/nextjs/hosting/mcp"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Test thủ công
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cd D:/nextjs/hosting/mcp
|
|
80
|
+
npm run dev
|
|
81
|
+
# Output: [MCP] DichVuRight Admin MCP Server v2.0 started
|
|
82
|
+
# Output: [MCP] API endpoint: http://localhost:3000/api/mcp/tools/call
|
|
83
|
+
# Output: [MCP] Tools registered: 28
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Tools (28 tools)
|
|
87
|
+
|
|
88
|
+
| Group | Tools |
|
|
89
|
+
|-------|-------|
|
|
90
|
+
| 🏥 Health (5) | health_check_all, health_check_mongodb, health_check_redis, health_check_suppliers, get_stuck_tasks |
|
|
91
|
+
| 📋 Logs (5) | query_logs*, get_error_summary, detect_suspicious_ips*, get_critical_events, get_admin_activity_digest |
|
|
92
|
+
| 🔔 Alerts (3) | send_alert✏️, get_alert_history, silence_alert✏️ |
|
|
93
|
+
| 📊 Dashboard (5) | get_revenue_stats, get_user_stats, get_service_utilization, get_ticket_stats, get_payment_health |
|
|
94
|
+
| 👤 Users (6) | find_user, get_user_profile, get_user_transactions, get_user_security_events, get_user_services, flag_user_for_review✏️ |
|
|
95
|
+
| 🎫 Tickets (4) | list_open_tickets, get_ticket_summary, get_overdue_tickets, get_ticket_stats |
|
|
96
|
+
|
|
97
|
+
`*` Heavy query: giới hạn 10 req/min
|
|
98
|
+
`✏️` Write op: tạo audit log
|
|
99
|
+
|
|
100
|
+
## Bảo mật
|
|
101
|
+
|
|
102
|
+
- **PII masking**: email, phone, name, IP tự động masked trước khi trả về AI
|
|
103
|
+
- **Không expose**: password hash, CCCD data, encrypted secrets
|
|
104
|
+
- **Rate limit**: 100 req/min (normal), 10 req/min (heavy queries)
|
|
105
|
+
- **Audit trail**: mọi tool call ghi vào MongoDB logs
|
|
106
|
+
- **Revoke ngay**: vào Admin → API Keys → Revoke nếu bị lộ key
|
|
107
|
+
|
|
108
|
+
## Troubleshooting
|
|
109
|
+
|
|
110
|
+
**"MCP_API_KEY và MCP_API_SECRET là bắt buộc"**
|
|
111
|
+
→ Kiểm tra file `mcp/.env`
|
|
112
|
+
|
|
113
|
+
**"Invalid API key" hoặc "Insufficient scope"**
|
|
114
|
+
→ API key chưa có scope `mcp:admin`, hoặc key bị revoke
|
|
115
|
+
|
|
116
|
+
**"Request expired"**
|
|
117
|
+
→ Đồng hồ máy lệch >5 phút, sync lại NTP
|
|
118
|
+
|
|
119
|
+
**Tools không xuất hiện trong IDE**
|
|
120
|
+
→ Kiểm tra `cwd` trỏ đúng `D:/nextjs/hosting/mcp`, restart IDE
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const API_URL = process.env.MCP_API_URL ?? "http://localhost:3000";
|
|
7
|
+
const API_KEY = process.env.MCP_API_KEY ?? "";
|
|
8
|
+
const API_SECRET = process.env.MCP_API_SECRET ?? "";
|
|
9
|
+
if (!API_KEY || !API_SECRET) {
|
|
10
|
+
console.error("[MCP] ERROR: MCP_API_KEY và MCP_API_SECRET là bắt buộc");
|
|
11
|
+
console.error("[MCP] Tạo API key trong Admin → API Keys với scope 'mcp:admin'");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
// ── HMAC call helper ───────────────────────────────────────────────────────
|
|
15
|
+
async function callApi(tool, args) {
|
|
16
|
+
const body = JSON.stringify({ tool, args });
|
|
17
|
+
const timestamp = String(Math.floor(Date.now() / 1000));
|
|
18
|
+
const requestId = crypto.randomUUID();
|
|
19
|
+
const path = "/api/mcp/tools/call";
|
|
20
|
+
const message = ["POST", path, body, timestamp, requestId].join("\n");
|
|
21
|
+
const signature = crypto.createHmac("sha256", API_SECRET).update(message).digest("hex");
|
|
22
|
+
const resp = await fetch(`${API_URL}${path}`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"X-Api-Key": API_KEY,
|
|
27
|
+
"X-Timestamp": timestamp,
|
|
28
|
+
"X-Request-Id": requestId,
|
|
29
|
+
"X-Signature": signature,
|
|
30
|
+
},
|
|
31
|
+
body,
|
|
32
|
+
});
|
|
33
|
+
const json = (await resp.json());
|
|
34
|
+
if (!resp.ok || json.status === "error") {
|
|
35
|
+
throw new Error(json.message ?? `API error ${resp.status}`);
|
|
36
|
+
}
|
|
37
|
+
return json.data?.result ?? json.data;
|
|
38
|
+
}
|
|
39
|
+
function text(result) {
|
|
40
|
+
return [{ type: "text", text: JSON.stringify(result, null, 2) }];
|
|
41
|
+
}
|
|
42
|
+
// ── Server ─────────────────────────────────────────────────────────────────
|
|
43
|
+
const server = new McpServer({ name: "dichvuright-admin", version: "2.0.0" });
|
|
44
|
+
// ── Health ─────────────────────────────────────────────────────────────────
|
|
45
|
+
server.registerTool("health_check_all", {
|
|
46
|
+
description: "Kiểm tra tổng quan hệ thống: MongoDB, Redis, task bị kẹt, WHM servers, VPS suppliers.",
|
|
47
|
+
}, async () => ({ content: text(await callApi("health_check_all", {})) }));
|
|
48
|
+
server.registerTool("health_check_mongodb", {
|
|
49
|
+
description: "Kiểm tra MongoDB: latency, connection state, số task theo trạng thái.",
|
|
50
|
+
}, async () => ({ content: text(await callApi("health_check_mongodb", {})) }));
|
|
51
|
+
server.registerTool("health_check_redis", {
|
|
52
|
+
description: "Kiểm tra Redis: memory, connected clients, hit rate, eviction policy.",
|
|
53
|
+
}, async () => ({ content: text(await callApi("health_check_redis", {})) }));
|
|
54
|
+
server.registerTool("health_check_suppliers", {
|
|
55
|
+
description: "Số WHM servers và VPS suppliers theo trạng thái active/inactive.",
|
|
56
|
+
}, async () => ({ content: text(await callApi("health_check_suppliers", {})) }));
|
|
57
|
+
server.registerTool("get_stuck_tasks", {
|
|
58
|
+
description: "Tasks đang running (status=2) quá 30 phút — có thể bị stuck.",
|
|
59
|
+
}, async () => ({ content: text(await callApi("get_stuck_tasks", {})) }));
|
|
60
|
+
// ── Logs ───────────────────────────────────────────────────────────────────
|
|
61
|
+
server.registerTool("query_logs", {
|
|
62
|
+
description: "Query audit logs với filter. ⚠️ Heavy query (10 req/min).",
|
|
63
|
+
inputSchema: {
|
|
64
|
+
severity: z.enum(["info", "warn", "error", "critical"]).optional().describe("Lọc theo mức độ"),
|
|
65
|
+
module: z.string().optional().describe("Lọc theo module (auth, user, vps, ...)"),
|
|
66
|
+
hours: z.number().optional().describe("Bao nhiêu giờ gần đây (mặc định 24)"),
|
|
67
|
+
limit: z.number().optional().describe("Số kết quả tối đa (mặc định 50, tối đa 200)"),
|
|
68
|
+
},
|
|
69
|
+
}, async (args) => ({ content: text(await callApi("query_logs", args)) }));
|
|
70
|
+
server.registerTool("get_error_summary", {
|
|
71
|
+
description: "Group errors theo module/severity. Dùng để xem điểm yếu hệ thống.",
|
|
72
|
+
inputSchema: {
|
|
73
|
+
hours: z.number().optional().describe("Bao nhiêu giờ gần đây (mặc định 24)"),
|
|
74
|
+
},
|
|
75
|
+
}, async (args) => ({ content: text(await callApi("get_error_summary", args)) }));
|
|
76
|
+
server.registerTool("detect_suspicious_ips", {
|
|
77
|
+
description: "Phát hiện IP có nhiều failed auth — dấu hiệu brute force. ⚠️ Heavy query.",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
threshold: z.number().optional().describe("Số lần thất bại tối thiểu (mặc định 5)"),
|
|
80
|
+
hours: z.number().optional().describe("Khoảng thời gian (mặc định 1 giờ)"),
|
|
81
|
+
},
|
|
82
|
+
}, async (args) => ({ content: text(await callApi("detect_suspicious_ips", args)) }));
|
|
83
|
+
server.registerTool("get_critical_events", {
|
|
84
|
+
description: "Lấy events có severity error/critical gần đây.",
|
|
85
|
+
inputSchema: {
|
|
86
|
+
hours: z.number().optional().describe("Bao nhiêu giờ gần đây (mặc định 6)"),
|
|
87
|
+
},
|
|
88
|
+
}, async (args) => ({ content: text(await callApi("get_critical_events", args)) }));
|
|
89
|
+
server.registerTool("get_admin_activity_digest", {
|
|
90
|
+
description: "Tóm tắt hoạt động của admin trong ngày theo module/action.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
date: z.string().optional().describe("Ngày cần xem YYYY-MM-DD (mặc định hôm nay)"),
|
|
93
|
+
},
|
|
94
|
+
}, async (args) => ({ content: text(await callApi("get_admin_activity_digest", args)) }));
|
|
95
|
+
// ── Alerts ─────────────────────────────────────────────────────────────────
|
|
96
|
+
server.registerTool("send_alert", {
|
|
97
|
+
description: "Gửi Telegram alert đến admin. Cooldown: warn=5m, error=1m, critical=không giới hạn. ✏️ WRITE.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
message: z.string().describe("Nội dung cảnh báo"),
|
|
100
|
+
severity: z.enum(["info", "warn", "error", "critical"]).describe("Mức độ nghiêm trọng"),
|
|
101
|
+
},
|
|
102
|
+
annotations: { destructiveHint: false },
|
|
103
|
+
}, async (args) => ({ content: text(await callApi("send_alert", args)) }));
|
|
104
|
+
server.registerTool("get_alert_history", {
|
|
105
|
+
description: "Lịch sử alerts đã gửi gần đây.",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
hours: z.number().optional().describe("Bao nhiêu giờ gần đây (mặc định 24)"),
|
|
108
|
+
},
|
|
109
|
+
}, async (args) => ({ content: text(await callApi("get_alert_history", args)) }));
|
|
110
|
+
server.registerTool("silence_alert", {
|
|
111
|
+
description: "Tắt alert tạm thời. key='all' để tắt hết. ✏️ WRITE.",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
key: z.string().describe("Alert key ('all' = tắt hết)"),
|
|
114
|
+
minutes: z.number().describe("Số phút silence (1-1440)"),
|
|
115
|
+
},
|
|
116
|
+
annotations: { destructiveHint: false },
|
|
117
|
+
}, async (args) => ({ content: text(await callApi("silence_alert", args)) }));
|
|
118
|
+
// ── Dashboard ──────────────────────────────────────────────────────────────
|
|
119
|
+
server.registerTool("get_revenue_stats", {
|
|
120
|
+
description: "Thống kê doanh thu theo ngày/tuần/tháng từ logbalance.",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
period: z.enum(["day", "week", "month"]).describe("Kỳ thống kê"),
|
|
123
|
+
},
|
|
124
|
+
}, async (args) => ({ content: text(await callApi("get_revenue_stats", args)) }));
|
|
125
|
+
server.registerTool("get_user_stats", {
|
|
126
|
+
description: "Thống kê user: mới đăng ký, active, bị ban trong kỳ.",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
period: z.enum(["day", "week", "month"]).describe("Kỳ thống kê"),
|
|
129
|
+
},
|
|
130
|
+
}, async (args) => ({ content: text(await callApi("get_user_stats", args)) }));
|
|
131
|
+
server.registerTool("get_service_utilization", {
|
|
132
|
+
description: "VPS + hosting đang chạy theo status.",
|
|
133
|
+
annotations: { readOnlyHint: true },
|
|
134
|
+
}, async () => ({ content: text(await callApi("get_service_utilization", {})) }));
|
|
135
|
+
server.registerTool("get_ticket_stats", {
|
|
136
|
+
description: "Thống kê tickets theo status, số quá hạn >48h.",
|
|
137
|
+
annotations: { readOnlyHint: true },
|
|
138
|
+
}, async () => ({ content: text(await callApi("get_ticket_stats", {})) }));
|
|
139
|
+
server.registerTool("get_payment_health", {
|
|
140
|
+
description: "Tỉ lệ thành công thanh toán theo loại (bank/card/momo).",
|
|
141
|
+
annotations: { readOnlyHint: true },
|
|
142
|
+
}, async () => ({ content: text(await callApi("get_payment_health", {})) }));
|
|
143
|
+
// ── Users ──────────────────────────────────────────────────────────────────
|
|
144
|
+
server.registerTool("find_user", {
|
|
145
|
+
description: "Tìm user theo username, email hoặc phone. Kết quả PII đã mask.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
query: z.string().describe("Từ khóa tìm kiếm (username / email / phone)"),
|
|
148
|
+
},
|
|
149
|
+
annotations: { readOnlyHint: true },
|
|
150
|
+
}, async (args) => ({ content: text(await callApi("find_user", args)) }));
|
|
151
|
+
server.registerTool("get_user_profile", {
|
|
152
|
+
description: "Thông tin user. Email/phone/name đã mask. KHÔNG có password hay CCCD.",
|
|
153
|
+
inputSchema: {
|
|
154
|
+
userId: z.string().describe("MongoDB ObjectId của user"),
|
|
155
|
+
},
|
|
156
|
+
annotations: { readOnlyHint: true },
|
|
157
|
+
}, async (args) => ({ content: text(await callApi("get_user_profile", args)) }));
|
|
158
|
+
server.registerTool("get_user_transactions", {
|
|
159
|
+
description: "Lịch sử giao dịch của user (nạp tiền, trừ tiền...).",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
userId: z.string().describe("MongoDB ObjectId của user"),
|
|
162
|
+
limit: z.number().optional().describe("Số giao dịch (mặc định 20, tối đa 50)"),
|
|
163
|
+
},
|
|
164
|
+
annotations: { readOnlyHint: true },
|
|
165
|
+
}, async (args) => ({ content: text(await callApi("get_user_transactions", args)) }));
|
|
166
|
+
server.registerTool("get_user_security_events", {
|
|
167
|
+
description: "Lịch sử login/2FA/failed auth của user. IP đã mask.",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
userId: z.string().describe("MongoDB ObjectId của user"),
|
|
170
|
+
hours: z.number().optional().describe("Bao nhiêu giờ gần đây (mặc định 48)"),
|
|
171
|
+
},
|
|
172
|
+
annotations: { readOnlyHint: true },
|
|
173
|
+
}, async (args) => ({ content: text(await callApi("get_user_security_events", args)) }));
|
|
174
|
+
server.registerTool("get_user_services", {
|
|
175
|
+
description: "VPS + hosting của user theo status.",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
userId: z.string().describe("MongoDB ObjectId của user"),
|
|
178
|
+
},
|
|
179
|
+
annotations: { readOnlyHint: true },
|
|
180
|
+
}, async (args) => ({ content: text(await callApi("get_user_services", args)) }));
|
|
181
|
+
server.registerTool("flag_user_for_review", {
|
|
182
|
+
description: "Đánh dấu user cần review. Chỉ tạo audit log, KHÔNG sửa User document. ✏️ WRITE.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
userId: z.string().describe("MongoDB ObjectId của user"),
|
|
185
|
+
reason: z.string().describe("Lý do đánh dấu (tối đa 500 ký tự)"),
|
|
186
|
+
},
|
|
187
|
+
annotations: { destructiveHint: false },
|
|
188
|
+
}, async (args) => ({ content: text(await callApi("flag_user_for_review", args)) }));
|
|
189
|
+
// ── Tickets ────────────────────────────────────────────────────────────────
|
|
190
|
+
server.registerTool("list_open_tickets", {
|
|
191
|
+
description: "Tickets chưa đóng, sort theo priority (urgent trước).",
|
|
192
|
+
inputSchema: {
|
|
193
|
+
limit: z.number().optional().describe("Số kết quả (mặc định 20)"),
|
|
194
|
+
priority: z.enum(["urgent", "high", "medium", "low"]).optional().describe("Lọc theo priority"),
|
|
195
|
+
},
|
|
196
|
+
annotations: { readOnlyHint: true },
|
|
197
|
+
}, async (args) => ({ content: text(await callApi("list_open_tickets", args)) }));
|
|
198
|
+
server.registerTool("get_ticket_summary", {
|
|
199
|
+
description: "Chi tiết ticket + 5 tin nhắn gần nhất. Email trong nội dung đã mask.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
ticketId: z.string().describe("MongoDB ObjectId của ticket"),
|
|
202
|
+
},
|
|
203
|
+
annotations: { readOnlyHint: true },
|
|
204
|
+
}, async (args) => ({ content: text(await callApi("get_ticket_summary", args)) }));
|
|
205
|
+
server.registerTool("get_overdue_tickets", {
|
|
206
|
+
description: "Tickets open/pending >48h chưa được xử lý, sort theo cũ nhất.",
|
|
207
|
+
annotations: { readOnlyHint: true },
|
|
208
|
+
}, async () => ({ content: text(await callApi("get_overdue_tickets", {})) }));
|
|
209
|
+
server.registerTool("get_ticket_stats_summary", {
|
|
210
|
+
description: "Thống kê tổng quan tickets: theo status, số quá hạn, tổng.",
|
|
211
|
+
annotations: { readOnlyHint: true },
|
|
212
|
+
}, async () => ({ content: text(await callApi("get_ticket_stats", {})) }));
|
|
213
|
+
// ── Start ──────────────────────────────────────────────────────────────────
|
|
214
|
+
const transport = new StdioServerTransport();
|
|
215
|
+
await server.connect(transport);
|
|
216
|
+
console.error(`[MCP] DichVuRight Admin MCP Server v2.0 started`);
|
|
217
|
+
console.error(`[MCP] API: ${API_URL}/api/mcp/tools/call`);
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dichvuright/mcp-hosting-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "DichVuRight Admin MCP Server — AI agent integration via HMAC API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dichvuright-mcp": "./dist/server.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/server.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "tsx server.ts",
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/server.js",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"registry": "https://registry.npmjs.org",
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/dichvuright/hosting-dichvuright.git",
|
|
26
|
+
"directory": "mcp"
|
|
27
|
+
},
|
|
28
|
+
"keywords": ["mcp", "dichvuright", "admin", "ai-agent"],
|
|
29
|
+
"license": "UNLICENSED",
|
|
30
|
+
"private": false,
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
33
|
+
"dotenv": "^16.0.0",
|
|
34
|
+
"zod": "^4.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"tsx": "^4.0.0",
|
|
39
|
+
"typescript": "^5.0.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|