AI Agent 专题 - 集成一个现成的MCP服务
公司的老总们,很快就能像管人一样管agent了。
Background
在个人代理的文章里,我们利用vs code插件链接了一个atlassian本地MCP服务,并且连接上,能够自用的使用里面的所有tools。 但是如果你希望开发一个agent 并让你的用户开始使用这个agent,这么做是非常不友好的。我不可能拉着我的用户安装一个vs code,并且在他的电脑上坑次坑次的配置MCP服务。
那么今天的这篇文章,我们就讨论一下,如果自己开发一个接入了各式各样MCP 服务的agent,让用户在无感知的情况下,觉得这个agent “智能”且有执行力。
为什么要用MCP服务?
MCP是一个软件行业非常通用的一种抽象和复用的思维,只需要服务(能力)提供商,一次定义好能力的上下文,就能够給所有的服务使用者快速接入ai的能力。如果没有MCP,每个服务的使用方,都在对不同的tools定义自己的prompt,不厌其烦的重复告诉ai一样的事情,而且由于每个人表达能力和侧重不一样,导致一样的tools 使用的真实效果也千差万别。
什么时候需要定义自己的MCP服务
- 当有多余1个的agent 需要接入你提供的tools时
- 当你想更加规范和统一的维护一类tools时
我们的项目,一开始计划只开放1-2个tools,并且调用方法的agent 目前也就只有一个。但是在真正实现tool的上下文时,我们发现tools的定义和开发也非常的没有规范科研,可复杂,可简单。于是我们决定,即使只有一个agent,我们也要用MCP来定义tools。
ai的规范化是目前阶段的一个大趋势,无论是MCP,还是AG-UI,A2A,A2C,大家都是在尝试让ai更加的规范化,减少幻觉,减少重复劳动。
开始之前
我们需要先回答几个问题,
- Is your agent read-only or read-write?
- Do you want to create audit logs for your agent events?
- Will your agent run on the client side or server side?
三个问题要一起考虑。
- 运行在client side 的优点是权限控制,和读取更多本地运行环境的能力。
- 运行在server side 的优点是,能够减少用户所需要配置的信息,如果你的客户是非软件开发人员,你就需要一个远程的MCP服务和一个已经高度集成好的agent客户端。
除了以上的区别外,还需要注意的是,在你设计的agent里所注册的tools里,要如何处理好授权和数据安全问题?
# | Agent 读写能力? | remote or local? |
---|---|---|
1 | 只读 | remote |
2 | 只读 | local |
3 | 读写 | remote |
4 | 读写 | local |
这里有四个case,在只读case下,无论是本地和远程,都不太需要关注授权问题(绝大部分场景),除非上市公司,需要对读取和写都有严格的audit log。
那么在local的mcp服务,也就是运行在客户端一侧的MCP服务,可以给每个客户都启动一个MCP链接,每个MCP链接也只有一个用户token信息的管理,它就能很轻松的知道所有操作对应的用户信息。
运行在remote的mcp服务,在设计的比较好的情况下是,服务记录好每个用户的登陆信息,再根据客户端的请求来区分用户,再用对应的用户信息来操作tools。
设计的一般的MCP remote 服务,则是用一个通用账号,比如某project 账号,来通用的操作所有的“读”case,忽略所有的audit log 需求。
设计的最糟的MCP remote 服务,则是忽略任何用户“读写”权限,只用通用账号来进行所有操作。
在今天的实操里,我们启动一个docker 镜像,用一个通用账户来操作所有的读操作。
实操吧
今天的实操中,我们会
- 利用docker 和现成的mcp 镜像,在本地启动一个mcp 服务(也可看成是一个远程MCP 服务)
- 把mcp 服务,注册到agent里
- 启动我们的agent,试用对应的tools
第一步:定义,启动我们的MCP 服务
今天我们用这个镜像: https://hub.docker.com/r/mcp/atlassian
多说一点,MCP镜像最近层出不穷,最好使用官方提供的镜像,或者是纯自己开发的镜像,否则会有一些问题,比如:
- tools 上下文描述和定义不够准确
- 安全问题,比如有人恶意注入一些不安全的上下文,乱操作你的服务
今天demo的镜像来自atlassian 官方MCP docker image,其中定义了37个tools
- confluence_add_comment
- confluence_add_label
- jira_search
- jira_get_issue
- ….
今天我们只用其中的两个jira_search 和 jira_get_issue。之后我们会讨论,为什么只用两个tools。
1
2
3
4
5
6
docker run -i --rm -p 8000:8000 \
-e JIRA_URL="https://meme.atlassian.net" \
-e JIRA_USERNAME="svc.dev.atlassian@meme.com" \
-e JIRA_API_TOKEN="your_api_token_here" \
mcp/atlassian:latest \
--transport sse -vv
from 文档:
👥 HTTP Transport Configuration Instead of using stdio, you can run the server as a persistent HTTP service using either:
- sse (Server-Sent Events) transport at /sse endpoint
- streamable-http transport at /mcp endpoint
运行好之后,可以用postman 验证一下启动情况:
第二步:定义一个agent
我们还是用ADK来,先按用ADK运行一个agent里写的框架来创建一个agent,专门用来处理jira summarize。
1
2
3
4
5
6
7
8
9
10
11
12
13
public static List<BaseTool> integrateJiraMCP() {
SseServerParameters serverParameters = SseServerParameters.builder().url("http://127.0.0.1:8000/sse")
.timeout(Duration.ofMinutes(1)).sseReadTimeout(Duration.ofMinutes(1))
.build();
McpToolset.McpToolsAndToolsetResult toolsAndToolsetResult;
try {
toolsAndToolsetResult = McpToolset.fromServer(serverParameters).get();
} catch (Exception e) {
throw new RuntimeException("Connect JIRA MCP server failed", e);
}
Set<String> supportTools = Set.of("jira_get_issue", "jira_search");
return toolsAndToolsetResult.getTools().stream().filter(mcpTool -> supportTools.contains(mcpTool.name())).map(mcpTool -> (BaseTool) mcpTool).toList();
}
注册你的MCP 服务
1
2
3
4
5
6
7
8
9
10
public static BaseAgent initAgent() {
return LlmAgent.builder()
.name(NAME)
.model("gemini-2.0-flash")
.description("Agent to summarize, organize jira tickets. There is a jira board to manage all tasks for MasterData team.")
.instruction(
"You are a helpful agent who can answer user questions about the jira tickets.")
.tools(integrateJiraMCP())
.build();
}
第三步:运行一下
1
15:15:50: Executing ':agents.multitool.MultiToolAgent.main()'…
键入你的问题:
summarize jira MD-16353
实操结束,接下来integrate everything吧。
接下来,我们就能用agent“有机”的把各种服务集成在一起了,曾经的集成是比较“死”的,没有智能可言。
拿经典的ifttt 来说,基本上就是一个trigger+ 做很多很多的事情。比如收到一封邮件,带有post twitter的关键字,就发布twitter,youtube,ins。
什么是“活”的integration呢?那就是由agent来决策所需要做的事情,比如:
- 收到prompt,将附件发布社交媒体,依据附件内容的不同情况,做不同的操作:
- 文字少的时候发twitter
- 文字多的时候生成缩略图后再发twitter
- 带有多图片的时候发ins+twitter
- 带视频链接的时候,下载下来发youtube
- 后来有一天,你想在发布之后,在notion文档里记录一下,并且发送一个slack 通知给自己。你也只需要稍加修改一下prompt,无需做任何软件层面的开发工作。
后面这一步,扩展了软件的边界,曾经的软件是专业人员的池塘,外行人听的云里雾里的。现在,任何人,只要能打字,都能来到这个池塘一起捉鱼,养鱼。
有那么☝️一个🤏小小的缺点,也有对应的解决方案。
当然MCP 如此方便,带来了一些代价,硕大的工具库冗余了非常多的tools 动辄几十个,上百个。这对token的消耗是无感且恐怖的,不知不觉预算就上去了。
而且实际情况是,我在一个任务的执行时,并不需要用到如此之多的tools,甚至许多tools 的能力会对agent 的判断形成干扰,做出没必要执行的动作。 虽然未来一定是token便宜的时代,这些巨量的tools干扰也成为了一个agent不稳定的因素。
所以呢,在这样的缺点面前,我们还有另外两个解决方案
- 出门执行任务前,先挑选你的装备: 比如我今天是去猎杀恐龙,那么我带的装备一定是和去滑雪不一样的。不过,这层人为的判断,就显得我们的Agent不是那么“智能”了!
- distributed agent(A2A): 如果你怕一次要带太多的工具出门,消耗太多的token,那么你可以先由一个总管agent 决定最终执行的细分“基层”agent,每一个“基层”agent 都是某细分领域的专家,它们只会携带对应领域的装备出门。Google的A2A协议,允许我们在agent之间进行沟通,这样,我们就能有意识的挑选组队的成员,而不是每次都所有人都一拥而上。我们又通过一个工程方法,解决了token消耗的科学问题了。
(第二点只是个人臆想,有待有识者验证)
公司的老总们,很快就能像管人一样管agent了
公司的老总们,站在A2A的思考框架下,就能像思考维服务一样,把一个一个的基层agent作为出发点,当基层agent 达到一定量(可以是数量,可以是服务的庞大性)之后,再对业务进行一定的抽象,在数量总多的基层agent之上开发一个manager agent做管理基层的事。
想不到AI的微服务来的如此之快,我估计这里面的原因跟ai“像”人是有关的,将来公司的软件部门的管理,能够很容易的把软件当作“人”来看,当作“人”来管。
个人的感受
个人的感受层面,ai确实让开发变得简单了,系统间,前后端之间,agent agent之间,有了规范和协议之后,确实很大程度上又解放了开发的实践能力。
下一篇,我们一起来实现一个定制的MCP服务器。