@taotao7/bypass-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @bypass/mcp
2
+
3
+ bypass-saas 平台的 MCP Server,提供课程、文章、学员、订单等管理能力,供 Claude Desktop / Claude Code 等 MCP 客户端调用。
4
+
5
+ ## 前置条件
6
+
7
+ 需要一个具有 D1 Edit 权限的 Cloudflare API Token:
8
+
9
+ 1. 前往 https://dash.cloudflare.com/profile/api-tokens
10
+ 2. 创建 Token,权限选择 **D1 Edit**
11
+ 3. 记录生成的 Token
12
+
13
+ ## 构建
14
+
15
+ ```bash
16
+ # 在 monorepo 根目录
17
+ pnpm install
18
+ cd packages/mcp && pnpm build
19
+ ```
20
+
21
+ ## 配置
22
+
23
+ ### Claude Desktop
24
+
25
+ 编辑 `~/Library/Application Support/Claude/claude_desktop_config.json`:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "bypass": {
31
+ "command": "node",
32
+ "args": ["/absolute/path/to/bypass-saas/packages/mcp/dist/index.js"],
33
+ "env": {
34
+ "CLOUDFLARE_API_TOKEN": "your-token-here"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### Claude Code
42
+
43
+ 编辑项目 `.claude/settings.json` 或全局配置:
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "bypass": {
49
+ "command": "node",
50
+ "args": ["packages/mcp/dist/index.js"],
51
+ "cwd": "/path/to/bypass-saas",
52
+ "env": {
53
+ "CLOUDFLARE_API_TOKEN": "your-token-here"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## 可用工具
61
+
62
+ ### 课程管理
63
+
64
+ | 工具 | 说明 |
65
+ |------|------|
66
+ | `bypass_list_courses` | 列出课程,支持 status/category_id/search 筛选 + 分页 |
67
+ | `bypass_get_course` | 按 ID 获取单个课程详情 |
68
+ | `bypass_create_course` | 创建课程(价格单位:分,如 29900 = ¥299) |
69
+ | `bypass_update_course` | 更新课程,仅更新传入的字段 |
70
+ | `bypass_delete_course` | 删除课程 |
71
+
72
+ ### 文章管理
73
+
74
+ | 工具 | 说明 |
75
+ |------|------|
76
+ | `bypass_list_articles` | 列出文章,支持 status/category/search 筛选 + 分页 |
77
+ | `bypass_get_article` | 按 ID 获取单篇文章 |
78
+ | `bypass_create_article` | 创建文章 |
79
+ | `bypass_update_article` | 更新文章,仅更新传入的字段 |
80
+ | `bypass_delete_article` | 删除文章 |
81
+
82
+ ### 用户与学员
83
+
84
+ | 工具 | 说明 |
85
+ |------|------|
86
+ | `bypass_list_users` | 列出注册用户,支持 search 筛选 + 分页 |
87
+ | `bypass_list_students` | 列出学员课程记录(含用户和课程详情),支持搜索 |
88
+
89
+ ### 订单
90
+
91
+ | 工具 | 说明 |
92
+ |------|------|
93
+ | `bypass_list_orders` | 列出订单(含用户和课程详情),支持 status 筛选 |
94
+
95
+ ### 统计与分类
96
+
97
+ | 工具 | 说明 |
98
+ |------|------|
99
+ | `bypass_get_stats` | 获取平台聚合统计(用户数、课程数、收入、文章数、学员数等) |
100
+ | `bypass_list_categories` | 列出所有课程分类 |
101
+
102
+ ## 技术细节
103
+
104
+ - 传输方式:stdio(本地运行)
105
+ - 数据访问:通过 Cloudflare D1 REST API,无需本地 wrangler
106
+ - 数据库:`bypass-saas-db`(与 web/admin 共享同一个 D1)
107
+ - 所有列表接口支持 `limit` + `offset` 分页,返回 `total` / `has_more` 元数据
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { initD1 } from "./services/d1.js";
5
+ import { registerCourseTools } from "./tools/courses.js";
6
+ import { registerArticleTools } from "./tools/articles.js";
7
+ import { registerStudentTools } from "./tools/students.js";
8
+ import { registerOrderTools } from "./tools/orders.js";
9
+ import { registerStatsTools } from "./tools/stats.js";
10
+ const token = process.env.CLOUDFLARE_API_TOKEN;
11
+ if (!token) {
12
+ console.error("ERROR: CLOUDFLARE_API_TOKEN environment variable is required.");
13
+ console.error("Create one at: https://dash.cloudflare.com/profile/api-tokens");
14
+ console.error("Required permission: D1 Edit");
15
+ process.exit(1);
16
+ }
17
+ initD1(token);
18
+ const server = new McpServer({
19
+ name: "bypass-mcp-server",
20
+ version: "1.0.0",
21
+ });
22
+ registerCourseTools(server);
23
+ registerArticleTools(server);
24
+ registerStudentTools(server);
25
+ registerOrderTools(server);
26
+ registerStatsTools(server);
27
+ async function main() {
28
+ const transport = new StdioServerTransport();
29
+ await server.connect(transport);
30
+ console.error("bypass-mcp-server running via stdio");
31
+ }
32
+ main().catch((error) => {
33
+ console.error("Server error:", error);
34
+ process.exit(1);
35
+ });
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;AAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CACX,+DAA+D,CAChE,CAAC;IACF,OAAO,CAAC,KAAK,CACX,+DAA+D,CAChE,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,CAAC,CAAC;AAEd,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,mBAAmB;IACzB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAE3B,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,qCAAqC,CAAC,CAAC;AACvD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare function initD1(token: string): void;
2
+ interface D1Result<T = Record<string, unknown>> {
3
+ results: T[];
4
+ success: boolean;
5
+ meta: {
6
+ changes: number;
7
+ last_row_id: number;
8
+ rows_read: number;
9
+ rows_written: number;
10
+ };
11
+ }
12
+ export declare function query<T = Record<string, unknown>>(sql: string, params?: (string | number | null)[]): Promise<D1Result<T>>;
13
+ export declare function queryFirst<T = Record<string, unknown>>(sql: string, params?: (string | number | null)[]): Promise<T | null>;
14
+ export {};
@@ -0,0 +1,31 @@
1
+ const ACCOUNT_ID = "29fda387d3a4cf2df6823c53c93138d0";
2
+ const DATABASE_ID = "a5fc6c86-ba97-478e-9655-c7b4a1c95b2b";
3
+ let apiToken;
4
+ export function initD1(token) {
5
+ apiToken = token;
6
+ }
7
+ export async function query(sql, params = []) {
8
+ const url = `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query`;
9
+ const res = await fetch(url, {
10
+ method: "POST",
11
+ headers: {
12
+ Authorization: `Bearer ${apiToken}`,
13
+ "Content-Type": "application/json",
14
+ },
15
+ body: JSON.stringify({ sql, params }),
16
+ });
17
+ if (!res.ok) {
18
+ const text = await res.text();
19
+ throw new Error(`D1 API error (${res.status}): ${text}`);
20
+ }
21
+ const body = (await res.json());
22
+ if (!body.success) {
23
+ throw new Error(`D1 query failed: ${body.errors.map((e) => e.message).join(", ")}`);
24
+ }
25
+ return body.result[0];
26
+ }
27
+ export async function queryFirst(sql, params = []) {
28
+ const result = await query(sql, params);
29
+ return result.results[0] ?? null;
30
+ }
31
+ //# sourceMappingURL=d1.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"d1.js","sourceRoot":"","sources":["../../src/services/d1.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,kCAAkC,CAAC;AACtD,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAE3D,IAAI,QAAgB,CAAC;AAErB,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,QAAQ,GAAG,KAAK,CAAC;AACnB,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,GAAW,EACX,SAAqC,EAAE;IAEvC,MAAM,GAAG,GAAG,iDAAiD,UAAU,gBAAgB,WAAW,QAAQ,CAAC;IAC3G,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,aAAa,EAAE,UAAU,QAAQ,EAAE;YACnC,cAAc,EAAE,kBAAkB;SACnC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;KACtC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA+E,CAAC;IAC9G,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,SAAqC,EAAE;IAEvC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAI,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACnC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerArticleTools(server: McpServer): void;
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ import { query, queryFirst } from "../services/d1.js";
3
+ export function registerArticleTools(server) {
4
+ server.registerTool("bypass_list_articles", {
5
+ title: "List Articles",
6
+ description: "List blog articles with optional filters. Returns paginated results.",
7
+ inputSchema: {
8
+ status: z.enum(["draft", "published"]).optional().describe("Filter by status"),
9
+ category: z.string().optional().describe("Filter by category name"),
10
+ search: z.string().optional().describe("Search in title"),
11
+ limit: z.number().int().min(1).max(100).default(20).describe("Max results"),
12
+ offset: z.number().int().min(0).default(0).describe("Pagination offset"),
13
+ },
14
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
15
+ }, async (params) => {
16
+ const conditions = [];
17
+ const binds = [];
18
+ if (params.status) {
19
+ conditions.push("status = ?");
20
+ binds.push(params.status);
21
+ }
22
+ if (params.category) {
23
+ conditions.push("category = ?");
24
+ binds.push(params.category);
25
+ }
26
+ if (params.search) {
27
+ conditions.push("title LIKE ?");
28
+ binds.push(`%${params.search}%`);
29
+ }
30
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
31
+ const countResult = await queryFirst(`SELECT COUNT(*) as total FROM blog_articles ${where}`, binds);
32
+ const total = countResult?.total ?? 0;
33
+ const rows = await query(`SELECT * FROM blog_articles ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`, [...binds, params.limit, params.offset]);
34
+ const output = { total, count: rows.results.length, offset: params.offset, articles: rows.results, has_more: total > params.offset + rows.results.length };
35
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
36
+ });
37
+ server.registerTool("bypass_get_article", {
38
+ title: "Get Article",
39
+ description: "Get a single article by ID.",
40
+ inputSchema: { id: z.number().int().describe("Article ID") },
41
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
42
+ }, async (params) => {
43
+ const article = await queryFirst("SELECT * FROM blog_articles WHERE id = ?", [params.id]);
44
+ if (!article)
45
+ return { content: [{ type: "text", text: `Article ${params.id} not found` }], isError: true };
46
+ return { content: [{ type: "text", text: JSON.stringify(article, null, 2) }] };
47
+ });
48
+ server.registerTool("bypass_create_article", {
49
+ title: "Create Article",
50
+ description: "Create a new blog article.",
51
+ inputSchema: {
52
+ title: z.string().min(1).describe("Article title"),
53
+ slug: z.string().min(1).describe("URL slug"),
54
+ category: z.string().min(1).describe("Category name"),
55
+ author_name: z.string().default("包帕斯").describe("Author name"),
56
+ excerpt: z.string().optional().describe("Short excerpt"),
57
+ content: z.string().optional().describe("Markdown content"),
58
+ cover_url: z.string().optional().describe("Cover image URL"),
59
+ status: z.enum(["draft", "published"]).default("draft").describe("Status"),
60
+ },
61
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
62
+ }, async (params) => {
63
+ const publishedAt = params.status === "published" ? new Date().toISOString() : null;
64
+ await query(`INSERT INTO blog_articles (slug, title, excerpt, content, category, cover_url, author_name, status, published_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [params.slug, params.title, params.excerpt ?? null, params.content ?? null, params.category, params.cover_url ?? null, params.author_name, params.status, publishedAt]);
65
+ return { content: [{ type: "text", text: `Article "${params.title}" created.` }] };
66
+ });
67
+ server.registerTool("bypass_update_article", {
68
+ title: "Update Article",
69
+ description: "Update an existing article by ID. Only provided fields are updated.",
70
+ inputSchema: {
71
+ id: z.number().int().describe("Article ID"),
72
+ title: z.string().optional(), slug: z.string().optional(),
73
+ excerpt: z.string().optional(), content: z.string().optional(),
74
+ category: z.string().optional(), cover_url: z.string().optional(),
75
+ author_name: z.string().optional(),
76
+ status: z.enum(["draft", "published"]).optional(),
77
+ },
78
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
79
+ }, async (params) => {
80
+ const { id, ...fields } = params;
81
+ const sets = [];
82
+ const binds = [];
83
+ for (const [key, val] of Object.entries(fields)) {
84
+ if (val !== undefined) {
85
+ sets.push(`${key} = ?`);
86
+ binds.push(val);
87
+ }
88
+ }
89
+ if (fields.status === "published") {
90
+ sets.push("published_at = COALESCE(published_at, CURRENT_TIMESTAMP)");
91
+ }
92
+ if (!sets.length)
93
+ return { content: [{ type: "text", text: "No fields to update." }] };
94
+ sets.push("updated_at = CURRENT_TIMESTAMP");
95
+ binds.push(id);
96
+ await query(`UPDATE blog_articles SET ${sets.join(", ")} WHERE id = ?`, binds);
97
+ return { content: [{ type: "text", text: `Article ${id} updated.` }] };
98
+ });
99
+ server.registerTool("bypass_delete_article", {
100
+ title: "Delete Article",
101
+ description: "Permanently delete an article by ID.",
102
+ inputSchema: { id: z.number().int().describe("Article ID") },
103
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
104
+ }, async (params) => {
105
+ await query("DELETE FROM blog_articles WHERE id = ?", [params.id]);
106
+ return { content: [{ type: "text", text: `Article ${params.id} deleted.` }] };
107
+ });
108
+ }
109
+ //# sourceMappingURL=articles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"articles.js","sourceRoot":"","sources":["../../src/tools/articles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCourseTools(server: McpServer): void;
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ import { query, queryFirst } from "../services/d1.js";
3
+ export function registerCourseTools(server) {
4
+ server.registerTool("bypass_list_courses", {
5
+ title: "List Courses",
6
+ description: "List courses with optional filters (status, category, search). Returns paginated results.",
7
+ inputSchema: {
8
+ status: z.enum(["draft", "published"]).optional().describe("Filter by status"),
9
+ category_id: z.number().int().optional().describe("Filter by category ID"),
10
+ search: z.string().optional().describe("Search in title"),
11
+ limit: z.number().int().min(1).max(100).default(20).describe("Max results"),
12
+ offset: z.number().int().min(0).default(0).describe("Pagination offset"),
13
+ },
14
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
15
+ }, async (params) => {
16
+ const conditions = [];
17
+ const binds = [];
18
+ if (params.status) {
19
+ conditions.push("c.status = ?");
20
+ binds.push(params.status);
21
+ }
22
+ if (params.category_id) {
23
+ conditions.push("c.category_id = ?");
24
+ binds.push(params.category_id);
25
+ }
26
+ if (params.search) {
27
+ conditions.push("c.title LIKE ?");
28
+ binds.push(`%${params.search}%`);
29
+ }
30
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
31
+ const countResult = await queryFirst(`SELECT COUNT(*) as total FROM courses c ${where}`, binds);
32
+ const total = countResult?.total ?? 0;
33
+ const rows = await query(`SELECT c.*, cat.name AS category_name FROM courses c LEFT JOIN categories cat ON c.category_id = cat.id ${where} ORDER BY c.created_at DESC LIMIT ? OFFSET ?`, [...binds, params.limit, params.offset]);
34
+ const output = { total, count: rows.results.length, offset: params.offset, courses: rows.results, has_more: total > params.offset + rows.results.length };
35
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
36
+ });
37
+ server.registerTool("bypass_get_course", {
38
+ title: "Get Course",
39
+ description: "Get a single course by ID with category info.",
40
+ inputSchema: { id: z.number().int().describe("Course ID") },
41
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
42
+ }, async (params) => {
43
+ const course = await queryFirst(`SELECT c.*, cat.name AS category_name FROM courses c LEFT JOIN categories cat ON c.category_id = cat.id WHERE c.id = ?`, [params.id]);
44
+ if (!course)
45
+ return { content: [{ type: "text", text: `Course ${params.id} not found` }], isError: true };
46
+ return { content: [{ type: "text", text: JSON.stringify(course, null, 2) }] };
47
+ });
48
+ server.registerTool("bypass_create_course", {
49
+ title: "Create Course",
50
+ description: "Create a new course. Price in cents (e.g. 29900 = ¥299).",
51
+ inputSchema: {
52
+ title: z.string().min(1).describe("Course title"),
53
+ slug: z.string().min(1).describe("URL slug"),
54
+ price: z.number().int().min(0).describe("Price in cents"),
55
+ instructor: z.string().default("包帕斯").describe("Instructor name"),
56
+ description: z.string().optional().describe("Course description"),
57
+ category_id: z.number().int().optional().describe("Category ID"),
58
+ original_price: z.number().int().optional().describe("Original price in cents"),
59
+ instructor_title: z.string().optional().describe("Instructor title"),
60
+ cover_url: z.string().optional().describe("Cover image URL"),
61
+ content_url: z.string().optional().describe("Content URL"),
62
+ status: z.enum(["draft", "published"]).default("draft").describe("Status"),
63
+ },
64
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
65
+ }, async (params) => {
66
+ await query(`INSERT INTO courses (slug, title, description, category_id, price, original_price, instructor, instructor_title, cover_url, content_url, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [params.slug, params.title, params.description ?? null, params.category_id ?? null, params.price, params.original_price ?? null, params.instructor, params.instructor_title ?? null, params.cover_url ?? null, params.content_url ?? null, params.status]);
67
+ return { content: [{ type: "text", text: `Course "${params.title}" created successfully.` }] };
68
+ });
69
+ server.registerTool("bypass_update_course", {
70
+ title: "Update Course",
71
+ description: "Update an existing course by ID. Only provided fields are updated.",
72
+ inputSchema: {
73
+ id: z.number().int().describe("Course ID"),
74
+ title: z.string().optional(), slug: z.string().optional(),
75
+ description: z.string().optional(), category_id: z.number().int().optional(),
76
+ price: z.number().int().optional(), original_price: z.number().int().optional(),
77
+ instructor: z.string().optional(), instructor_title: z.string().optional(),
78
+ cover_url: z.string().optional(), content_url: z.string().optional(),
79
+ status: z.enum(["draft", "published"]).optional(),
80
+ },
81
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
82
+ }, async (params) => {
83
+ const { id, ...fields } = params;
84
+ const sets = [];
85
+ const binds = [];
86
+ for (const [key, val] of Object.entries(fields)) {
87
+ if (val !== undefined) {
88
+ sets.push(`${key} = ?`);
89
+ binds.push(val);
90
+ }
91
+ }
92
+ if (!sets.length)
93
+ return { content: [{ type: "text", text: "No fields to update." }] };
94
+ sets.push("updated_at = CURRENT_TIMESTAMP");
95
+ binds.push(id);
96
+ await query(`UPDATE courses SET ${sets.join(", ")} WHERE id = ?`, binds);
97
+ return { content: [{ type: "text", text: `Course ${id} updated.` }] };
98
+ });
99
+ server.registerTool("bypass_delete_course", {
100
+ title: "Delete Course",
101
+ description: "Permanently delete a course by ID.",
102
+ inputSchema: { id: z.number().int().describe("Course ID") },
103
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
104
+ }, async (params) => {
105
+ await query("DELETE FROM courses WHERE id = ?", [params.id]);
106
+ return { content: [{ type: "text", text: `Course ${params.id} deleted.` }] };
107
+ });
108
+ }
109
+ //# sourceMappingURL=courses.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"courses.js","sourceRoot":"","sources":["../../src/tools/courses.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerOrderTools(server: McpServer): void;
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ import { query, queryFirst } from "../services/d1.js";
3
+ export function registerOrderTools(server) {
4
+ server.registerTool("bypass_list_orders", {
5
+ title: "List Orders",
6
+ description: "List orders with user and course details. Supports filtering by status.",
7
+ inputSchema: {
8
+ status: z.enum(["pending", "paid", "failed"]).optional().describe("Filter by order status"),
9
+ limit: z.number().int().min(1).max(100).default(20).describe("Max results"),
10
+ offset: z.number().int().min(0).default(0).describe("Pagination offset"),
11
+ },
12
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
13
+ }, async (params) => {
14
+ const conditions = [];
15
+ const binds = [];
16
+ if (params.status) {
17
+ conditions.push("o.status = ?");
18
+ binds.push(params.status);
19
+ }
20
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
21
+ const countResult = await queryFirst(`SELECT COUNT(*) as total FROM orders o ${where}`, binds);
22
+ const total = countResult?.total ?? 0;
23
+ const rows = await query(`SELECT o.*, u.name AS user_name, u.email AS user_email, c.title AS course_title
24
+ FROM orders o
25
+ LEFT JOIN users u ON o.user_id = u.id
26
+ LEFT JOIN courses c ON o.course_id = c.id
27
+ ${where} ORDER BY o.created_at DESC LIMIT ? OFFSET ?`, [...binds, params.limit, params.offset]);
28
+ const output = { total, count: rows.results.length, offset: params.offset, orders: rows.results, has_more: total > params.offset + rows.results.length };
29
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
30
+ });
31
+ }
32
+ //# sourceMappingURL=orders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orders.js","sourceRoot":"","sources":["../../src/tools/orders.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,YAAY,CAAC,oBAAoB,EAAE;QACxC,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,yEAAyE;QACtF,WAAW,EAAE;YACX,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAC3F,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC3E,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;SACzE;QACD,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;KACxG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAClB,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,KAAK,GAAwB,EAAE,CAAC;QAEtC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAAC,CAAC;QAElF,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,MAAM,WAAW,GAAG,MAAM,UAAU,CAAoB,0CAA0C,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QAClH,MAAM,KAAK,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,CAAC;QAEtC,MAAM,IAAI,GAAG,MAAM,KAAK,CACtB;;;;SAIG,KAAK,8CAA8C,EACtD,CAAC,GAAG,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CACxC,CAAC;QAEF,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACzJ,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACzF,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerStatsTools(server: McpServer): void;
@@ -0,0 +1,29 @@
1
+ import { queryFirst } from "../services/d1.js";
2
+ export function registerStatsTools(server) {
3
+ server.registerTool("bypass_get_stats", {
4
+ title: "Get Dashboard Stats",
5
+ description: "Get aggregated platform statistics: users, courses, revenue, articles, enrollments.",
6
+ inputSchema: {},
7
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
8
+ }, async () => {
9
+ const [users, courses, orders, articles, enrollments] = await Promise.all([
10
+ queryFirst("SELECT COUNT(*) as total FROM users"),
11
+ queryFirst(`SELECT COUNT(*) as total, SUM(CASE WHEN status='published' THEN 1 ELSE 0 END) as published, SUM(CASE WHEN status='draft' THEN 1 ELSE 0 END) as draft FROM courses`),
12
+ queryFirst(`SELECT COUNT(*) as total_orders, SUM(CASE WHEN status='paid' THEN 1 ELSE 0 END) as paid_orders, COALESCE(SUM(CASE WHEN status='paid' THEN amount ELSE 0 END),0) as total_revenue FROM orders`),
13
+ queryFirst(`SELECT COUNT(*) as total, SUM(CASE WHEN status='published' THEN 1 ELSE 0 END) as published FROM blog_articles`),
14
+ queryFirst(`SELECT COUNT(*) as total_enrollments, COUNT(DISTINCT user_id) as total_students, COALESCE(AVG(progress),0) as avg_progress, SUM(CASE WHEN progress>=100 THEN 1 ELSE 0 END) as completed FROM user_courses`),
15
+ ]);
16
+ const output = { users, courses, orders, articles, enrollments };
17
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
18
+ });
19
+ server.registerTool("bypass_list_categories", {
20
+ title: "List Categories",
21
+ description: "List all course categories.",
22
+ inputSchema: {},
23
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
24
+ }, async () => {
25
+ const { results } = await (await import("../services/d1.js")).query("SELECT * FROM categories ORDER BY sort_order ASC");
26
+ return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
27
+ });
28
+ }
29
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/tools/stats.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,YAAY,CAAC,kBAAkB,EAAE;QACtC,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,qFAAqF;QAClG,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;KACxG,EAAE,KAAK,IAAI,EAAE;QACZ,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxE,UAAU,CAAoB,qCAAqC,CAAC;YACpE,UAAU,CACR,mKAAmK,CACpK;YACD,UAAU,CACR,8LAA8L,CAC/L;YACD,UAAU,CACR,+GAA+G,CAChH;YACD,UAAU,CACR,2MAA2M,CAC5M;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QACjE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,CAAC,wBAAwB,EAAE;QAC5C,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,6BAA6B;QAC1C,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;KACxG,EAAE,KAAK,IAAI,EAAE;QACZ,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CACjE,kDAAkD,CACnD,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1F,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerStudentTools(server: McpServer): void;
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ import { query, queryFirst } from "../services/d1.js";
3
+ export function registerStudentTools(server) {
4
+ server.registerTool("bypass_list_students", {
5
+ title: "List Student Enrollments",
6
+ description: "List student-course enrollments with user and course details. Supports search by name/email/course title.",
7
+ inputSchema: {
8
+ search: z.string().optional().describe("Search in user name, email, or course title"),
9
+ limit: z.number().int().min(1).max(100).default(20).describe("Max results"),
10
+ offset: z.number().int().min(0).default(0).describe("Pagination offset"),
11
+ },
12
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
13
+ }, async (params) => {
14
+ const conditions = [];
15
+ const binds = [];
16
+ if (params.search) {
17
+ conditions.push("(u.name LIKE ? OR u.email LIKE ? OR c.title LIKE ?)");
18
+ const s = `%${params.search}%`;
19
+ binds.push(s, s, s);
20
+ }
21
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
22
+ const countResult = await queryFirst(`SELECT COUNT(*) as total FROM user_courses uc JOIN users u ON uc.user_id = u.id JOIN courses c ON uc.course_id = c.id ${where}`, binds);
23
+ const total = countResult?.total ?? 0;
24
+ const rows = await query(`SELECT uc.*, u.name AS user_name, u.email AS user_email, c.title AS course_title, c.price AS course_price
25
+ FROM user_courses uc JOIN users u ON uc.user_id = u.id JOIN courses c ON uc.course_id = c.id
26
+ ${where} ORDER BY uc.purchased_at DESC LIMIT ? OFFSET ?`, [...binds, params.limit, params.offset]);
27
+ const output = { total, count: rows.results.length, offset: params.offset, enrollments: rows.results, has_more: total > params.offset + rows.results.length };
28
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
29
+ });
30
+ server.registerTool("bypass_list_users", {
31
+ title: "List Users",
32
+ description: "List all registered users with optional search.",
33
+ inputSchema: {
34
+ search: z.string().optional().describe("Search in name or email"),
35
+ limit: z.number().int().min(1).max(100).default(20).describe("Max results"),
36
+ offset: z.number().int().min(0).default(0).describe("Pagination offset"),
37
+ },
38
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
39
+ }, async (params) => {
40
+ const conditions = [];
41
+ const binds = [];
42
+ if (params.search) {
43
+ conditions.push("(name LIKE ? OR email LIKE ?)");
44
+ const s = `%${params.search}%`;
45
+ binds.push(s, s);
46
+ }
47
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
48
+ const countResult = await queryFirst(`SELECT COUNT(*) as total FROM users ${where}`, binds);
49
+ const total = countResult?.total ?? 0;
50
+ const rows = await query(`SELECT * FROM users ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`, [...binds, params.limit, params.offset]);
51
+ const output = { total, count: rows.results.length, offset: params.offset, users: rows.results, has_more: total > params.offset + rows.results.length };
52
+ return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] };
53
+ });
54
+ }
55
+ //# sourceMappingURL=students.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"students.js","sourceRoot":"","sources":["../../src/tools/students.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@taotao7/bypass-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for bypass-saas platform management — courses, articles, students, orders",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "bypass-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "keywords": [
15
+ "mcp",
16
+ "mcp-server",
17
+ "cloudflare",
18
+ "d1",
19
+ "bypass-saas"
20
+ ],
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/taotao7/bypass-saas",
25
+ "directory": "packages/mcp"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "scripts": {
31
+ "start": "node dist/index.js",
32
+ "dev": "tsx watch src/index.ts",
33
+ "build": "tsc",
34
+ "prepublishOnly": "tsc"
35
+ },
36
+ "dependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.6.1",
38
+ "zod": "^3.23.8"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.10.0",
42
+ "tsx": "^4.19.2",
43
+ "typescript": "^5.7.2"
44
+ }
45
+ }