You've already forked agentic-coding-workflow
完善 MCP 工具
This commit is contained in:
@@ -14,6 +14,10 @@ import (
|
||||
|
||||
func registerPMTools(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
registerListProjects(srv, deps)
|
||||
registerCreateProject(srv, deps)
|
||||
registerUpdateProject(srv, deps)
|
||||
registerArchiveProject(srv, deps)
|
||||
registerUnarchiveProject(srv, deps)
|
||||
registerListWorkspaces(srv, deps)
|
||||
registerListRequirements(srv, deps)
|
||||
registerListIssues(srv, deps)
|
||||
@@ -807,5 +811,176 @@ func registerListRequirementArtifacts(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
})
|
||||
}
|
||||
|
||||
// ===== create_project =====
|
||||
|
||||
type createProjectIn struct {
|
||||
Slug string `json:"slug" jsonschema:"project slug (non-empty)"`
|
||||
Name string `json:"name" jsonschema:"display name (non-empty)"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Visibility string `json:"visibility,omitempty" jsonschema:"private|internal (default private)"`
|
||||
}
|
||||
type projectDetail struct {
|
||||
Slug string `json:"slug"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Visibility string `json:"visibility"`
|
||||
Archived bool `json:"archived"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
type projectCreateOut struct {
|
||||
OK bool `json:"ok"`
|
||||
Project projectDetail `json:"project"`
|
||||
}
|
||||
|
||||
func projectDetailFrom(p *project.Project) projectDetail {
|
||||
return projectDetail{
|
||||
Slug: p.Slug, Name: p.Name, Description: p.Description,
|
||||
Visibility: string(p.Visibility), Archived: p.ArchivedAt != nil,
|
||||
CreatedAt: p.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
|
||||
}
|
||||
}
|
||||
|
||||
// checkProjectScopeUnrestricted 用于 create_project:token 若被限定到具体
|
||||
// project 列表,则不允许创建落在列表外的新项目(新项目 ID 创建前未知,
|
||||
// 只能整体拒绝)。
|
||||
func checkProjectScopeUnrestricted(ctx context.Context) error {
|
||||
sess, ok := FromContext(ctx)
|
||||
if !ok {
|
||||
return errs.New(errs.CodeInternal, "mcp: AuthSession missing")
|
||||
}
|
||||
if sess.Scope.ProjectIDs != nil {
|
||||
return errs.New(errs.CodeMcpTokenScopeViolation,
|
||||
"token is restricted to specific projects; creating new projects is not allowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerCreateProject(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "create_project", Description: "Create a new project."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in createProjectIn) (*mcpsdk.CallToolResult, projectCreateOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "create_project"); err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
if err := checkProjectScopeUnrestricted(ctx); err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Create(ctx, c, project.CreateProjectInput{
|
||||
Slug: in.Slug, Name: in.Name, Description: in.Description,
|
||||
Visibility: project.Visibility(in.Visibility),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
return nil, projectCreateOut{OK: true, Project: projectDetailFrom(p)}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== update_project =====
|
||||
|
||||
type updateProjectIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Visibility string `json:"visibility,omitempty" jsonschema:"private|internal"`
|
||||
}
|
||||
|
||||
func registerUpdateProject(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "update_project", Description: "Update a project (name/description/visibility)."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in updateProjectIn) (*mcpsdk.CallToolResult, projectCreateOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "update_project"); err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
input := project.UpdateProjectInput{}
|
||||
if in.Name != "" {
|
||||
input.Name = &in.Name
|
||||
}
|
||||
if in.Description != "" {
|
||||
input.Description = &in.Description
|
||||
}
|
||||
if in.Visibility != "" {
|
||||
v := project.Visibility(in.Visibility)
|
||||
input.Visibility = &v
|
||||
}
|
||||
p, err = deps.Projects.Update(ctx, c, in.ProjectSlug, input)
|
||||
if err != nil {
|
||||
return nil, projectCreateOut{}, err
|
||||
}
|
||||
return nil, projectCreateOut{OK: true, Project: projectDetailFrom(p)}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// ===== archive/unarchive_project =====
|
||||
|
||||
type archiveProjectIn struct {
|
||||
ProjectSlug string `json:"project_slug" jsonschema:"required field"`
|
||||
}
|
||||
|
||||
func registerArchiveProject(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "archive_project", Description: "Archive a project (idempotent)."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in archiveProjectIn) (*mcpsdk.CallToolResult, okOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "archive_project"); err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
if err := deps.Projects.Archive(ctx, c, in.ProjectSlug); err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
return nil, okOut{OK: true}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func registerUnarchiveProject(srv *mcpsdk.Server, deps ServerDeps) {
|
||||
mcpsdk.AddTool(srv,
|
||||
&mcpsdk.Tool{Name: "unarchive_project", Description: "Unarchive a project (idempotent)."},
|
||||
func(ctx context.Context, _ *mcpsdk.CallToolRequest, in archiveProjectIn) (*mcpsdk.CallToolResult, okOut, error) {
|
||||
if err := CheckToolFromCtx(ctx, "unarchive_project"); err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
c, err := deps.Caller.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
p, err := deps.Projects.Get(ctx, c, in.ProjectSlug)
|
||||
if err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
if err := CheckProjectFromCtx(ctx, p.ID); err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
if err := deps.Projects.Unarchive(ctx, c, in.ProjectSlug); err != nil {
|
||||
return nil, okOut{}, err
|
||||
}
|
||||
return nil, okOut{OK: true}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// 保证 fmt 引用(编译期检查)
|
||||
var _ = fmt.Sprintf
|
||||
|
||||
Reference in New Issue
Block a user