You've already forked agentic-coding-workflow
86 lines
3.3 KiB
Go
86 lines
3.3 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/google/uuid"
|
|
mcpsdk "github.com/modelcontextprotocol/go-sdk/mcp"
|
|
|
|
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
|
|
)
|
|
|
|
// OrchestratorBridge 是 mcp 调用 orchestrator 服务的窄桥接接口(orchestrator →
|
|
// mcp 的既有依赖边由 orchestrator 实现该接口,mcp 不反向 import orchestrator,避免循环)。
|
|
type OrchestratorBridge interface {
|
|
// RequestSubtask 让运行中的 agent 为其当前编排 step 派生一个子 step。
|
|
// parentACPSessionID 是调用方所在的 acp session(从 AuthSession 解析);
|
|
// 返回新建子 step 的 id。
|
|
RequestSubtask(ctx context.Context, parentACPSessionID uuid.UUID, phase string, prompt string, agentKindID *uuid.UUID) (subtaskID string, err error)
|
|
}
|
|
|
|
// registerOrchestratorTools 注册编排器相关 MCP 工具。
|
|
func registerOrchestratorTools(srv *mcpsdk.Server, deps ServerDeps) {
|
|
registerRequestSubtask(srv, deps)
|
|
}
|
|
|
|
// ===== request_subtask =====
|
|
|
|
type requestSubtaskIn struct {
|
|
Phase string `json:"phase,omitempty" jsonschema:"target phase for the subtask (defaults to the parent step phase)"`
|
|
Prompt string `json:"prompt" jsonschema:"prompt for the child agent"`
|
|
AgentKindID string `json:"agent_kind_id,omitempty" jsonschema:"optional agent kind UUID for the subtask"`
|
|
}
|
|
|
|
type requestSubtaskOut struct {
|
|
OK bool `json:"ok"`
|
|
StepID string `json:"step_id"`
|
|
}
|
|
|
|
func registerRequestSubtask(srv *mcpsdk.Server, deps ServerDeps) {
|
|
mcpsdk.AddTool(srv,
|
|
&mcpsdk.Tool{
|
|
Name: "request_subtask",
|
|
Description: "Ask the orchestrator to spawn a child step (sub-agent) for the current run, subject to depth and fan-out limits. Only callable from within an active agent session.",
|
|
},
|
|
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in requestSubtaskIn) (*mcpsdk.CallToolResult, requestSubtaskOut, error) {
|
|
if err := CheckToolFromCtx(ctx, "request_subtask"); err != nil {
|
|
return nil, requestSubtaskOut{}, err
|
|
}
|
|
if deps.Orchestrator == nil {
|
|
return nil, requestSubtaskOut{}, errs.New(errs.CodeNotFound, "orchestrator not enabled")
|
|
}
|
|
// request_subtask 是 create_acp_session 的逆向闸门:只有在 session 内的
|
|
// agent(system token)可调,普通用户/admin token 不可(它们走 StartRun)。
|
|
sess, ok := FromContext(ctx)
|
|
if !ok {
|
|
return nil, requestSubtaskOut{}, errs.New(errs.CodeInternal, "mcp: AuthSession missing")
|
|
}
|
|
if sess.Issuer != IssuerSystem {
|
|
return nil, requestSubtaskOut{}, errs.New(errs.CodeForbidden,
|
|
"request_subtask is only callable from within an agent session")
|
|
}
|
|
if sess.ACPSessionID == nil || *sess.ACPSessionID == uuid.Nil {
|
|
return nil, requestSubtaskOut{}, errs.New(errs.CodeForbidden,
|
|
"calling token is not bound to an acp session")
|
|
}
|
|
if in.Prompt == "" {
|
|
return nil, requestSubtaskOut{}, errs.New(errs.CodeInvalidInput, "prompt is required")
|
|
}
|
|
|
|
var akID *uuid.UUID
|
|
if in.AgentKindID != "" {
|
|
id, perr := uuid.Parse(in.AgentKindID)
|
|
if perr != nil {
|
|
return nil, requestSubtaskOut{}, errs.Wrap(perr, errs.CodeInvalidInput, "agent_kind_id parse")
|
|
}
|
|
akID = &id
|
|
}
|
|
|
|
stepID, err := deps.Orchestrator.RequestSubtask(ctx, *sess.ACPSessionID, in.Phase, in.Prompt, akID)
|
|
if err != nil {
|
|
return nil, requestSubtaskOut{}, err
|
|
}
|
|
return nil, requestSubtaskOut{OK: true, StepID: stepID}, nil
|
|
})
|
|
}
|