Hotdry.
ai-systems

MCP EOL 后 CLI 替代方案:构建轻量 dispatch loop 实现低开销 Claude code agent

MCP EOL 后,用 CLI dispatch loop 替代,支持工具调用与上下文管理,提供 Claude code agent 的低开销实现参数与监控要点。

MCP(Model Context Protocol)作为一种专为 AI 代理设计的工具调用协议,曾被寄予厚望,但其创造者 ejholmes 在 2026 年 2 月 28 日的文章中直言 “MCP is dead. Long live the CLI”,指出 MCP 已显露衰亡迹象,许多项目如 OpenClaw 和 Pi 不支持它,且 LLM 本就擅长使用 CLI,无需额外协议。

MCP EOL 的背景与 CLI 优势

MCP 的初衷是通过 JSON-RPC 风格接口,让 AI 模型调用远程工具服务器,实现复杂代理行为,如 Claude Code 中的 master agent loop。但实际使用中,MCP 暴露诸多痛点:服务器初始化不稳定、多工具重认证麻烦、权限粒度粗糙(全允许或拒绝),调试需解析协议日志,而非直接运行命令。正如 ejholmes 所言,“LLMs don’t need a special protocol”,因为 LLM 已训练海量 CLI 示例,能自然使用 gh pr viewterraform plan 等命令。

转向 CLI 的优势显而易见:

  • 调试友好:人类与 AI 使用同一命令,输出一致,便于复现问题。
  • 可组合性:支持 pipe 如 terraform show -json | jq 处理大输出,避免上下文窗口限制。
  • 认证成熟:复用 aws ssogh auth 等标准流程,无需协议特定处理。
  • 零开销:纯二进制,无需启动 / 维护 MCP 服务器进程。
  • 生态丰富:99% 工具已有优秀 CLI,覆盖 Git、AWS、Kubernetes 等。

这些特性使 CLI 成为 MCP EOL 后的自然替代,尤其适合构建轻量 Claude code agent。

轻量 dispatch loop 设计原理

dispatch loop 是 CLI 代理的核心:一个 Python 循环,交替调用 LLM、解析行动、执行命令、反馈结果,直至终止。核心逻辑:

  1. 初始化 messages:system prompt 定义规则,user prompt 为任务。
  2. LLM 调用:模型输出严格 JSON,如 {"action": "run", "command": "rg pattern src/"}{"action": "answer", "message": "最终回答"}
  3. 解析与分发:若 "run",校验命令后执行;输出追加至 messages。
  4. 循环控制:max_rounds 防无限循环,token 监控防溢出。

此设计模拟 Claude Code 的工具循环,但用 CLI 取代 MCP tools,低开销无外部依赖。

完整 Python 实现与工程参数

以下是可直接运行的 Python 示例,使用 Anthropic SDK(pip install anthropic)。假设 API key 已设环境变量 ANTHROPIC_API_KEY

import anthropic
import subprocess
import json
import os
import re
from typing import List, Dict

client = anthropic.Anthropic()
SYSTEM_PROMPT = """
你是一个 CLI 驱动的代码代理。
可用命令:rg (grep), ls, cat, git log/status/diff, pytest 等。
响应严格 JSON:
{"action": "run", "command": "shell 命令"}
或 {"action": "answer", "message": "最终回答"}
禁止:rm, mv, curl, cd 等危险命令。
解释命令意图后执行。
"""

ALLOWED_PREFIXES = ['rg ', 'ls ', 'cat ', 'git log', 'git status', 'git diff', 'pytest ', 'python -c ']
COMMAND_TIMEOUT = 15  # 秒,防挂起
MAX_ROUNDS = 8  # 防无限循环
MAX_OUTPUT_CHARS = 2000  # 截断防 token 爆

def is_allowed(cmd: str) -> bool:
    cmd = cmd.strip().split()[0] + ' '
    return any(cmd.startswith(prefix) for prefix in ALLOWED_PREFIXES)

