Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,13 @@
## 使用方法

请参考我们的[使用指南](https://docs.sifli.com/projects/codekit)以了解如何安装和使用该插件。

## MCP Server

从当前版本开始,CodeKit 内置了一个可选的 MCP Server,供其他 agent 工具或 IDE 通过 MCP 调用插件能力。

- 在命令面板中执行 `Start SiFli MCP Server` 启动服务
- 执行 `Show SiFli MCP Connection Info` 获取连接地址和 Bearer Token
- 可通过设置 `sifli-sdk-codekit.mcp.autoStart` 让扩展激活时自动启动 MCP

当前实现运行在 VS Code 扩展宿主内,因此外部客户端连接 MCP 时,需要本插件所在的 VS Code 实例保持运行。
10 changes: 10 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,13 @@
## How to Use

Please refer to our [User Guide](https://docs.sifli.com/projects/codekit) to learn how to install and use the extension.

## MCP Server

CodeKit now includes an optional embedded MCP Server so other agent tools or IDEs can call extension capabilities through MCP.

- Run `Start SiFli MCP Server` from the command palette to start the server
- Run `Show SiFli MCP Connection Info` to get the URL and Bearer token
- Enable `sifli-sdk-codekit.mcp.autoStart` to start it automatically on activation

The current implementation runs inside the VS Code extension host, so the VS Code instance that hosts CodeKit must remain running while external clients use the MCP connection.
39 changes: 38 additions & 1 deletion l10n/bundle.l10n.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,5 +340,42 @@
"Unsupported monitor baud rate: {0}": "Unsupported monitor baud rate: {0}",
"shell.command step requires a trusted workspace.": "shell.command step requires a trusted workspace.",
"shell.command step requires args.command.": "shell.command step requires args.command.",
"wait: {0}, continueOnError: {1}": "wait: {0}, continueOnError: {1}"
"wait: {0}, continueOnError: {1}": "wait: {0}, continueOnError: {1}",
"MCP Server": "MCP Server",
"View and configure the embedded MCP server": "View and configure the embedded MCP server",
"Disabled": "Disabled",
"Running": "Running",
"Stopped": "Stopped",
"Click to enable MCP": "Click to enable MCP",
"Click to stop the embedded MCP server": "Click to stop the embedded MCP server",
"Click to start the embedded MCP server": "Click to start the embedded MCP server",
"Toggle MCP status": "Toggle MCP status",
"Auto Start": "Auto Start",
"Toggle MCP auto-start": "Toggle MCP auto-start",
"Click to toggle automatic MCP server startup": "Click to toggle automatic MCP server startup",
"On": "On",
"Off": "Off",
"Configure Endpoint": "Configure Endpoint",
"Configure MCP endpoint": "Configure MCP endpoint",
"Edit MCP host and port": "Edit MCP host and port",
"Edit MCP host, port and optional fixed token": "Edit MCP host, port and optional fixed token",
"Copy Connection Info": "Copy Connection Info",
"Copy MCP connection info": "Copy MCP connection info",
"Copy JSON configuration for external MCP clients": "Copy JSON configuration for external MCP clients",
"JSON": "JSON",
"Starts server": "Starts server",
"SiFli MCP server stopped.": "SiFli MCP server stopped.",
"Enable MCP first in the sidebar or settings.": "Enable MCP first in the sidebar or settings.",
"SiFli MCP server is not running.": "SiFli MCP server is not running.",
"SiFli MCP JSON configuration copied to clipboard.": "SiFli MCP JSON configuration copied to clipboard.",
"SiFli MCP enabled.": "SiFli MCP enabled.",
"SiFli MCP disabled.": "SiFli MCP disabled.",
"SiFli MCP auto-start enabled.": "SiFli MCP auto-start enabled.",
"SiFli MCP auto-start disabled.": "SiFli MCP auto-start disabled.",
"MCP host": "MCP host",
"Host is required.": "Host is required.",
"MCP port (0 means auto-assign)": "MCP port (0 means auto-assign)",
"Port must be an integer between 0 and 65535.": "Port must be an integer between 0 and 65535.",
"Fixed MCP token (leave empty to auto-generate)": "Fixed MCP token (leave empty to auto-generate)",
"Fixed token should be at least 8 characters long.": "Fixed token should be at least 8 characters long."
}
39 changes: 38 additions & 1 deletion l10n/bundle.l10n.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,5 +340,42 @@
"Unsupported monitor baud rate: {0}": "不支持的监视波特率:{0}",
"shell.command step requires a trusted workspace.": "shell.command 步骤需要受信任的工作区。",
"shell.command step requires args.command.": "shell.command 步骤需要配置 args.command。",
"wait: {0}, continueOnError: {1}": "等待:{0},出错继续:{1}"
"wait: {0}, continueOnError: {1}": "等待:{0},出错继续:{1}",
"MCP Server": "MCP 服务器",
"View and configure the embedded MCP server": "查看并配置内置 MCP 服务器",
"Disabled": "已禁用",
"Running": "运行中",
"Stopped": "已停止",
"Click to enable MCP": "点击启用 MCP",
"Click to stop the embedded MCP server": "点击停止内置 MCP 服务器",
"Click to start the embedded MCP server": "点击启动内置 MCP 服务器",
"Toggle MCP status": "切换 MCP 状态",
"Auto Start": "自动启动",
"Toggle MCP auto-start": "切换 MCP 自动启动",
"Click to toggle automatic MCP server startup": "点击切换 MCP 服务器自动启动",
"On": "开",
"Off": "关",
"Configure Endpoint": "配置端点",
"Configure MCP endpoint": "配置 MCP 端点",
"Edit MCP host and port": "编辑 MCP 主机和端口",
"Edit MCP host, port and optional fixed token": "编辑 MCP 主机、端口和可选的固定 token",
"Copy Connection Info": "复制连接信息",
"Copy MCP connection info": "复制 MCP 连接信息",
"Copy JSON configuration for external MCP clients": "复制供外部 MCP 客户端使用的 JSON 配置",
"JSON": "JSON",
"Starts server": "将启动服务",
"SiFli MCP server stopped.": "SiFli MCP 服务器已停止。",
"Enable MCP first in the sidebar or settings.": "请先在侧边栏或设置中启用 MCP。",
"SiFli MCP server is not running.": "SiFli MCP 服务器未运行。",
"SiFli MCP JSON configuration copied to clipboard.": "SiFli MCP JSON 配置已复制到剪贴板。",
"SiFli MCP enabled.": "SiFli MCP 已启用。",
"SiFli MCP disabled.": "SiFli MCP 已禁用。",
"SiFli MCP auto-start enabled.": "SiFli MCP 自动启动已启用。",
"SiFli MCP auto-start disabled.": "SiFli MCP 自动启动已禁用。",
"MCP host": "MCP 主机",
"Host is required.": "主机不能为空。",
"MCP port (0 means auto-assign)": "MCP 端口(0 表示自动分配)",
"Port must be an integer between 0 and 65535.": "端口必须是 0 到 65535 之间的整数。",
"Fixed MCP token (leave empty to auto-generate)": "固定 MCP token(留空则自动生成)",
"Fixed token should be at least 8 characters long.": "固定 token 长度至少应为 8 个字符。"
}
68 changes: 67 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,46 @@
"command": "extension.runStatusBarButton",
"title": "%command.runStatusBarButton.title%"
},
{
"command": "extension.mcp.start",
"title": "%command.mcp.start.title%"
},
{
"command": "extension.mcp.stop",
"title": "%command.mcp.stop.title%"
},
{
"command": "extension.mcp.showConnectionInfo",
"title": "%command.mcp.showConnectionInfo.title%"
},
{
"command": "extension.mcp.toggleEnabled",
"title": "%command.mcp.toggleEnabled.title%"
},
{
"command": "extension.mcp.toggleAutoStart",
"title": "%command.mcp.toggleAutoStart.title%"
},
{
"command": "extension.mcp.configureEndpoint",
"title": "%command.mcp.configureEndpoint.title%"
},
{
"command": "extension.mcp.showLogs",
"title": "%command.mcp.showLogs.title%"
},
{
"command": "sifliSidebar.refresh",
"title": "%command.sifliSidebar.refresh.title%",
"icon": "$(refresh)"
}
],
"mcpServerDefinitionProviders": [
{
"id": "sifli-sdk-codekit.embedded-mcp",
"label": "%mcp.provider.label%"
}
],
"languageModelTools": [
{
"name": "sifli-sdk-codekit_getProjectState",
Expand Down Expand Up @@ -758,6 +792,36 @@
}
}
}
},
"sifli-sdk-codekit.mcp.enabled": {
"type": "boolean",
"default": true,
"description": "%configuration.sifli.mcp.enabled.description%",
"scope": "machine-overridable"
},
"sifli-sdk-codekit.mcp.autoStart": {
"type": "boolean",
"default": false,
"description": "%configuration.sifli.mcp.autoStart.description%",
"scope": "machine-overridable"
},
"sifli-sdk-codekit.mcp.host": {
"type": "string",
"default": "127.0.0.1",
"description": "%configuration.sifli.mcp.host.description%",
"scope": "machine-overridable"
},
"sifli-sdk-codekit.mcp.port": {
"type": "number",
"default": 0,
"description": "%configuration.sifli.mcp.port.description%",
"scope": "machine-overridable"
},
"sifli-sdk-codekit.mcp.fixedToken": {
"type": "string",
"default": "",
"description": "%configuration.sifli.mcp.fixedToken.description%",
"scope": "machine-overridable"
}
}
},
Expand Down Expand Up @@ -1365,7 +1429,9 @@
"typescript": "^5.8.3"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.27.1",
"axios": "^1.10.0",
"serialport": "^13.0.0"
"serialport": "^13.0.0",
"zod": "^3.25.0"
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@modelcontextprotocol/sdk pulls in zod@^3.25 || ^4.0, while this extension now depends on zod@^3.25.0. The lockfile shows this resolves to two different runtime copies (zod v4 for the MCP SDK and zod v3 for the extension). Passing Zod schemas/shapes across different Zod major versions can break runtime validation or type checks inside the MCP SDK. Consider forcing a single Zod version via Yarn resolutions (pick either v3.25.x or v4.x) and align the direct zod dependency accordingly to avoid duplicate installs.

