Skip to main content

Command Palette

Search for a command to run...

将 Kubernetes 服务注册为 MCP Tools

Published
•2 min read•View as Markdown

前些天看到阿里云的一篇文章《Nacos 发布 MCP Registry,实现存量应用接口“0改动”升级到 MCP 协议》,深受启发,用服务注册发现结合网关设施,将存量服务转换为 MCP 工具的玩法,能非常有效地将存量服务装进 MCP 的新瓶子。那么按照我之前发表的《MCP 是一座桥》一文的思路,是不是可以更进一步,去除对特定厂商的依赖,用更通用的方式实现 MCP 的快速上车呢?下面讲讲我的尝试。

太长不看

总体流程如下图所示:

总的说来,就是利用 Kubernetes Service 的标签和注解进行标识,MCP Server 读取 Service 信息,将其作为 Tools 暴露给 MCP Client。

注册发现的实现

MCP 规范中,提供了 Tools 列表和调用的接口,在 Python SDK 里,通常对 Tools 的实现都是使用如下代码:

import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("My App")


@mcp.tool()
def calculate_bmi(weight_kg: float, height_m: float) -> float:
    """Calculate BMI given weight in kg and height in meters"""
    return weight_kg / (height_m**2)

在我们这个场景中,Tools 是动态刷新的,因此不能用这种硬编码的形式来声明,Python SDK 提供了低阶的 Server,便于我们进行更细致的能力实现。源码(lowlevel/server.py)中提供了创建实例、实现能力直到运行的简单介绍,例如:

@server.list_prompts()
async def handle_list_prompts() -> list[types.Prompt]:
   # Implementation

@server.get_prompt()
async def handle_get_prompt(
   name: str, arguments: dict[str, str] | None
) -> types.GetPromptResult:
   # Implementation

@server.list_tools()
async def handle_list_tools() -> list[types.Tool]:
   # Implementation

@server.call_tool()
async def handle_call_tool(
   name: str, arguments: dict | None
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
   # Implementation

字面意思上看,只要实现 list_tool 和 call_tool,就能动态注册工具并执行了。例如官方 README 中介绍的代码:

from mcp.server.lowlevel import NotificationOptions, Server
from mcp.server.models import InitializationOptions

# Create a server instance
server = Server("example-server")


@server.list_prompts()
async def handle_list_prompts() -> list[types.Prompt]:
...

@server.get_prompt()
async def handle_get_prompt(
    name: str, arguments: dict[str, str] | None
...

async def run():
    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            InitializationOptions(
                server_name="example",
                server_version="0.1.0",
                capabilities=server.get_capabilities(
                    notification_options=NotificationOptions(),
                    experimental_capabilities={},
                ),
            ),
        )


if __name__ == "__main__":
    import asyncio

    asyncio.run(run())

可以看到,低阶 MCP Server 实现中,需要自行处理原语的列表、读取和执行操作。如此看来在我们的场景中,只需要实现 list_tools 即可,例如使用如下的伪代码,查找被标记的 Service,并从 Annotation 中读取 MCP 工具定义,从而生成 MCP Tools:

try:
    kubeconfig_path = os.path.expanduser("/Users/dummy/.kube/config")
    namespace = "default"

    fetcher = kubernetes_tools.KubernetesToolsFetcher(kubeconfig_path)
    try:
        k8s_tools = fetcher.get_all_mcp_tools(namespace)
        for tool in k8s_tools:
            missing_fields = [field for field in ("name", "description", "inputSchema") if field not in tool]
            if missing_fields:
                logger.error(f"Kubernetes 工具定义缺失字段: {missing_fields},内容: {tool}")
                continue
            result.append(types.Tool(
                name=tool["name"],
                description=tool["description"],
                inputSchema=tool["inputSchema"]
            ))
        logger.info(f"从 Kubernetes 加载了 {len(k8s_tools)} 个工具")
    finally:
        fetcher.close()
except Exception as e:
    logger.error(f"从 Kubernetes 加载工具时出错: {e}")

根据如上代码编写的 MCP Server,可以在 Client 配置中加入如下内容启动:

{
  "mcpServers": {
    "Demo": {
      "command": "uv",
      "args": [
        "run",
        "--with",
        "mcp[cli]",
        "--with",
        "pyyaml",
        "--with",
        "kubernetes",
        "[somewhere]/main.py"
      ]
    }
  },
  "globalShortcut": ""
}

创建几个 Service,加上必要的标签和注解,例如:

apiVersion: v1
kind: Service
metadata:
  name: mcp-demo-service-1
  namespace: default
  labels:
    mcp: "true"
  annotations:
    mcptool: |
      [
        {
          "name": "echo",
          "description": "Echo back the input text",
          "inputSchema": {
            "type": "object",
            "required": ["message"],
            "properties": {
              "message": {
                "type": "string",
                "description": "The message to echo"
              }
            }
          }
        }
      ]
spec:
  selector:
    app: mcp-demo-1
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

创建 Kubernetes 服务后,启动 MCP 客户端,例如 Claude.app,可以看到 Kubernetes 服务已经出现,如下图所示:

还不能调用

前文贴的代码里,已经展示了 call_tool 的能力,下一步应该就是调用了,这方面会稍显复杂,要涉及到通信、认证、加解密、封装等问题,各种 API 网关、或者 ZTM 这样的应用层产品,应该能辅助解决这类问题。

More from this blog

AI Agent 的“莫兰迪时刻”

一个新范式真正走向大众,往往不是从所有人都正确理解它开始,而是从它开始被反复引用、简化、误读,甚至滥用开始。 今天的 AI Agent,可能正走到这样一个时刻。 这篇文章的念头,来自我最近收听《大内密谈》vol.1434《审美升级or消费主义?从莫兰迪在中国聊起》时的一些联想。节目从莫兰迪色在中国的走红,谈到作品价格、知识产权、商业流量与消费主义。[9] 我听着听着想到另一个熟悉的技术史问题:为

Oct 5, 20263 min read

FDE 是传统运维产品厂商的出路吗?

很多传统运维产品厂商,已经不缺产品了。 从 ITSM、CMDB、监控、自动化,到 IAM、数据中心管理和各类大屏,一个成熟厂商往往都能拿出一张很完整的产品地图。销售材料上看,客户运维部门的日常工作几乎都被覆盖了。 但项目一落地,味道就变了。 产品越多,集成越重;模块越全,边界越模糊;客户越大,定制越深。到了后期,项目成功本身也会变成麻烦:现场改出来的东西回不到主干,配置越来越像黑箱,谁都不敢轻易升

Jun 29, 20263 min read

绵里藏针才是 AIOps 的本质?

Agent 让运维编排变得柔性、可变、甚至自演进;但真正敢进入生产环境的 AIOps,仍然离不开坚实、受控、可审计、可回退的自动化底座。 从 Gartner 提出 AIOps 概念到现在,也大概有十年了。这么多年来,这个领域好像发生了很多变化,又好像没什么“本质”的变化。技术上,我们经历了传统机器学习、深度学习和神经网络、以及大模型和智能体这样“翻天覆地”的变化;业务上,我们面对的是更多品种、更大

May 31, 20263 min read

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read
【

【伪】架构师

346 posts