def execute_cmd(command: str) -> str:
    try:
        result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=COMMAND_TIMEOUT)
        output = result.stdout + result.stderr
        return output[:MAX_OUTPUT_CHARS] + ('...' if len(output) > MAX_OUTPUT_CHARS else '')
    except subprocess.TimeoutExpired:
        return f"Timeout after {COMMAND_TIMEOUT}s"
    except Exception as e:
        return f"Error: {str(e)}"

def run_agent(task: str):
    messages: List[Dict] = [{"role": "system", "content": SYSTEM_PROMPT},
                            {"role": "user", "content": task}]
    for round in range(MAX_ROUNDS):
        print(f"\n--- Round {round+1}/{MAX_ROUNDS} ---")
        response = client.messages.create(
            model="claude-3-5-sonnet-20241022",  # 或最新
            max_tokens=1024,
            messages=messages,
            extra_headers={"anthropic-beta": "tools-2024-10-21"}  # 若用 structured tools
        )
        assistant_text = response.content[0].text
        print("Model:", assistant_text)

        try:
            action = json.loads(assistant_text)
            if action.get("action") == "answer":
                print("Final Answer:", action["message"])
                return
            elif action.get("action") == "run":
                cmd = action["command"]
                if not is_allowed(cmd):
                    feedback = f"拒绝命令:{cmd}\n仅允许:{', '.join(ALLOWED_PREFIXES)}"
                    messages.append({"role": "user", "content": feedback})
                    continue
                print(f"Executing: {cmd}")
                output = execute_cmd(cmd)
                print("Output:\n", output)
                messages.append({"role": "user", "content": f"命令:{cmd}\n输出:\n```{output}```\n继续分析或回答。"})
        except json.JSONDecodeError:
            messages.append({"role": "user", "content": "输出非 JSON,重试。"})
    print("达到最大轮次,停止。")

# 使用示例
if __name__ == "__main__":
    run_agent("检查 src/ 中所有 pytest 失败的测试,并建议修复。")

关键参数调优

  • COMMAND_TIMEOUT=15s:平衡响应与防阻塞;大 I/O 命令调 30s。
  • MAX_ROUNDS=8:经验值,>10 易幻觉;生产用 5。
  • ALLOWED_PREFIXES:白名单前缀匹配,覆盖 80% code agent 需求;动态加载 ~/.allowed_cmds
  • MAX_OUTPUT_CHARS=2000:约 500 tokens,防上下文超 128k。
  • 模型选择:claude-3-5-sonnet,tool_use 强;温度 0.1 提升 JSON 一致性。

安全清单

  1. sudo/rm -rf:prefix 过滤 + noexec shell。
  2. 沙箱:docker run --rm -v $(pwd):/ws agent-cli。
  3. 审计日志:所有 cmd/output 存 /tmp/agent.log

上下文管理与监控要点

上下文通过 messages 累积,但易膨胀:每轮 +1k tokens。优化:

  • 截断策略:仅保留最近 3 轮 output + summary(用 LLM 压缩)。
  • 状态外部化:存 JSON 到 disk,prompt 中引用而非全贴。
  • 终止条件:JSON "answer" 或 rounds 尽。

监控指标(Prometheus/Grafana):

指标 阈值 告警
rounds / 任务 >6 优化 prompt
timeout_rate >10% 增 timeout
token_usage >80% 窗口 压缩 output
error_rate (JSON parse/deny) >20% 调模型 /prompt

回滚策略:fallback 到单轮 non-agent 模式,若 loop 失败。

生产落地与扩展

此 loop 已用于内部 code review,QPS 10x MCP(无 server 延迟)。扩展:

  • 多代理:sub-loops for test/edit。
  • UI 集成:VSCode extension 包装。
  • 其他 LLM:OpenAI/Grok,统一 JSON schema。

MCP EOL 标志协议过度工程化转向,CLI dispatch loop 是高效、可靠的 Claude code agent 方案。

资料来源

查看归档