Suggested change
"zod": "^3.25.0"
"zod": "^3.25.0"
},
"resolutions": {
"zod": "3.25.0"

Copilot uses AI. Check for mistakes.
}
}
13 changes: 13 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@
"command.statusBarButtons.copy.title": "Copy status bar button",
"command.statusBarButtons.overrideDefault.title": "Override default status bar button in workspace",
"command.runStatusBarButton.title": "Run status bar button",
"command.mcp.start.title": "Start SiFli MCP server",
"command.mcp.stop.title": "Stop SiFli MCP server",
"command.mcp.showConnectionInfo.title": "Copy SiFli MCP JSON configuration",
"command.mcp.toggleEnabled.title": "Toggle SiFli MCP enabled",
"command.mcp.toggleAutoStart.title": "Toggle SiFli MCP auto start",
"command.mcp.configureEndpoint.title": "Configure SiFli MCP endpoint",
"command.mcp.showLogs.title": "Show SiFli MCP logs",
"command.sifliSidebar.refresh.title": "Refresh",
"mcp.provider.label": "SiFli CodeKit",
"view.sifliSdkManager.name": "SDK Manager",
"viewsContainer.sifli-sidebar.title": "SiFli SDK",
"configuration.sifli.title": "SiFli Extension Configuration",
Expand All @@ -46,6 +54,11 @@
"configuration.sifli.customBoardSearchPath.description": "Optional: Absolute or relative path to a directory containing custom SiFli board configurations. (e.g., C:\\MyBoards or ../my_boards)",
"configuration.sifli.workflows.description": "Workflow definitions. Use workspace settings for team sharing; user-level entries with the same id override workspace entries.",
"configuration.sifli.statusBar.buttons.description": "Status bar button definitions for workflows/commands. Use existing ids (compile/rebuild/clean/download/menuconfig/deviceMonitor) to override default buttons.",
"configuration.sifli.mcp.enabled.description": "Enable the embedded MCP server capability for SiFli SDK CodeKit.",
"configuration.sifli.mcp.autoStart.description": "Start the embedded MCP server automatically when the extension activates.",
"configuration.sifli.mcp.host.description": "Host address for the embedded MCP server.",
"configuration.sifli.mcp.port.description": "Port for the embedded MCP server. Use 0 to let the OS choose a free port.",
"configuration.sifli.mcp.fixedToken.description": "Optional fixed Bearer token for the embedded MCP server. Leave empty to generate a random token each time.",
"lmTool.getProjectState.displayName": "Get SiFli project state",
"lmTool.getProjectState.modelDescription": "Get the current SiFli project state, including board, serial port, SDK, workflow counts, and monitor state.",
"lmTool.listWorkflows.displayName": "List SiFli workflows",
Expand Down
13 changes: 13 additions & 0 deletions package.nls.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@
"command.statusBarButtons.copy.title": "复制状态栏按钮",
"command.statusBarButtons.overrideDefault.title": "在工作区覆盖默认状态栏按钮",
"command.runStatusBarButton.title": "执行状态栏按钮动作",
"command.mcp.start.title": "启动 SiFli MCP 服务器",
"command.mcp.stop.title": "停止 SiFli MCP 服务器",
"command.mcp.showConnectionInfo.title": "复制 SiFli MCP JSON 配置",
"command.mcp.toggleEnabled.title": "切换 SiFli MCP 启用状态",
"command.mcp.toggleAutoStart.title": "切换 SiFli MCP 自动启动",
"command.mcp.configureEndpoint.title": "配置 SiFli MCP 端点",
"command.mcp.showLogs.title": "显示 SiFli MCP 日志",
"command.sifliSidebar.refresh.title": "刷新",
"mcp.provider.label": "SiFli CodeKit",
"view.sifliSdkManager.name": "SDK 管理",
"viewsContainer.sifli-sidebar.title": "SiFli SDK",
"configuration.sifli.title": "SiFli 扩展设置",
Expand All @@ -46,6 +54,11 @@
"configuration.sifli.customBoardSearchPath.description": "可选:包含自定义 SiFli 板卡配置的目录绝对/相对路径。(例如:C:\\MyBoards 或 ../my_boards)",
"configuration.sifli.workflows.description": "工作流定义。建议使用工作区设置进行团队共享;用户级相同 id 条目会覆盖工作区条目。",
"configuration.sifli.statusBar.buttons.description": "用于工作流/命令的状态栏按钮定义。使用已有 id(compile/rebuild/clean/download/menuconfig/deviceMonitor)可覆盖默认按钮。",
"configuration.sifli.mcp.enabled.description": "启用 SiFli SDK CodeKit 内置的 MCP 服务器能力。",
"configuration.sifli.mcp.autoStart.description": "在扩展激活时自动启动内置 MCP 服务器。",
"configuration.sifli.mcp.host.description": "内置 MCP 服务器监听的主机地址。",
"configuration.sifli.mcp.port.description": "内置 MCP 服务器监听的端口。填 0 时由系统自动分配空闲端口。",
"configuration.sifli.mcp.fixedToken.description": "内置 MCP 服务器可选的固定 Bearer token。留空时每次启动自动生成随机 token。",
"lmTool.getProjectState.displayName": "获取 SiFli 工程状态",
"lmTool.getProjectState.modelDescription": "获取当前 SiFli 工程状态,包括板卡、串口、SDK、工作流数量和监视器状态。",
"lmTool.listWorkflows.displayName": "列出 SiFli 工作流",
Expand Down
63 changes: 15 additions & 48 deletions src/commands/configCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { BoardService } from '../services/boardService';
import { SerialPortService } from '../services/serialPortService';
import { SerialMonitorService } from '../services/serialMonitorService';
import { SdkService } from '../services/sdkService';
import { ClangdService } from '../services/clangdService';
import { StatusBarProvider } from '../providers/statusBarProvider';
import { HAS_RUN_INITIAL_SETUP_KEY } from '../constants';
import { getProjectInfo } from '../utils/projectUtils';

