基于 MCP 协议构建私有研发智能体:将内部 API 与 CI/CD 全面 Agent 化

基于 MCP 协议构建私有研发智能体:将内部 API 与 CI/CD 全面 Agent 化

基于 MCP 协议构建私有研发智能体:将内部 API 与 CI/CD 全面 Agent 化

在日常研发协同中,工程师经常在各种异构平台间疲于奔命:切到 GitLab 提 MR、登录 Jenkins 查看流水线构建失败原因、再开终端敲 kubectl logs 排查 CrashLoopBackOff。虽然大模型拥有强大的代码理解与推理能力,但传统以单一 Function Calling 为核心的二次开发方案存在协议不通用、上下文状态共享脆弱、缺乏统一鉴权与生命周期管理等严重缺陷。

随着 Anthropic 主导开源的 MCP(Model Context Protocol,模型上下文协议) 迅速成为事实标准,研发基础设施的 Agent 化迎来了架构统一的最佳契机。本文将以大厂生产落地的视角,带大家基于 Java / Spring Boot 生态,将 GitLab、Jenkins、K8s 封装为标准化 MCP 架构,并攻克高危发布场景下的 Human-in-the-loop(人机交互确认) 安全难题。


一、问题背景与业务痛点

在尝试让 LLM 接管日常研发与运维流程时,多数团队初期会采用“大模型 + 自建 OpenAPI Plugin”的简易方案,但在复杂企业环境中很快会遭遇以下痛点:

  1. 协议孤岛与上下文割裂:每个研发系统(GitLab、Jira、Jenkins、ArgoCD)的 API 返回结构迥异。LLM 单纯靠多次调用 Tool,不仅消耗大量 Token,还极易丢失上下文;同时,缺乏如“实时查看日志流”这种针对动态资源(Resource)的标准订阅通道。
  2. 缺乏统一协议,客户端难以复用:自建的 Function Calling 往往深度绑定特定的 Agent 框架。一旦开发者想在 Claude Desktop、Cursor、VS Code 或内部自研聊天机器人中接入这些运维能力,必须重写大量适配层。
  3. 高危写操作引发的“灾难性幻觉”:运维操作与代码分析不同,触发流水线、重启 Pod、打 Tag、合并主干均具有不可逆的外部副作用。如果缺乏框架级的安全拦截与二次确认机制,模型的任何一次意图漂移都可能导致生产事故。

MCP 的出现完美解耦了这一架构:它将外部能力抽象为 Resources(静态/动态上下文)、Tools(可执行函数) 以及 Prompts(提示词模版),基于 JSON-RPC 2.0 规范,让私有内部服务能够一处编写、处处挂载。


二、

Modern Agentic Architecture & RAG Retrieval Flow
▲ 权威参考图:Modern Agentic Architecture & RAG Retrieval Flow (已转存博客园图床)

核心设计与解决思路

为了实现高内聚、低耦合且安全可控的 DevOps Agent,我们基于 Spring Boot 构建统一的 Private DevOps MCP Server。

1. 核心架构设计与组件拓扑图

MCP 架构的核心是明确三层职责:Host(宿主) 负责大模型驱动与 UI 渲染,Client(客户端) 负责路由与连接,Server(服务端) 负责业务封装与资源隔离。

