MCP 实战:从工具入门到企业级应用

MCP 实战:从工具入门到企业级应用

新手接触 MCP 时,常常会MCP提供的几种能力: resource(资源)、 prompt(提示词)、tools(工具)的概念绕晕。 简单来说:

  • resource 就像做饭用的食材;
  • prompt 则是对做法的规范要求;
  • tools 是做饭过程中需要用到的铲子、锅、手机(查询菜谱)

但在实际开发中,主流 IDE 或者插件(像 Cursor、Cline)对resourceprompt 功能的支持比较有限,因为它们通常只能访问本地资源,通过 IDE 自带的能力完全可以替代,所以比较鸡肋。 所以咱们今天重点聊聊 MCP 里真正能打的 "实战武器"------tools(工具模块),通过两个具体案例带你快速上手。

一、极简入门:用 Python 打造你的第一个 MCP 工具

为什么选这个案例?

就像学编程要先写 Hello World,我们先用一个超简单的 come on 工具来理解 MCP 工具的核心逻辑。它的功能很单纯:接收一个名字,返回一句加油打气的话,比如输入 "小明",就输出 "小明,加油!你一定能搞定的!"。

开发步骤

1. 准备环境

2. 编写代码

python 复制代码
from mcp.server.fastmcp import FastMCP

# 创建一个MCP服务器
mcp = FastMCP("encouragement")

# 添加一个打气工具
@mcp.tool()
def come_on(name: str) -> str:
    """接收一个名字,返回一句加油打气的话"""
    return f"{name},加油!你一定能搞定的!"

if __name__ == "__main__":
    # 启动服务器
    mcp.run() 

我们使用 FastMCP 仅仅10行左右代码就可以完成一个 MCP Server 编写。

3.客户端使用 找一个支持 MCP 的客户端比如 Cursor、Cline, 我这里以 Cursor 为例:

json 复制代码
{
    "mcpServers": {
        "come_on": {
            "command": "python",
            "args": [
                "{文件路径}/come_on.py"
            ]
        }
    }
}

注意把 {文件路径} 替换为你本地代码所在路径。 看看使用效果:

二、企业级实战分享:打通飞书协作

对于一些需要认证授权的企业平台,我们可以利用MCP和平台提供的 openapi 将数据流打通。

搜索飞书项目需求

我们可以直接利用MCP工具全局搜索飞书需求(飞书提供了openapi),减少不同工具的切换、搜索、拷贝内容的时间,代码使用 go 编写。

核心代码如下:

go 复制代码
func (t *FeishuProjectMCP) NewWorkItemListTool() mcp.Tool {
	// 创建搜索工具
	tool := mcp.NewTool(
		"search_feishu_story",
		mcp.WithDescription(
			"用于搜索飞书需求并返回结果。"+
				"如果只找到一个需求,将自动返回其详细信息;如果找到多个需求,将返回列表供选择,请和用户确认查看哪一个需求详情"+
				"注意:只有出现 '飞书'、'需求'、'story'、'feishu'、'lark' 等匹配度较高的关键字才会调用此工具",
		),
		mcp.WithString("project_key",
			mcp.Description("项目key, 选填,不填则默认可访问的所有项目"),
		),
		mcp.WithString("description",
			mcp.Required(),
			mcp.Description("需求描述关键词"),
		),
	)

	return tool
}

// handleWorkItemList 处理搜索需求工具
func (t *FeishuProjectMCP) HandleWorkItemList(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	description, ok := request.Params.Arguments["description"].(string)
	if !ok {
		return mcp.NewToolResultError("description must be a string"), nil
	}
	projectKey, _ := request.Params.Arguments["project_key"].(string)

	result, err := t.Client.SearchRequirements(ctx, t.UserKey, projectKey, description)
	if err != nil {
		slog.Error("Search error", "error", err)
		return mcp.NewToolResultError(err.Error()), nil
	}

	// 如果只找到一个需求,直接获取详情
	if len(result.Items) == 1 {
		item := result.Items[0]
		storyID, err := strconv.ParseInt(item.ID, 10, 64)
		if err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("转换需求ID失败: %v", err)), nil
		}
		detail, err := t.Client.GetRequirementDetail(ctx, item.ProjectKey, t.UserKey, storyID)
		if err != nil {
			return mcp.NewToolResultError(fmt.Sprintf("获取需求详情失败: %v", err)), nil
		}

		// 格式化详情输出
		formattedDetail := formatRequirementDetail(detail)
		response := mcp.NewToolResultText(fmt.Sprintf("找到一个匹配的需求,详情如下:\n\n%s", formattedDetail))
		return response, nil
	}

	// 如果找到多个需求,返回列表供选择
	if len(result.Items) > 1 {
		// 格式化需求列表
		formattedList := formatRequirementList(result.Items)
		return mcp.NewToolResultText(fmt.Sprintf(`找到 %d 个匹配的需求,请选择一个查看详情:

%s

要查看详情,请使用 get_feishu_story_detail 工具,并提供相应的 space_id 和 story_id。`, len(result.Items), formattedList)), nil
	}

	// 没有找到需求
	return mcp.NewToolResultText("未找到匹配的需求,请尝试使用其他关键词。"), nil
}

