Refactor MCP integration to use structured handlers with snake_case

This commit is contained in:
Ian Gulliver
2025-07-05 14:24:50 -07:00
parent f0c7ee76ef
commit 33d54f399c
4 changed files with 110 additions and 139 deletions

165
mcp.go
View File

@@ -2,22 +2,73 @@ package taskcp
import (
"context"
"encoding/json"
"fmt"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func RegisterMCPTools(s *server.MCPServer, service *Service) error {
s.AddTool(
type setTaskSuccessArgs struct {
ProjectID string `json:"project_id"`
TaskID string `json:"task_id"`
Result string `json:"result"`
Notes string `json:"notes,omitempty"`
}
type setTaskFailureArgs struct {
ProjectID string `json:"project_id"`
TaskID string `json:"task_id"`
Error string `json:"error"`
Notes string `json:"notes,omitempty"`
}
type taskResponse struct {
TaskID string `json:"task_id"`
Message string `json:"message"`
NextTask *Task `json:"next_task,omitempty"`
}
type errorResponse struct {
Error string `json:"error"`
}
type ServiceHandlerFunc[TArgs any, TResponse any] func(s *Service, ctx context.Context, args TArgs) (*TResponse, error)
func wrapServiceHandler[TArgs any, TResponse any](s *Service, handler ServiceHandlerFunc[TArgs, TResponse]) func(context.Context, mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
var args TArgs
if err := request.BindArguments(&args); err != nil {
errorJSON, _ := json.Marshal(errorResponse{Error: err.Error()})
return mcp.NewToolResultText(string(errorJSON)), nil
}
response, err := handler(s, ctx, args)
if err != nil {
errorJSON, _ := json.Marshal(errorResponse{Error: err.Error()})
return mcp.NewToolResultText(string(errorJSON)), nil
}
resultJSON, err := json.MarshalIndent(response, "", " ")
if err != nil {
errorJSON, _ := json.Marshal(errorResponse{Error: err.Error()})
return mcp.NewToolResultText(string(errorJSON)), nil
}
return mcp.NewToolResultText(string(resultJSON)), nil
}
}
func (s *Service) RegisterMCPTools(mcpServer *server.MCPServer) error {
mcpServer.AddTool(
mcp.NewTool(
"SetTaskSuccess",
"set_task_success",
mcp.WithDescription("Mark a task as successfully completed"),
mcp.WithString("projectId",
mcp.WithString("project_id",
mcp.Required(),
mcp.Description("The project ID"),
),
mcp.WithString("taskId",
mcp.WithString("task_id",
mcp.Required(),
mcp.Description("The task ID to mark as successful"),
),
@@ -29,18 +80,18 @@ func RegisterMCPTools(s *server.MCPServer, service *Service) error {
mcp.Description("Additional notes about the task completion"),
),
),
handleSetTaskSuccess(service),
wrapServiceHandler(s, handleSetTaskSuccess),
)
s.AddTool(
mcpServer.AddTool(
mcp.NewTool(
"SetTaskFailure",
"set_task_failure",
mcp.WithDescription("Mark a task as failed"),
mcp.WithString("projectId",
mcp.WithString("project_id",
mcp.Required(),
mcp.Description("The project ID"),
),
mcp.WithString("taskId",
mcp.WithString("task_id",
mcp.Required(),
mcp.Description("The task ID to mark as failed"),
),
@@ -52,78 +103,42 @@ func RegisterMCPTools(s *server.MCPServer, service *Service) error {
mcp.Description("Additional notes about the task failure"),
),
),
handleSetTaskFailure(service),
wrapServiceHandler(s, handleSetTaskFailure),
)
return nil
}
func handleSetTaskSuccess(service *Service) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
projectId, err := request.RequireString("projectId")
if err != nil {
return nil, fmt.Errorf("failed to get projectId: %w", err)
}
taskId, err := request.RequireString("taskId")
if err != nil {
return nil, fmt.Errorf("failed to get taskId: %w", err)
}
result, err := request.RequireString("result")
if err != nil {
return nil, fmt.Errorf("failed to get result: %w", err)
}
notes := request.GetString("notes", "")
project, err := service.GetProject(projectId)
if err != nil {
return nil, fmt.Errorf("failed to get project: %w", err)
}
nextTask := project.SetTaskSuccess(taskId, result, notes)
message := fmt.Sprintf("Task %s marked as successful", taskId)
if nextTask != nil {
message += fmt.Sprintf("\nNext task: %s (ID: %s)", nextTask.Instructions, nextTask.ID)
}
return mcp.NewToolResultText(message), nil
func handleSetTaskSuccess(s *Service, ctx context.Context, args setTaskSuccessArgs) (*taskResponse, error) {
project, err := s.GetProject(args.ProjectID)
if err != nil {
return nil, fmt.Errorf("failed to get project: %w", err)
}
nextTask := project.SetTaskSuccess(args.TaskID, args.Result, args.Notes)
response := &taskResponse{
TaskID: args.TaskID,
Message: fmt.Sprintf("Task %s marked as successful", args.TaskID),
NextTask: nextTask,
}
return response, nil
}
func handleSetTaskFailure(service *Service) func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
projectId, err := request.RequireString("projectId")
if err != nil {
return nil, fmt.Errorf("failed to get projectId: %w", err)
}
taskId, err := request.RequireString("taskId")
if err != nil {
return nil, fmt.Errorf("failed to get taskId: %w", err)
}
errorMsg, err := request.RequireString("error")
if err != nil {
return nil, fmt.Errorf("failed to get error: %w", err)
}
notes := request.GetString("notes", "")
project, err := service.GetProject(projectId)
if err != nil {
return nil, fmt.Errorf("failed to get project: %w", err)
}
nextTask := project.SetTaskFailure(taskId, errorMsg, notes)
message := fmt.Sprintf("Task %s marked as failed", taskId)
if nextTask != nil {
message += fmt.Sprintf("\nNext task: %s (ID: %s)", nextTask.Instructions, nextTask.ID)
}
return mcp.NewToolResultText(message), nil
func handleSetTaskFailure(s *Service, ctx context.Context, args setTaskFailureArgs) (*taskResponse, error) {
project, err := s.GetProject(args.ProjectID)
if err != nil {
return nil, fmt.Errorf("failed to get project: %w", err)
}
nextTask := project.SetTaskFailure(args.TaskID, args.Error, args.Notes)
response := &taskResponse{
TaskID: args.TaskID,
Message: fmt.Sprintf("Task %s marked as failed", args.TaskID),
NextTask: nextTask,
}
return response, nil
}