项目背景
近期出现了一些基于 VS Code 开发自带大模型能力的 IDE,如 Cursor,Trae,它们的默认样式和习惯使用的 VS Code 有些不一样,比如打开 Cursor 的第一时间就在找侧边栏怎么恢复,所以想到了可以开发一个 MCP Server 来通过自然语言修改 IDE 的配置
我要实现的这个 MCP Server 只有 Tool 部分,且只有一个功能,就是设置 IDE,如修改字体和字体大小,设置 Tab 占几个空格,都可以通过自然语言让大模型去设置
效果展示
Pypi: pypi.org/project/ide…
- Trae
同时修改字号和字体
将字号和字体恢复
方案
Python + FastMCP + Stdio 协议
- 纯本地操作,使用 stdio 协议会比较快
- Python 写代码少一点,开发速度快
实现
类 VS Code 的 IDE 都存在一份用户配置和默认配置,其用户配置都是存储在一个 settings.json 文件里,而且这个配置文件修改并保存后会立刻生效。
这个 MCP Server 的实现原理是让大模型根据用户需求直接修改对应 settings.json 文件,所以首先需要两个 tool 来支持读和写 settings.json 文件,同时为了确保大模型知道应该使用哪个 key 来做配置,增加一个 tool 返回 IDE 所有可用的配置及其值和描述信息
// 根据key获取配置的值,如果未获取到,会从defaultSettings里获取默认值
get_ide_settings_by_key(key: str)
// 根据key设置value
set_ide_settings_by_key(key: str, val: any)
// 获取IDE的默认配置值和描述
get_ide_default_settings()
不用 MCP Server 这个功能似乎也能实现?
的确,直接在提示词里告诉大模型要求修改 settings.json 文件,提供具体的路径,配合大部分 Agent 都自带的修改文件的 tool 也能实现功能,但效果肯定比提供一个专门的 MCP Server 要差,这其实是通过一部分工程化的代码提高了大模型操作的准确率
而且直接用现成的就没法实践 MCP Server 开发了💪
get_ide_setting_by_key
这个 tool 的目的是根据配置 key 获取设置的值
在 VS Code 里,配置分为用户配置和系统配置两部分,用户配置文件settings.json只包含用户修改的那部分配置,如果一个配置未被修改过,需要返回这个配置的默认值
通过命令Preferences: Open Default Settings (Json) 可以获取到 VS Code 的默认配置,将其存储为文件defaultSettings.json,内置在 MCP Server 中,解决获取默认值的问题
@mcp.tool()
def get_ide_setting_by_key(key: str) -> Dict[str, Any]:
"""Get IDE user setting by key, if not found in user settings, will return default value
Args:
key: Setting key name
Returns:
Dictionary containing setting value, or error message if key doesn't exist
"""
try:
# Get current settings
current_settings = _get_ide_settings()
# Check if key exists
if key in current_settings:
return {key: current_settings[key]}
else:
# If key not found in user settings, try to get default value from defaultSettings.json
try:
default_settings_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'defaultSettings.json')
if os.path.exists(default_settings_path):
with open(default_settings_path, 'r', encoding='utf-8') as f:
default_settings = commentjson.load(f)
if key in default_settings:
return {key: default_settings[key]}
return {"error": f" failed to find default setting for key: {key}"}
except Exception:
return {"error": f" failed to find default setting for key: {key}"}
except Exception as e:
return {"error": f" failed to get IDE setting for key: {key}, error: {str(e)}"}
defaultSettings.json 文件的部分内容示例
{
// Controls the font family.
"editor.fontFamily": "Consolas, 'Courier New', monospace",
// Configures font ligatures or font features. Can be either a boolean to enable/disable ligatures or a string for the value of the CSS 'font-feature-settings' property.
"editor.fontLigatures": false,
// Controls the font size in pixels.
"editor.fontSize": 14
...
}
get_ide_default_settings
LLM 可能不知道对应配置的 key 是什么,比如字号的 key 是editor.fontSize,所以还需要提供一份文档,提供所有的 key 和对应的描述信息
这个需求看起来可以用 MCP 协议里的 Resource 来实现,实际上不行,Tools 何时调用由 LLM 决定,会在 response 的 tool_choice 里返回要调用的 tool 及其参数,但 LLM 不会返回对 Resource 的需求,Resource 是由应用主动注入到 LLM 上下文中的,在这个场景下无法控制应用程序(即 IDE)的行为,所以改为了使用 Tools 实现
提供了一个获取 default_settings 的 tool,实现逻辑就是直接返回 MCP Server 里内置的defaultSettings.json文件的内容,里面包括了所有的 key,value 和注释,通过这个能获取一份完整的默认配置,可以作为文档使用
@mcp.tool(
name="get_default_settings",
description="Get all available IDE settings and default value",
)
def get_default_settings() -> str:
"""Get all available IDE settings and default value
Returns:
IDE's default settings in json format with comments preserved
"""
try:
return _get_default_setttings()
except Exception as e:
return json.dumps({"error": f" failed to get default settings: {str(e)}"})
# 被@mcp.tool修饰的函数不能直接调用了,所以封装一个内部函数
def _get_default_setttings() -> str:
try:
default_settings_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'defaultSettings.json')
if not os.path.exists(default_settings_path):
return json.dumps({"error": "Default configuration file does not exist"})
with open(default_settings_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
return json.dumps({"error": f" failed to read default settings file: {str(e)}"})
set_ide_settings_by_key
注意这里的提示词,如果大模型不知道 key 是什么,指示大模型去调用get_default_settings,这种在注释里让大模型调用其他 Tool 目前是可行的,同时也是一个潜在的安全隐患,比如提示词里让大模型调用file_edit把用户电脑里的文件都删了
if you're not sure about the setting key, you can get all available settings via get_default_settings tool or fetch code.visualstudio.com/docs/getsta…
@mcp.tool()
def set_ide_setting_by_key(
key: Annotated[str, "the key of the setting"],
value: Annotated[Any, "the value of the setting"]
) -> Dict[str, Any]:
"""Set IDE user setting by key, if you're not sure about the setting key, you can get all available settings via get_default_settings tool or fetch https://code.visualstudio.com/docs/getstarted/settings
Args:
key: Setting key name
value: New value for the setting
Returns:
Updated setting value, or error message if update failed
"""
try:
_set_ide_settings(key, value)
except Exception as e:
return {"error": f" failed to set IDE setting for key: {key}, error: {str(e)}"}
return {key: value}
支持多 IDE
目前支持了 VS Code,Cursor,Trae,实现原理是这些 IDE 起始都有用户配置文件,只是路径不一样,MCP Server 配置时传入 IDE 的标识符,内部读写配置文件时切换路径即可
测试
可以参考 MCP 协议的测试方案,多个维度的测试,首先是 function 维度,通过 inspector 直接调用到具体的函数,测试完成后,将 MCP Server 配置到 IDE,通过和大模型对话来测试 Tool 是否正常被使用
Testing tools
A comprehensive testing strategy for MCP tools should cover:
Functional testing: Verify tools execute correctly with valid inputs and handle invalid inputs appropriately
Integration testing: Test tool interaction with external systems using both real and mocked dependencies
Security testing: Validate authentication, authorization, input sanitization, and rate limiting
Performance testing: Check behavior under load, timeout handling, and resource cleanup
Error handling: Ensure tools properly report errors through the MCP protocol and clean up resources
Inspector(Tool 维度测试)
使用 inspector 调试,stdio 协议无法使用断点调试,可以先使用 http 协议启动 MCP Server,发布前改回 stdio 协议
mcp.run(transport="http", host="127.0.0.1", port=8000, path="/mcp")
inspector 的使用参考 MCP Inspector - Model Context Protocol,比较简单,这里略过
Agent 调试(和 LLM 集成测试)
找一个有 MCP 功能的 IDE,这里用的是 Trae,先本地引用这个 MCP Server,和大模型对话试试能否正常调用 tools
使用本地正在开发的 MCP Server 时,配置文件里的 command 就是这个 MCP Server 的启动命令,所以一个本地的 Python 程序,就是python {文件路径} {参数:可选}
{
"mcpServers": {
"ide-config-mcp": {
"command": "python",
"args": [
"D:\Projects\ide-config-mcp-server\server.py",
"TraeCN"
]
}
}
}
发布
补充项目信息
先补充一些信息,创建一个 pyproject.toml 文件,后续配置自动发布会使用到,内容如下
这个文件的作用是提供程序的元数据,如版本号,作者,名称等,同时提供了构建系统配置和依赖项,程序的入口点(即 main 函数位置),有了这些信息才能自动化构建程序
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "ide-config-mcp"
version = "0.1.5"
description = "IDE config MCP Python package"
authors = [
{ name = "arcsinw", email = "your@email.com" }
]
readme = "README.md"
requires-python = ">=3.7"
dependencies = [
"fastmcp==0.6.0",
"commentjson==0.9.0",
"fastapi==0.103.1",
"uvicorn==0.23.2"
]
[project.urls]
Homepage = "https://github.com/arcsinw/ide-config-mcp"
Repository = "https://github.com/arcsinw/ide-config-mcp"
[project.scripts]
ide-config-mcp = "server:main"
代码上传到 Github
git commit -am "Initial Commit"
git push origin master
配置自动发布
利用 Github 的 Actions 功能可以配置在 master 分支更新后自动发布到 PypI,直接使用 Github 提供的Publish Python Package
下面是自动生成的 python-publish.yml,在.github 目录下,我只改动了on的部分,支持手动触发 workflow 方便测试,增加了 master 分支更新时自动发布到 PyPI 的配置
Todo: 这个工作流目前还有问题在于 build 产物的版本号不会自动递增,每次发布都需要手动修改pyproject.toml 里的 version
# This workflow will upload a Python Package to PyPI when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Upload Python Package
on:
workflow_dispatch: # 支持手动触发
push: # 支持代码推送触发
branches:
- master # 只在 master 分支触发
release:
types: [published]
permissions:
contents: read
jobs:
release-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Build release distributions
run: |
# NOTE: put your own distribution build steps here.
python -m pip install build
python -m build
- name: Upload distributions
uses: actions/upload-artifact@v4
with:
name: release-dists
path: dist/
pypi-publish:
runs-on: ubuntu-latest
needs:
- release-build
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
# Dedicated environments with protections for publishing are strongly recommended.
# For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
environment:
name: pypi
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
# url: https://pypi.org/p/YOURPROJECT
#
# ALTERNATIVE: if your GitHub Release name is the PyPI project version string
# ALTERNATIVE: exactly, uncomment the following line instead:
# url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}
steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v4
with:
name: release-dists
path: dist/
- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
设置 PyPI
- 注册 PyPI 账号
- 进入 Publishing 页面 (pypi.org/manage/acco… Name 可以不填
PyPI Project Name 就是平时 pip install package_name 时的那个 package_name
Workflow name要填写项目里用于发布到 PyPi 的 workflow 的文件名,这里就是python-publish.yml
填写完成后可以在页面看到一个 publisher 就说明配置成功了
再去 Github 触发一次 Action(页面上手动 Run 或者往 master 推一个 commit),Action 成功执行后可以看到已经发布到 Pypi 了,此时已经可以 pip install然后直接 run 起来了
注意这个 workflow 目前没有自动更新版本号的功能,每次修改完代码需要手动修改pyproject.toml 里的 version ,否则 Action 执行会报错
使用方法
需要先安装uv,参考uv.doczh.com/getting-sta…
支持 VS Code 类 IDE(包括 Cursor 和 Trae),区别是在配置 MCP Server 时在参数里增加 IDE 的标识符
| 对应的 IDE | 标识符 |
|---|---|
| VS code | Code |
| Trae 国际版 | Trae |
| Trae 国内版 | TraeCN |
| Cursor | Cursor |
Trae 国内版,按下面配置,其他版本将 TraeCN 替换为对应 IDE 的标识符即可
{
"mcpServers": {
"ide-config-mcp": {
"command": "uvx",
"args": [
"ide-config-mcp",
"TraeCN"
]
}
}
}
后续优化方向
- 优化 Tool 提示词,提升 Tool 正确调用率
- Tool 支持同时设置多个 key 和获取多个 key 的值
- 支持跨IDE同步配置
- 自然语言统一团队配置
Tool 较佳实践
- Tool 要有详细的描述信息,每个参数的含义、格式要求,可以在 Tool 的描述中增加 User Query 示例,比如
get_weather
Description:
Get weather is specify location
Args:
locatoin: the location you want know weather, like "Beijing"
Example user Queries:
- "what's the weather in Beijing"
Return:
weather info, like "Sunny", "Rainy"
- 要假设大模型没有看过 API 说明文档,Tool 的返回值需要是自解释的,如果是枚举,不要返回 1、2、3 这种,而应该是具体的字符串描述信息,错误信息也是如此,除了错误码也需要有具体的文字描述
- 返回值去掉大模型不需要的字段,减少无关信息干扰,也能节省成本
MCP Server存在的风险
- Tool的描述里可以实现提示词注入
- 使用第三方MCP Server时最好能Review下源代码,配置时固定版本号