flowchart TD
    subgraph Agent Host & Client Layer [智能体宿主与交互层]
        Host[研发交互终端 IDE / ChatBot / Cursor]
        LLM[大语言模型 (DeepSeek / Claude / Qwen)]
        MCPClient[MCP Client (路由、协议解析、连接池)]
        Host <--> LLM
        Host <--> MCPClient
    end

    subgraph Security Layer [安全与审批层]
        HITL[Human-in-the-loop 拦截网关]
        Approver[企业微信 / 钉钉审批端]
        HITL -.->|高危操作推审| Approver
    end

    subgraph Private DevOps MCP Server [私有 DevOps MCP 服务端 (Spring Boot 3.x)]
        Dispatcher[JSON-RPC 2.0 调度器 (Stdio / SSE)]
        subgraph MCP Primitives
            Tools[MCP Tools<br/>- trigger_jenkins_build<br/>- restart_k8s_deployment]
            Resources[MCP Resources<br/>- k8s://logs/{podName}<br/>- gitlab://diff/{mrId}]
            Prompts[MCP Prompts<br/>- triage-ci-failure<br/>- review-k8s-event]
        end
        Dispatcher --> Tools
        Dispatcher --> Resources
        Dispatcher --> Prompts
    end

    subgraph Internal Infra [企业基础设施]
        GitLab[(GitLab API)]
        Jenkins[(Jenkins Master)]
        K8s[(Kubernetes Cluster)]
    end

    MCPClient <==>|JSON-RPC via SSE/HTTP| Dispatcher
    Tools --> HITL
    HITL --> Jenkins
    HITL --> K8s
    Resources --> GitLab
    Resources --> K8s

2. 端到端请求执行时序图(含 Human-in-the-loop 审批)

当研发人员发出“排查构建失败并重新发布到预发环境”的意向时,系统通过 MCP 资源拉取日志,分析后若涉及写操作,自动触发中断挂起与人工确认:

▲ 时序图 2:端到端请求处理与调用时序链路
▲ 时序图 2:端到端请求处理与调用时序链路

3. 技术方案选型对比

评估维度 传统自建 OpenAPI + Function Calling LangChain / Agent Framework 专用 Tool 基于 MCP 标准协议 (本文方案)
生态通用性 极低,仅私有系统可用 中等,强绑定 Python / TS 运行时环境 极高,天然支持 Cursor、Claude、各类 IDE 与 CLI
模型上下文管理 纯靠字符串拼接,无标准协议约束 依赖 Framework 内置 Memory,多系统难共享 协议级支持 Resources 与 Prompts,结构化下发
传输协议支持 仅标准 HTTP Restful 框架进程内部调用,跨语言成本高 双模支持:本地 Stdio 管道 / 远程 SSE (Server-Sent Events)
运维控制粒度 依赖业务代码写死安全控制 难以跨进程解耦中断审核流 标准拦截点:在 RPC 协议层原生实现鉴权与 HITL

三、完整实战代码与配置

环境说明:JDK 21、Spring Boot 3.3.4、Spring AI 1.0.0-M3(包含 MCP 官方规范适配体系)或使用标准 JSON-RPC 2.0 规范接入。此处给出基于真实企业环境的 MCP DevOps Server 核心实现代码。

1. 核心依赖配置与引导配置

<!-- pom.xml 片段 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <!-- 引入标准 JSON-RPC 支持与 Kubernetes Client -->
    <dependency>
        <groupId>io.fabric8</groupId>
        <artifactId>kubernetes-client</artifactId>
        <version>6.13.1</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>
# bootstrap.yml
server:
  port: 8088

mcp:
  server:
    name: "corp-devops-agent-server"
    version: "1.0.0"
  security:
    hitl:
      webhook-url: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-token"
      require-approval-tools:
        - "restart_k8s_deployment"
        - "trigger_jenkins_build"

gitlab:
  api-url: "https://gitlab.internal.corp/api/v4"
  token: "${GITLAB_PRIVATE_TOKEN}"

2. MCP 协议基础模型与注解声明

// McpSchema.java - 抽象核心 MCP 交互协议规范
package com.corp.mcp.protocol;

import java.lang.annotation.*;
import java.util.Map;

public class McpSchema {

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface McpTool {
        String name();
        String description();
        boolean requiresApproval() default false;
    }

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface McpResource {
        String uriTemplate();
        String mimeType() default "text/plain";
        String description();
    }

    public record ToolCallRequest(String name, Map<String, Object> arguments) {}
    public record ToolCallResult(String content, boolean isPendingApproval, String taskId) {}
    public record ResourceResponse(String uri, String mimeType, String text) {}
}

3. DevOps MCP Server 资源与工具实现

// DevOpsMcpTools.java - 将 Jenkins、K8s 注册为标准 MCP 能力
package com.corp.mcp.server;

import com.corp.mcp.protocol.McpSchema.*;
import com.corp.mcp.security.HitlApprovalService;
import io.fabric8.kubernetes.client.KubernetesClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;

@Slf4j
@Component
@RequiredArgsConstructor
public class DevOpsMcpTools {

    private final KubernetesClient k8sClient;
    private final HitlApprovalService hitlService;

    /**
     * 声明 MCP Resource:通过 URI 读取实时 Pod 日志
     * 客户端可直接读取:k8s://logs/{namespace}/{podName}
     */
    @McpResource(
        uriTemplate = "k8s://logs/{namespace}/{podName}",
        mimeType = "text/plain",
        description = "实时获取 Kubernetes 指定集群命名空间内 Pod 的最后 100 行日志"
    )
    public ResourceResponse getPodLogs(String namespace, String podName) {
        log.info("MCP Resource 读取日志: {}/{}", namespace, podName);
        String logData = k8sClient.pods()
                .inNamespace(namespace)
                .withName(podName)
                .tailingLines(100)
                .getLog();
        return new ResourceResponse("k8s://logs/" + namespace + "/" + podName, "text/plain", logData);
    }

    /**
     * 声明 MCP Tool:重启 K8s 服务(定义为敏感写操作,接入审批流程)
     */
    @McpTool(
        name = "restart_k8s_deployment",
        description = "对指定的 Kubernetes Deployment 触发滚动重启",
        requiresApproval = true
    )
    public ToolCallResult restartDeployment(Map<String, Object> params) {
        String namespace = (String) params.get("namespace");
        String deploymentName = (String) params.get("deploymentName");
        String operator = (String) params.getOrDefault("operator", "agent-system");

        // 触发 HITL 审批挂起机制
        String taskId = hitlService.submitApprovalTask("K8S_ROLLOUT_RESTART", 
                "请求重启 Deployment: " + namespace + "/" + deploymentName, operator, () -> {
                    k8sClient.apps().deployments()
                            .inNamespace(namespace)
                            .withName(deploymentName)
                            .rolling()
                            .restart();
                    log.info("Approval Passed: Restart executed for {}/{}", namespace, deploymentName);
                });

        return new ToolCallResult("操作属于高危指令,已进入人机审核队列。任务ID: " + taskId, true, taskId);
    }

    /**
     * 声明 MCP Tool:触发 Jenkins 构建
     */
    @McpTool(
        name = "trigger_jenkins_build",
        description = "触发特定工程的分支流水线构建",
        requiresApproval = false
    )
    public ToolCallResult triggerBuild(Map<String, Object> params) {
        String jobName = (String) params.get("jobName");
        String branch = (String) params.get("branch");
        log.info("执行 Jenkins 构建: job={}, branch={}", jobName, branch);

        // 此处省略 Jenkins Rest API 客户端调用
        return new ToolCallResult("Build triggered successfully on branch: " + branch, false, null);
    }
}

4. Human-in-the-loop (HITL) 审批协调器

// HitlApprovalService.java - 处理安全挂起与异步回调通知
package com.corp.mcp.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class HitlApprovalService {

    private final Map<String, PendingTask> taskRegistry = new ConcurrentHashMap<>();

    public record PendingTask(String taskId, String actionType, String detail, String operator, Runnable callback) {}

    public String submitApprovalTask(String actionType, String detail, String operator, Runnable callback) {
        String taskId = UUID.randomUUID().toString().substring(0, 8);
        taskRegistry.put(taskId, new PendingTask(taskId, actionType, detail, operator, callback));

        // 模拟企业微信 Webhook 通知
        pushNotificationToApprover(taskId, actionType, detail, operator);
        return taskId;
    }

    public boolean approveAndExecute(String taskId) {
        PendingTask task = taskRegistry.remove(taskId);
        if (task == null) {
            log.warn("审批任务不存在或已失效: {}", taskId);
            return false;
        }
        try {
            task.callback().run();
            log.info("任务 [{}] 审批通过,已回调成功执行!", taskId);
            return true;
        } catch (Exception e) {
            log.error("执行批准任务失败: {}", e.getMessage(), e);
            return false;
        }
    }

    private void pushNotificationToApprover(String taskId, String actionType, String detail, String operator) {
        log.warn(">>>> [HITL 预警] 操作需人工确认: TaskID=[{}], Action=[{}], 详情=[{}], 发起人=[{}]", 
                taskId, actionType, detail, operator);
        // 生产对接企业微信 API 发送带「批准」「拒绝」按钮的卡片
    }
}

四、避坑指南与总结验证

在生产落地与大模型压测过程中,将运维系统 Agent 化会暴露多个极具隐蔽性的工程挑战,以下是核心避坑经验:

1. 核心踩坑与解决方案

  • 避坑 1:日志资源拉取导致“上下文膨胀”与 Token 瞬时打爆
    • 问题场景:运维排查让 Agent 读取日志,K8s 默认返回上千行堆栈,直接耗尽上下文窗口甚至引发模型拒绝响应。
    • 解法:在 MCP Resource 内部做智能分块(Chunking)与特征修剪(Pruning)。在服务端预过滤掉健康检查(如 /actuator/health 200 OK 日志),并仅截取异常抛错点(Exception/Error/Fatal)向上 30 行、向下 50 行的上下文返回,严禁原样抛出原始流。
  • 避坑 2:SSE(Server-Sent Events)长连接被内网网关强行超时重置
    • 问题场景:远程 MCP 客户端使用 SSE 协议连接内部 MCP Server,Nginx 或 ALB 默认配置了 60 秒无数据自动断开,导致 Agent 长对话中途出现断连。
    • 解法:服务端必须实现规范的心跳 ping 机制(每 15 秒推送一次空注释包 :keepalive\n\n);同时配置 Nginx 禁用代理缓冲(proxy_buffering off;)与读写超时时间(proxy_read_timeout 3600s;)。
  • 避坑 3:Tool 参数注入漏洞与越权操作(Prompt Injection)
    • 问题场景:恶意 PR 或攻击者通过 Issue 标题注入恶意 Prompt,诱导 LLM 构造出如 deploymentName: "order-service; rm -rf /" 等异常参数。
    • 解法:MCP Server 层坚决不直接通过 Shell/Bash 执行拼接字符串。全面采用强类型的 SDK(如 Fabric8 Kubernetes Client、GitLab4J),参数严格走正则白名单校验(如 ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$)。

2. 成果与验证步骤

  1. 协议发现验证:启动 MCP 服务,通过客户端发送 JSON-RPC 探活请求:
    bash curl -X POST http://localhost:8088/mcp/v1/rpc \ -H "Content-Type: application/json" \ -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}'
    检查是否成功输出 trigger_jenkins_build、restart_k8s_deployment 及其 JSON Schema。
  2. 端到端发布阻断测试:
    在 Chat 终端输入:“帮我重启订单服务”。
  3. 观察控制台,MCP Server 立即截获该操作,返回状态 Pending Approval,并打印出审批 Task ID;
  4. 模拟运维人员调用审批接口后,K8s 集群才真正触发滚动重启。

总结

通过引入 MCP 协议,我们成功抹平了研发运维体系中由于异构 API 导致的割裂现状。LLM 不再是一个空洞的代码生成器,而是升级为具备私有上下文洞察力、拥有标准化执行抓手、且始终处于安全护栏限制下的高可靠研发副驾(Copilot)。这种标准化的 Agentic DevOps 架构,将是企业研发平台演进的确定性方向。

posted @ 2026-10-08 07:46  丨吴丨  阅读(15)  评论(0)    收藏  举报