前阵子一直在折腾 ai 出图的可修改性. 用 sd 生成一张图容易, 想改其中一只手, 改完之后别的地方全变样, 这就很难受. 所有"ai 用得顺手"的领域都有一个共同点: 产物是可以被精确修改的, 代码是这样, 所以代码成了大语言模型第一个攻占的地方. 尝试把 ae 的 mcp 接入 agent, 让 agent 直接操作 ae, 但效果不理想, 因为 agent "看不到"项目效果.
倒是想到很久前写着自用的schedule-pro, 觉得能让 agent 直接在上面记录东西也省去了复制-粘贴的操作.
mcp 发生在哪里
model context protocol, 目标是连接 llm 与应用, 他们之间的关系是这样的.
llm => agent => mcp client => mcp server => app.
mcp 是发生在 mcp client 和 mcp server 之间的, 只要 agent 根据协议开发 client, 应用根据协议开发 server, 任意 agent 和任意应用之间就可以互相调用.
展开这条链路是这样的:
- agent 和 llm 交互的时候有 function calling 的 json schema, 来告诉 llm 有哪些工具可以用, llm 回复需要用哪些. tool, skill 也是如此的.
- agent 内部维护 mcp 的 client, 通过 client 与 server 交互, 在调用 llm 前获取到应用有哪些能力, 在 llm 需要调用应用的时候, 通过 client 与 server 去交互拿结果.
- server 做的事除了通过协议与 client 交互, 还有根据业务去操作应用, 所以 mcp server 是应用开发者维护的.
协议交互的方式
准备在自己的应用里开发 mcp, 先要确定接入方式, 服务由 agent 启动, 还是由应用自己启动.
方案 A 是 stdio, 让 agent 按配置去拉起一个子进程. 这是最常见的做法, 配置里写个 command 和 args 就完事, 不用管端口, 服务随 agent 的生命周期起停.
方案 B 是应用内起一个 http 服务, agent 连过来, 配置里写一个 url 就可以.
我选 B, 理由有三个.
第一, 数据只有一份. 方案 A 里 agent 起的进程和正在运行的 app 是两个进程, 写数据只能写文件, 还得让 app 去监听文件变化再刷新界面, 中间多了一层不可靠. 方案 B 里写操作直接走 app 自己的数据层, 落盘和界面刷新是同一件事.
第二, 应用没开的时候, 写进去也没意义. 我的诉求是"看得见", 而界面只存在于正在运行的应用里. 让 agent 起一个后台进程, 数据写进去了, 但用户打开应用之前什么都看不到, 这个体验是断的.
第三, 多个客户端可以共用一个服务. 我可能同时用两个不同的 ai 客户端, 方案 A 会起两份进程, 方案 B 天然共享.
代价也明确: 用户要手动把服务打开, 端口可能被别的程序占用, 应用退出服务就没了. 这些我都接受, 因为它们都发生在"我正在用电脑"的场景里.
协议交互的内容
接下来要决定提供什么接口, 协议里提供的概念分这几种:
服务端提供的:
- tools: 可执行的动作, 由模型决定何时调用.
- resources: 只读的数据, 比如一篇文档、一条记录.
- prompts: 预设的提示词模板, 一般是斜杠命令的形式, 由用户主动选择.
客户端提供的:
- sampling: server 反过来请求客户端帮忙调一次模型.
- roots: 客户端告诉 server, 你能访问的目录边界在哪.
- elicitation: server 在干活途中向用户提问, 补充信息. 这是比较新的能力.
我这里选择了 tools 来进行读取, 写入操作. 代码实现也非常简单, 在 mcp server 里注册 tool, 触发的时候调用应用内部方法操作数据就可以. 这一系列给 mcp 调用的方法挂到一个 bridge 变量上方便管理, 我在 mcp 写入的时候增加了标志, 和应用 ui 配合展示"这条数据是由 mcp 写入的".
如何触发
在注册 tool 的时候填写的 description, 这个字段是让 llm 决定是否要调用这个 mcp的. 和 skill 类似, agent 会带着 mcp 的工具列表去请求 llm, llm 通过读取 description 和 instruction 来决定是否调用 mcp, 调用哪个工具. 所以能不能在合理的时候触发 mcp 取决于如何设置 description 和 instruction.
我的应用schedule-pro是一个记事软件, 所以会强调"记录"相关的关键词, 再根据"记录目标"的类型来决定记录到什么分类中.
语言和实现
electron 的应用, 主进程本来就是 node, 这一条几乎把语言选好了. mcp 官方有 typescript 的 sdk, 主进程里直接引, 不用跨语言, 不用额外运行时.
mcp ui
mcp 有 mcp apps 的扩展协议, 可以在注册 resources 的时候设置 html 的 uri, 让 支持该协议的 agent 调用 mcp 的时候可以获取 html 并展示. 页面我也用 vue 写, 打包的时候一起 build 一下就可以了.
总结
给软件开发 mcp 的技术成本低, 直接拉包就可以, 主要需要花的精力是业务上的, 即设计 mcp 的具体资源和 description. 个人觉得, mcp 对 token 的开销也不大, 只是在上下文挂了个列表, mcp 调用的时候再慢也是不消耗 token 的, 但 agent 的使用者尽可能清楚自己挂了哪些 tool/skill/mcp 还是必要的.
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
It's hard to get a map that's easy to create with sd, to change one of the hands, to change everything else. There's one thing in common in all the areas of "ai can use ": the product can be precisely modified, the code is this, so the code becomes the first place for a large-language model to attack. Try to get an ae's mcp into angent, let angent operate ae, but it's not ideal, because anent can't see the effects of the project.
It's been a long time since I've written my own Schedule-pro, and I think it's a good idea to let angent record things directly on it and not copy-paint them.
Mcp, where did it happen?
The goal is to connect llm to application, and their relationship is this.
llm => agent => mcp client => mcp server => app.
mcp is between mcp clit and mcp service, so long as ant develops clit according to the protocol, applies the protocol to develop the service, and any application can be called between any application.
Here's how it works:
- Agent and llm interact with json schema, to tell me what tools lllm can use, tool, skill.
- Agent's internal maintenance mcp clit, interact with server by channel, get the capabilities to apply before calling llm, and cross-check the results with the llm when it needs to call the application.
- What servicer does is not only interact with client by protocol, but also operate on business, so mcp servicer is maintained by application developers.
The way the protocol interacts
Prepare to develop mcp in your application, first to determine access, whether the service is started by angent, or by the application itself.
Option A is stdio, allowing antent to pull up a subprocess by configuration. This is the most common approach, writing a command and args in the configuration is done without running the port, and the service stops by the life cycle of anent.
Option B is to use an http service in application, anent to connect, write an url in configuration.
I choose B for three reasons.
First, there's only one data. The process from the program A angent and the app that is running are two processes, writing the data only for files, and allowing app to listen to file changes and refresh the interfaces, and there's an unreliability layer in the program B. The writing operation goes straight to the application of its own data layer, and the disk and interface refreshing is the same thing.
Second, it doesn't make sense to write when the application isn't open. My plea is to "see," and the interface is only in a running application. Let angent start a backstage process, and the data goes in, but the user can't see anything before opening the application, and the experience is broken.
Third, multiple clients can share a service. I might use two different ai clients at the same time, option A would start two processes, option B natural sharing.
The cost is clear: the user needs to manually open the service, the port may be occupied by another program, and the application will not be available.
Content of protocol interaction
And the next step is to decide what interfaces to provide, which are the concepts to be provided in the protocol:
Service provider:
- Tools: Executable actions, modeling when to call.
Read-only data, like a document, a record.
- Prompts: Predefined prompt templates, usually in the form of slash commands, selected by the user.
Client provides:
-Sampling: server, in turn, asks the client for help with the model.
The client told the server where the directory boundary is.
It's a relatively new ability.
I've chosen tools here for reading, writing operations. Codes are very simple to do, and to register tool in mcp servicer, to call on in-house method operating data when triggered. This series of methods for mcp calls is posted to a Bridge variable that can be easily managed, and I added signs to mcp writing, and applied ui to display "this data is written by mcp."
How?
field is for llm to decide whether to call this mcp. Like skill, angent will ask llm, llm to ask for mcp by reading description and institution to decide which to call mcp. So can you trigger mcp at reasonable times depending on how to set up the mcription and integration.
My application, Schedule-pro, is a transcription software, so it's focused on "record"-related keywords, and it's based on the type of "recording target" that determines what kind of classification to record.
Languages and achievements
Electron application, the main process is nonde, which almost selects the language. mcp officially has typescript sdk, direct lead in the main process, no cross-language, no extra running time.
mcp ui
mcp has an extension protocol for mcp apps, which can set up an html uri when registering accesses, so that html can be accessed and displayed when an agent who supports the protocol calls mcp. The page is also written with vue, and the bagging is done with the build.
Summary
The technical cost of developing mcp for software is low, so just pull the package directly, the main effort required is business, that is, the specific resources to design mcp and design. Personally, mcp doesn't cost token much, except that context has a list, mcp calls slow and non-consuming token, but angent users are as clear as possible about what tool/skill/mcp hangs.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.