feat: 云端 MCP 工具 12→20(pause/goal-resume/followup/agents/workers/metrics/members/models)
This commit is contained in:
1 parent
0787496427
commit
b71cff5ee2
2 files changed
+90
No files matched your search
@@ -88,6 +88,24 @@ TOOLS = [
|
||||
{"task_id": {"type": "string"}}, ["task_id"]),
|
||||
_tool("merge_task", "合并一个已完成任务的改动(服务端状态机校验,未完成会拒绝)。",
|
||||
{"task_id": {"type": "string"}}, ["task_id"]),
|
||||
_tool("pause_task", "暂停一个进行中任务(服务端状态机校验)。",
|
||||
{"task_id": {"type": "string"}}, ["task_id"]),
|
||||
_tool("goal_resume_task", "断点续跑一个 goal 任务(服务端状态机校验)。",
|
||||
{"task_id": {"type": "string"}}, ["task_id"]),
|
||||
_tool("create_followup", "给进行中任务发后续消息;mode=queued 排队进队尾不打断本轮,direct_send 立即送达且本轮结束后优先开下一轮。",
|
||||
{"task_id": {"type": "string"},
|
||||
"body": {"type": "string"},
|
||||
"mode": {"type": "string"}}, ["task_id", "body"]),
|
||||
_tool("get_task_agents", "取单个任务的 agent 分工条目。",
|
||||
{"task_id": {"type": "string"}}, ["task_id"]),
|
||||
_tool("list_workers", "列出 worker 聚合状态(普通用户只见在线/离线数,管理员见完整清单)。",
|
||||
{}, []),
|
||||
_tool("get_metrics", "取控制面可观测性聚合:任务计数、成功率、平均耗时、成本合计(只统计调用者是成员的项目)。",
|
||||
{}, []),
|
||||
_tool("list_project_members", "列出某项目的成员。",
|
||||
{"project_id": {"type": "string"}}, ["project_id"]),
|
||||
_tool("get_models", "取模型一览(价目与默认模型,服务端口径)。",
|
||||
{}, []),
|
||||
]
|
||||
|
||||
|
||||
@@ -187,6 +205,40 @@ class MCPServer:
|
||||
return True, {"error": "task_id required"}
|
||||
return self._wrap(*self.transport(
|
||||
"POST", "/tasks/" + args["task_id"] + "/merge", {}))
|
||||
if name == "pause_task":
|
||||
if not args.get("task_id"):
|
||||
return True, {"error": "task_id required"}
|
||||
return self._wrap(*self.transport(
|
||||
"POST", "/tasks/" + args["task_id"] + "/pause", {}))
|
||||
if name == "goal_resume_task":
|
||||
if not args.get("task_id"):
|
||||
return True, {"error": "task_id required"}
|
||||
return self._wrap(*self.transport(
|
||||
"POST", "/tasks/" + args["task_id"] + "/goal-resume", {}))
|
||||
if name == "create_followup":
|
||||
if not args.get("task_id") or not args.get("body"):
|
||||
return True, {"error": "task_id and body required"}
|
||||
body = {"body": args["body"]}
|
||||
if args.get("mode") is not None:
|
||||
body["mode"] = args["mode"]
|
||||
return self._wrap(*self.transport(
|
||||
"POST", "/tasks/" + args["task_id"] + "/followups", body))
|
||||
if name == "get_task_agents":
|
||||
if not args.get("task_id"):
|
||||
return True, {"error": "task_id required"}
|
||||
return self._wrap(*self.transport(
|
||||
"GET", "/tasks/" + args["task_id"] + "/agents"))
|
||||
if name == "list_workers":
|
||||
return self._wrap(*self.transport("GET", "/workers"))
|
||||
if name == "get_metrics":
|
||||
return self._wrap(*self.transport("GET", "/metrics"))
|
||||
if name == "list_project_members":
|
||||
if not args.get("project_id"):
|
||||
return True, {"error": "project_id required"}
|
||||
return self._wrap(*self.transport(
|
||||
"GET", "/projects/" + args["project_id"] + "/members"))
|
||||
if name == "get_models":
|
||||
return self._wrap(*self.transport("GET", "/models"))
|
||||
return True, {"error": "unknown tool: %s" % name}
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -49,6 +49,22 @@ class FakeTransport:
|
||||
return 200, {"id": "t1", "status": "queued"}
|
||||
if path == "/tasks/t1/merge":
|
||||
return 200, {"id": "t1", "status": "merged"}
|
||||
if path == "/tasks/t1/pause":
|
||||
return 200, {"id": "t1", "status": "paused"}
|
||||
if path == "/tasks/t1/goal-resume":
|
||||
return 200, {"id": "t1", "status": "queued"}
|
||||
if path == "/tasks/t1/followups" and method == "POST":
|
||||
return 200, {"id": "t1", "mode": "queued"}
|
||||
if path == "/tasks/t1/agents":
|
||||
return 200, {"agents": []}
|
||||
if path == "/workers":
|
||||
return 200, {"workers_total": 2}
|
||||
if path == "/metrics":
|
||||
return 200, {"total_tasks": 3}
|
||||
if path == "/projects/p1/members":
|
||||
return 200, {"members": []}
|
||||
if path == "/models":
|
||||
return 200, {"models": []}
|
||||
if path == "/tasks/nope":
|
||||
return 404, {"error": "not found"}
|
||||
return 400, {"error": "bad request"}
|
||||
@@ -143,6 +159,28 @@ class MCPServerTestCase(unittest.TestCase):
|
||||
self.assertFalse(is_error)
|
||||
self.assertEqual(payload["status"], "merged")
|
||||
|
||||
def test_expanded_tools_translate_to_rest(self):
|
||||
cases = [
|
||||
("pause_task", {"task_id": "t1"}, ("POST", "/tasks/t1/pause")),
|
||||
("goal_resume_task", {"task_id": "t1"},
|
||||
("POST", "/tasks/t1/goal-resume")),
|
||||
("create_followup", {"task_id": "t1", "body": "hi"},
|
||||
("POST", "/tasks/t1/followups")),
|
||||
("get_task_agents", {"task_id": "t1"},
|
||||
("GET", "/tasks/t1/agents")),
|
||||
("list_workers", {}, ("GET", "/workers")),
|
||||
("get_metrics", {}, ("GET", "/metrics")),
|
||||
("list_project_members", {"project_id": "p1"},
|
||||
("GET", "/projects/p1/members")),
|
||||
("get_models", {}, ("GET", "/models")),
|
||||
]
|
||||
for name, args, expected in cases:
|
||||
is_error, payload = self.server.call_tool(name, args)
|
||||
self.assertFalse(is_error, name)
|
||||
self.assertEqual(self.transport.calls[-1][:2], expected, name)
|
||||
is_error, _ = self.server.call_tool("create_followup", {"task_id": "t1"})
|
||||
self.assertTrue(is_error)
|
||||
|
||||
def test_missing_arg_is_error_not_rest_call(self):
|
||||
before = len(self.transport.calls)
|
||||
is_error, payload = self.server.call_tool("list_tasks", {})
|
||||
|
||||
Reference in new issue
Block a user