Skip to content

Commit a3cafd2

Browse files
committed
feat: download template
1 parent 58845fd commit a3cafd2

File tree

3 files changed

+418
-271
lines changed

3 files changed

+418
-271
lines changed

mcp/src/cli.ts

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
44
import { createCloudBaseMcpServer } from "./server.js";
5-
import { telemetryReporter, reportToolkitLifecycle } from "./utils/telemetry.js";
6-
import { info, warn } from './utils/logger.js';
5+
import {
6+
telemetryReporter,
7+
reportToolkitLifecycle,
8+
} from "./utils/telemetry.js";
9+
import { info, warn } from "./utils/logger.js";
710

811
/**
912
* Parse command line arguments
@@ -19,25 +22,32 @@ function parseCommandLineArgs(): {
1922

2023
for (let i = 0; i < args.length; i++) {
2124
const arg = args[i];
22-
23-
if (arg === '--cloud-mode') {
25+
26+
if (arg === "--cloud-mode") {
2427
cloudMode = true;
25-
} else if (arg === '--integration-ide' && i + 1 < args.length) {
28+
} else if (arg === "--integration-ide" && i + 1 < args.length) {
2629
ide = args[i + 1];
2730
i++; // Skip the next argument since we consumed it
28-
} else if (arg.startsWith('--integration-ide=')) {
29-
ide = arg.split('=')[1];
31+
} else if (arg.startsWith("--integration-ide=")) {
32+
ide = arg.split("=")[1];
3033
}
3134
}
3235

3336
return { cloudMode, ide };
3437
}
3538

3639
// 劫持 console.log/info/warn,防止污染 stdout 协议流
37-
const joinArgs = (...args: any[]) => args.map(a => {
38-
if (typeof a === 'string') return a;
39-
try { return JSON.stringify(a); } catch { return String(a); }
40-
}).join(' ');
40+
const joinArgs = (...args: any[]) =>
41+
args
42+
.map((a) => {
43+
if (typeof a === "string") return a;
44+
try {
45+
return JSON.stringify(a);
46+
} catch {
47+
return String(a);
48+
}
49+
})
50+
.join(" ");
4151

4252
(globalThis as any).console._originLog = console.log;
4353
(globalThis as any).console._originInfo = console.info;
@@ -53,7 +63,8 @@ console.warn = (...args) => warn(joinArgs(...args));
5363
const startTime = Date.now();
5464

5565
// 在测试环境中禁用遥测,避免网络连接问题
56-
const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';
66+
const isTestEnvironment =
67+
process.env.NODE_ENV === "test" || process.env.VITEST === "true";
5768
const enableTelemetry = !isTestEnvironment;
5869

5970
// Parse command line arguments
@@ -64,32 +75,31 @@ if (cloudMode) {
6475
info("Starting CloudBase MCP Server in cloud mode");
6576
}
6677

67-
ide = ide || process.env.INTEGRATION_IDE;
78+
ide = ide || process.env.INTEGRATION_IDE;
6879

6980
if (ide) {
7081
info(`Integration IDE: ${ide}`);
7182
}
7283

73-
7484
// Create server instance with conditional telemetry and CLI options
7585
const server = createCloudBaseMcpServer({
7686
name: "cloudbase-mcp",
7787
version: "1.0.0",
7888
enableTelemetry,
7989
cloudMode,
80-
ide
90+
ide,
8191
});
8292

8393
async function main() {
8494
const transport = new StdioServerTransport();
85-
await server.connect(transport);
95+
await (await server).connect(transport);
8696
info("TencentCloudBase MCP Server running on stdio");
8797

8898
// 上报启动信息
8999
if (telemetryReporter.isEnabled()) {
90100
await reportToolkitLifecycle({
91-
event: 'start',
92-
ide
101+
event: "start",
102+
ide,
93103
});
94104
}
95105
}
@@ -100,40 +110,40 @@ function setupExitHandlers() {
100110
if (telemetryReporter.isEnabled()) {
101111
const duration = Date.now() - startTime;
102112
await reportToolkitLifecycle({
103-
event: 'exit',
113+
event: "exit",
104114
duration,
105115
exitCode,
106116
error: signal ? `Process terminated by signal: ${signal}` : undefined,
107-
ide
117+
ide,
108118
});
109119
}
110120
};
111121

112122
// 正常退出
113-
process.on('exit', (code) => {
123+
process.on("exit", (code) => {
114124
// 注意:exit 事件中不能使用异步操作,所以这里只能同步处理
115125
// 异步上报在其他信号处理中完成
116126
});
117127

118128
// 异常退出处理
119-
process.on('SIGINT', async () => {
120-
await handleExit(0, 'SIGINT');
129+
process.on("SIGINT", async () => {
130+
await handleExit(0, "SIGINT");
121131
process.exit(0);
122132
});
123133

124-
process.on('SIGTERM', async () => {
125-
await handleExit(0, 'SIGTERM');
134+
process.on("SIGTERM", async () => {
135+
await handleExit(0, "SIGTERM");
126136
process.exit(0);
127137
});
128138

129-
process.on('uncaughtException', async (error) => {
130-
console.error('Uncaught Exception:', error);
139+
process.on("uncaughtException", async (error) => {
140+
console.error("Uncaught Exception:", error);
131141
await handleExit(1, `uncaughtException: ${error.message}`);
132142
process.exit(1);
133143
});
134144

135-
process.on('unhandledRejection', async (reason) => {
136-
console.error('Unhandled Rejection:', reason);
145+
process.on("unhandledRejection", async (reason) => {
146+
console.error("Unhandled Rejection:", reason);
137147
await handleExit(1, `unhandledRejection: ${String(reason)}`);
138148
process.exit(1);
139149
});
@@ -149,13 +159,13 @@ main().catch(async (error) => {
149159
if (telemetryReporter.isEnabled()) {
150160
const duration = Date.now() - startTime;
151161
await reportToolkitLifecycle({
152-
event: 'exit',
162+
event: "exit",
153163
duration,
154164
exitCode: 1,
155165
error: `Startup failed: ${error.message}`,
156-
ide
166+
ide,
157167
});
158168
}
159169

160170
process.exit(1);
161-
});
171+
});

mcp/src/server.ts

Lines changed: 66 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { registerEnvTools } from "./tools/env.js";
66
import { registerFunctionTools } from "./tools/functions.js";
77
import { registerHostingTools } from "./tools/hosting.js";
88
import { registerInteractiveTools } from "./tools/interactive.js";
9-
import { registerRagTools } from './tools/rag.js';
9+
import { registerRagTools } from "./tools/rag.js";
1010
import { registerSetupTools } from "./tools/setup.js";
1111
import { registerStorageTools } from "./tools/storage.js";
1212
// import { registerMiniprogramTools } from "./tools/miniprogram.js";
@@ -19,15 +19,28 @@ import { CloudBaseOptions } from "./types.js";
1919
import { enableCloudMode } from "./utils/cloud-mode.js";
2020
import { wrapServerWithTelemetry } from "./utils/tool-wrapper.js";
2121

22-
2322
// 插件定义
2423
interface PluginDefinition {
2524
name: string;
26-
register: (server: ExtendedMcpServer) => void;
25+
register: (server: ExtendedMcpServer) => void | Promise<void>;
2726
}
2827

2928
// 默认插件列表
30-
const DEFAULT_PLUGINS = ['env', 'database', 'functions', 'hosting', 'storage', 'setup', 'interactive', 'rag', 'cloudrun', 'gateway', 'download', 'security-rule', 'invite-code'];
29+
const DEFAULT_PLUGINS = [
30+
"env",
31+
"database",
32+
"functions",
33+
"hosting",
34+
"storage",
35+
"setup",
36+
"interactive",
37+
"rag",
38+
"cloudrun",
39+
"gateway",
40+
"download",
41+
"security-rule",
42+
"invite-code",
43+
];
3144

3245
function registerDatabase(server: ExtendedMcpServer) {
3346
registerDatabaseTools(server);
@@ -37,20 +50,23 @@ function registerDatabase(server: ExtendedMcpServer) {
3750

3851
// 可用插件映射
3952
const AVAILABLE_PLUGINS: Record<string, PluginDefinition> = {
40-
env: { name: 'env', register: registerEnvTools },
41-
database: { name: 'database', register: registerDatabase },
42-
functions: { name: 'functions', register: registerFunctionTools },
43-
hosting: { name: 'hosting', register: registerHostingTools },
44-
storage: { name: 'storage', register: registerStorageTools },
45-
setup: { name: 'setup', register: registerSetupTools },
46-
interactive: { name: 'interactive', register: registerInteractiveTools },
47-
rag: { name: 'rag', register: registerRagTools },
48-
download: { name: 'download', register: registerDownloadTools },
49-
gateway: { name: 'gateway', register: registerGatewayTools },
53+
env: { name: "env", register: registerEnvTools },
54+
database: { name: "database", register: registerDatabase },
55+
functions: { name: "functions", register: registerFunctionTools },
56+
hosting: { name: "hosting", register: registerHostingTools },
57+
storage: { name: "storage", register: registerStorageTools },
58+
setup: { name: "setup", register: registerSetupTools },
59+
interactive: { name: "interactive", register: registerInteractiveTools },
60+
rag: { name: "rag", register: registerRagTools },
61+
download: { name: "download", register: registerDownloadTools },
62+
gateway: { name: "gateway", register: registerGatewayTools },
5063
// miniprogram: { name: 'miniprogram', register: registerMiniprogramTools },
51-
'security-rule': { name: 'security-rule', register: registerSecurityRuleTools },
52-
'invite-code': { name: 'invite-code', register: registerInviteCodeTools },
53-
cloudrun: { name: 'cloudrun', register: registerCloudRunTools },
64+
"security-rule": {
65+
name: "security-rule",
66+
register: registerSecurityRuleTools,
67+
},
68+
"invite-code": { name: "invite-code", register: registerInviteCodeTools },
69+
cloudrun: { name: "cloudrun", register: registerCloudRunTools },
5470
};
5571

5672
/**
@@ -59,23 +75,23 @@ const AVAILABLE_PLUGINS: Record<string, PluginDefinition> = {
5975
function parseEnabledPlugins(): string[] {
6076
const enabledEnv = process.env.CLOUDBASE_MCP_PLUGINS_ENABLED;
6177
const disabledEnv = process.env.CLOUDBASE_MCP_PLUGINS_DISABLED;
62-
78+
6379
let enabledPlugins: string[];
64-
80+
6581
if (enabledEnv) {
6682
// 如果指定了启用的插件,使用指定的插件
67-
enabledPlugins = enabledEnv.split(',').map(p => p.trim());
83+
enabledPlugins = enabledEnv.split(",").map((p) => p.trim());
6884
} else {
6985
// 否则使用默认插件
7086
enabledPlugins = [...DEFAULT_PLUGINS];
7187
}
72-
88+
7389
if (disabledEnv) {
7490
// 从启用列表中移除禁用的插件
75-
const disabledPlugins = disabledEnv.split(',').map(p => p.trim());
76-
enabledPlugins = enabledPlugins.filter(p => !disabledPlugins.includes(p));
91+
const disabledPlugins = disabledEnv.split(",").map((p) => p.trim());
92+
enabledPlugins = enabledPlugins.filter((p) => !disabledPlugins.includes(p));
7793
}
78-
94+
7995
return enabledPlugins;
8096
}
8197

@@ -89,37 +105,37 @@ export interface ExtendedMcpServer extends McpServer {
89105
* Create and configure a CloudBase MCP Server instance
90106
* @param options Server configuration options
91107
* @returns Configured McpServer instance
92-
*
108+
*
93109
* @example
94110
* import { createCloudBaseMcpServer } from "@cloudbase/mcp-server";
95111
* import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
96-
*
97-
* const server = createCloudBaseMcpServer({ cloudBaseOptions: {
112+
*
113+
* const server = createCloudBaseMcpServer({ cloudBaseOptions: {
98114
* envId, // 环境ID
99115
* secretId, // 腾讯云密钥ID
100116
* secretKey, // 腾讯云密钥
101117
* region, // 地域,默认是 ap-shanghai
102118
* token // 临时密钥,有有效期限制,生成密钥时可控制
103119
* } });
104-
*
120+
*
105121
* const transport = new StdioServerTransport();
106122
* await server.connect(transport);
107123
*/
108-
export function createCloudBaseMcpServer(options?: {
124+
export async function createCloudBaseMcpServer(options?: {
109125
name?: string;
110126
version?: string;
111127
enableTelemetry?: boolean;
112128
cloudBaseOptions?: CloudBaseOptions;
113129
cloudMode?: boolean;
114130
ide?: string;
115-
}): ExtendedMcpServer {
131+
}): Promise<ExtendedMcpServer> {
116132
const {
117133
name = "cloudbase-mcp",
118134
version = "1.0.0",
119135
enableTelemetry = true,
120136
cloudBaseOptions,
121137
cloudMode = false,
122-
ide
138+
ide,
123139
} = options ?? {};
124140

125141
// Enable cloud mode if specified
@@ -128,15 +144,18 @@ export function createCloudBaseMcpServer(options?: {
128144
}
129145

130146
// Create server instance
131-
const server = new McpServer({
132-
name,
133-
version
134-
}, {
135-
capabilities: {
136-
tools: {},
137-
...(ide === 'CodeBuddy' ? { logging: {}}: {})
147+
const server = new McpServer(
148+
{
149+
name,
150+
version,
151+
},
152+
{
153+
capabilities: {
154+
tools: {},
155+
...(ide === "CodeBuddy" ? { logging: {} } : {}),
156+
},
138157
},
139-
}) as ExtendedMcpServer;
158+
) as ExtendedMcpServer;
140159

141160
// Store cloudBaseOptions in server instance for tools to access
142161
if (cloudBaseOptions) {
@@ -155,11 +174,11 @@ export function createCloudBaseMcpServer(options?: {
155174

156175
// 根据配置注册插件
157176
const enabledPlugins = parseEnabledPlugins();
158-
177+
159178
for (const pluginName of enabledPlugins) {
160179
const plugin = AVAILABLE_PLUGINS[pluginName];
161180
if (plugin) {
162-
plugin.register(server);
181+
await plugin.register(server);
163182
}
164183
}
165184

@@ -169,13 +188,16 @@ export function createCloudBaseMcpServer(options?: {
169188
/**
170189
* Get the default configured CloudBase MCP Server
171190
*/
172-
export function getDefaultServer(): ExtendedMcpServer {
191+
export function getDefaultServer(): Promise<ExtendedMcpServer> {
173192
return createCloudBaseMcpServer();
174193
}
175194

176195
// Re-export types and utilities that might be useful
177196
export type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
178197
export { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
179198
export { error, info, warn } from "./utils/logger.js";
180-
export { reportToolCall, reportToolkitLifecycle, telemetryReporter } from "./utils/telemetry.js";
181-
199+
export {
200+
reportToolCall,
201+
reportToolkitLifecycle,
202+
telemetryReporter,
203+
} from "./utils/telemetry.js";

0 commit comments

Comments
 (0)