feat: accept string values for all numeric input parameters (#138)

## Summary

- MCP clients may send numbers as strings. This adds `ToInt64` and `GetOptionalInt` helpers to `pkg/params` and replaces all raw `.(float64)` type assertions across operation handlers to accept both `float64` and string inputs.

## Test plan

- [x] Verify `go test ./...` passes
- [x] Test with an MCP client that sends numeric parameters as strings

*Created by Claude on behalf of @silverwind*

Reviewed-on: https://gitea.com/gitea/gitea-mcp/pulls/138
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
This commit is contained in:
silverwind
2026-02-25 23:28:14 +00:00
committed by Lunny Xiao
parent 4a2935d898
commit 67a1e1e7fe
18 changed files with 628 additions and 627 deletions

View File

@@ -7,6 +7,7 @@ import (
"gitea.com/gitea/gitea-mcp/pkg/gitea"
"gitea.com/gitea/gitea-mcp/pkg/log"
"gitea.com/gitea/gitea-mcp/pkg/params"
"gitea.com/gitea/gitea-mcp/pkg/to"
"gitea.com/gitea/gitea-mcp/pkg/tool"
@@ -109,17 +110,17 @@ func GetMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTool
if !ok {
return to.ErrorResult(errors.New("repo is required"))
}
id, ok := req.GetArguments()["id"].(float64)
if !ok {
return to.ErrorResult(errors.New("id is required"))
id, err := params.GetIndex(req.GetArguments(), "id")
if err != nil {
return to.ErrorResult(err)
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
milestone, _, err := client.GetMilestone(owner, repo, int64(id))
milestone, _, err := client.GetMilestone(owner, repo, id)
if err != nil {
return to.ErrorResult(fmt.Errorf("get %v/%v/milestone/%v err: %v", owner, repo, int64(id), err))
return to.ErrorResult(fmt.Errorf("get %v/%v/milestone/%v err: %v", owner, repo, id, err))
}
return to.TextResult(milestone)
@@ -143,14 +144,8 @@ func ListMilestonesFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallTo
if !ok {
name = ""
}
page, ok := req.GetArguments()["page"].(float64)
if !ok {
page = 1
}
pageSize, ok := req.GetArguments()["pageSize"].(float64)
if !ok {
pageSize = 100
}
page := params.GetOptionalInt(req.GetArguments(), "page", 1)
pageSize := params.GetOptionalInt(req.GetArguments(), "pageSize", 100)
opt := gitea_sdk.ListMilestoneOption{
State: gitea_sdk.StateType(state),
Name: name,
@@ -216,9 +211,9 @@ func EditMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
if !ok {
return to.ErrorResult(errors.New("repo is required"))
}
id, ok := req.GetArguments()["id"].(float64)
if !ok {
return to.ErrorResult(errors.New("id is required"))
id, err := params.GetIndex(req.GetArguments(), "id")
if err != nil {
return to.ErrorResult(err)
}
opt := gitea_sdk.EditMilestoneOption{}
@@ -240,9 +235,9 @@ func EditMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToo
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
milestone, _, err := client.EditMilestone(owner, repo, int64(id), opt)
milestone, _, err := client.EditMilestone(owner, repo, id, opt)
if err != nil {
return to.ErrorResult(fmt.Errorf("edit %v/%v/milestone/%v err: %v", owner, repo, int64(id), err))
return to.ErrorResult(fmt.Errorf("edit %v/%v/milestone/%v err: %v", owner, repo, id, err))
}
return to.TextResult(milestone)
@@ -258,17 +253,17 @@ func DeleteMilestoneFn(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallT
if !ok {
return to.ErrorResult(errors.New("repo is required"))
}
id, ok := req.GetArguments()["id"].(float64)
if !ok {
return to.ErrorResult(errors.New("id is required"))
id, err := params.GetIndex(req.GetArguments(), "id")
if err != nil {
return to.ErrorResult(err)
}
client, err := gitea.ClientFromContext(ctx)
if err != nil {
return to.ErrorResult(fmt.Errorf("get gitea client err: %v", err))
}
_, err = client.DeleteMilestone(owner, repo, int64(id))
_, err = client.DeleteMilestone(owner, repo, id)
if err != nil {
return to.ErrorResult(fmt.Errorf("delete %v/%v/milestone/%v err: %v", owner, repo, int64(id), err))
return to.ErrorResult(fmt.Errorf("delete %v/%v/milestone/%v err: %v", owner, repo, id, err))
}
return to.TextResult("Milestone deleted successfully")