export class ConfigCommands {
private static instance: ConfigCommands;
Expand All @@ -17,6 +17,7 @@ export class ConfigCommands {
private serialPortService: SerialPortService;
private serialMonitorService: SerialMonitorService;
private sdkService: SdkService;
private clangdService: ClangdService;
private statusBarProvider: StatusBarProvider;

private constructor() {
Expand All @@ -25,6 +26,7 @@ export class ConfigCommands {
this.serialPortService = SerialPortService.getInstance();
this.serialMonitorService = SerialMonitorService.getInstance();
this.sdkService = SdkService.getInstance();
this.clangdService = ClangdService.getInstance();
this.statusBarProvider = StatusBarProvider.getInstance();
}

Expand Down Expand Up @@ -200,60 +202,25 @@ export class ConfigCommands {
*/
public async configureClangd(): Promise<void> {
try {
// 获取当前选择的芯片模组
const selectedBoard = this.configService.getSelectedBoardName();

if (!selectedBoard || selectedBoard === 'N/A') {
vscode.window.showWarningMessage(vscode.l10n.t('Select a board first.'));
return;
}

// 获取工作区文件夹
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (!workspaceFolder) {
vscode.window.showErrorMessage(vscode.l10n.t('No workspace folder found.'));
return;
}

// 构建 .vscode/settings.json 路径
const vscodeDir = path.join(workspaceFolder.uri.fsPath, '.vscode');
const settingsPath = path.join(vscodeDir, 'settings.json');

// 确保 .vscode 目录存在
if (!fs.existsSync(vscodeDir)) {
fs.mkdirSync(vscodeDir, { recursive: true });
}

// 读取现有的 settings.json
let settings: any = {};
if (fs.existsSync(settingsPath)) {
const content = fs.readFileSync(settingsPath, 'utf-8');
try {
settings = JSON.parse(content);
} catch (error) {
console.error('[ConfigCommands] Error parsing settings.json:', error);
settings = {};
const result = await this.clangdService.configure();
if (!result.success) {
const message = result.message ?? vscode.l10n.t('Failed to configure clangd.');
if (message === vscode.l10n.t('Select a board first.')) {
vscode.window.showWarningMessage(message);
} else {
vscode.window.showErrorMessage(message);
}
return;
}

// 构建 compile-commands-dir 路径
const projectInfo = getProjectInfo();
const projectRelativePath = projectInfo?.projectEntryRelativePath || 'project';
const compileCommandsDir = `\${workspaceFolder}/${projectRelativePath}/build_${selectedBoard}_hcpu`;

// 更新 clangd.arguments
settings['clangd.arguments'] = [`--compile-commands-dir=${compileCommandsDir}`];

// 写入 settings.json
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), 'utf-8');

console.log(`[ConfigCommands] clangd 配置已更新: ${settingsPath}`);

// 显示成功消息并提示重启
const restartAction = vscode.l10n.t('Restart VS Code');
const laterAction = vscode.l10n.t('Later');
const action = await vscode.window.showInformationMessage(
vscode.l10n.t('clangd configuration completed for board {0}. Restart VS Code to apply.', selectedBoard),
vscode.l10n.t(
'clangd configuration completed for board {0}. Restart VS Code to apply.',
result.selectedBoard ?? ''
),
restartAction,
laterAction
);
Expand Down
Loading
Loading