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等相关方面仍然是有缺陷,没有统一的解决方案。

相关推荐
LinXunFeng11 分钟前
AI - Gemini CLI 摆脱终端限制
openai·ai编程·gemini
程序员X小鹿1 小时前
腾讯还是太全面了,限时免费!超全CodeBuddy IDE保姆级教程!(附案例)
ai编程
yeshan6 小时前
使用 Claude Code 的自定义 Sub Agent 完善博文写作体验
ai编程·claude·掘金·日新计划
人生都在赌8 小时前
一个AI工作流如何让代码审查从手动到智能?实战拆解
ai编程·devops·cursor
北极的树8 小时前
大模型上下文工程之Prefix Caching技术详解
人工智能·ai编程
软件测试君8 小时前
【Rag实用分享】小白也能看懂的文档解析和分割教程
aigc·openai·ai编程
qiyue778 小时前
AI编程专栏(七)-什么是上下文工程,与提示工程区别
人工智能·ai编程·cursor
wayne2148 小时前
不写一行代码,也能做出 App?一文看懂「Vibe Coding」
人工智能·ai编程
茉莉花99213 小时前
如何编写一个spring ai alibaba工具
ai编程
友莘居士14 小时前
Dify中的Agent和发现和调用mcp工具两个节点调用的异同
agent·react·dify·functioncalling·mcp