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

相关推荐
江小皮不皮12 小时前
为何选择MCP?自建流程与Anthropic MCP的对比分析
人工智能·llm·nlp·aigc·sse·mcp·fastmcp
爬呀爬的水滴2 天前
工具篇-如何在Github Copilot中使用MCP服务?
copilot·mcp
带刺的坐椅2 天前
jFinal 使用 SolonMCP 开发 MCP(拥抱新潮流)
java·ai·solon·jfinal·mcp
菜鸟分享录2 天前
MCP 入门实战:用 C# 开启 AI 新篇章
ai·c#·semantic kernel·mcp
全栈技术负责人2 天前
cursor对话关键词技巧
ai编程
小白跃升坊2 天前
1Panel + MaxKB 对接高德地图 MCP Server
mcp·max kb
犬余2 天前
模型上下文协议(MCP):AI的“万能插座”
人工智能·mcp
一个处女座的程序猿3 天前
LLMs之MCP:2025年5月2日,Anthropic 宣布 Claude 重大更新:集成功能上线,研究能力大幅提升
anthropic·mcp
带刺的坐椅3 天前
FastMCP(python)和 SolonMCP(java)的体验比较(不能说一样,但真的很像)
java·python·solon·mcp·fastmcp
零炻大礼包3 天前
【MCP】服务端搭建(python和uv环境搭建、nodejs安装、pycharma安装)
开发语言·python·uv·mcp