获取飞书文档

企业飞书文档大部分不是公开的,我们也需要使用 openapi 的方式获取。

核心代码如下:

go 复制代码
func (t *FeishuDocMCP) NewGetFeishuDocTool() mcp.Tool {
	return mcp.NewTool("get_feishu_doc",
		mcp.WithDescription("获取飞书文档内容"),
		mcp.WithString("url",
			mcp.Description("飞书文档链接"),
			mcp.Required(),
		),
	)
}

func (t *FeishuDocMCP) HandleGetFeishuDoc(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	slog.Info("handling get feishu doc")

	// 获取文档链接参数
	url, ok := request.Params.Arguments["url"].(string)
	if !ok || url == "" {
		slog.Error("invalid or missing url parameter")
		return mcp.NewToolResultText("文档链接参数无效或缺失"), nil
	}

	// 获取文档内容
	content, err := t.client.GetFeishuDocumentContent(url)
	if err != nil {
		slog.Error("failed to get feishu document content",
			"error", err,
			"url", url)
		return mcp.NewToolResultText(fmt.Sprintf("获取文档内容失败: %v", err)), nil
	}

	slog.Info("feishu document content retrieved successfully",
		"url", url)

	return mcp.NewToolResultText(content), nil
}

为什么要使用 SSE

为什么建议企业内使用 SSE 的方式维护 MCP 工具。

降低使用门栏 :无需安装 npx、docker 之类的包管理工具,去除了环境依赖。

统一管控 :所有 MCP 工具的输出都通过 SSE 服务中转,涉及到密钥等敏感信息可以统一在服务端管控。

快速迭代 :当工具功能更新时,只需要升级 SSE 服务,员工端无需任何操作,就像手机 APP 后台自动更新。

三、系统打通的另一个思路:浏览器自动化

针对每个三方系统,我们需要开发对应工具去解决认证授权问题,接入openapi,比较麻烦。

还有一个通用的方案是:通过浏览器自动化访问,因为浏览器本身存储了我们的 cookie 等认证信息,我们可以复用。 目前开源比较好用的浏览器自动化 MCP 是 playwright 你可以使用如下配置:

json 复制代码
{
    "mcpServers": {
        "playwright": {
            "isActive": true,
            "command": "npx",
            "args": [
                "@playwright/mcp@latest"
            ],
            "name": "playwright"
        }
    }
}

缺点:流程长、效率低

优点:减少开发量

四、MCP 当前的问题

如果经常浏览 MCP 的工具网站,你会发现很少有一些涉及到复杂权限的工具,比如 飞书、飞书项目这类的,因为 MCP 协议目前还不太完善,在权限、SSE等相关方面仍然是有缺陷,没有统一的解决方案。

相关推荐
一只爱撸猫的程序猿3 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
Jimmy4 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
星际码仔8 小时前
停止无脑“Vibe”!一行命令,让你的Claude变身编程导师、结对伙伴
ai编程·claude
程序员小潘9 小时前
Java开发MCP服务器
mcp
程序员老刘10 小时前
Flutter 3.35 更新要点解析
flutter·ai编程·客户端
CodeDevMaster11 小时前
从零到一:打包并发布你的第一个MCP AI工具服务
mcp
逍岚子11 小时前
以官网计算器为例:手把手教你用 TypeScript SDK 开发 MCP Server
llm·agent·mcp
mortimer12 小时前
一次与“顽固”外部程序的艰难交锋:subprocess 调用exe踩坑实录
windows·python·ai编程
摸着石头过河的石头12 小时前
手把手教你入门 MCP:模型上下文协议与 Trae IDE 中的实践
前端·mcp
逍岚子15 小时前
新闻搜索 MCP Server 开发秘籍:Python - SDK 携手 SerpApi,融入 Trae 不再难
llm·agent·mcp