From 7d5574cfc90390f9e36a55a951d7c32b526f3ba8 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 10:35:08 -0700 Subject: [PATCH 01/21] add consts for methods --- internal/twdesk/companies.go | 15 +++++++++++++++ internal/twdesk/customers.go | 15 +++++++++++++++ internal/twdesk/priorities.go | 15 +++++++++++++++ internal/twdesk/sources.go | 15 +++++++++++++++ internal/twdesk/tags.go | 15 +++++++++++++++ internal/twdesk/tickets.go | 15 +++++++++++++++ internal/twdesk/types.go | 15 +++++++++++++++ internal/twdesk/users.go | 15 +++++++++++++++ 8 files changed, 120 insertions(+) create mode 100644 internal/twdesk/companies.go create mode 100644 internal/twdesk/customers.go create mode 100644 internal/twdesk/priorities.go create mode 100644 internal/twdesk/sources.go create mode 100644 internal/twdesk/tags.go create mode 100644 internal/twdesk/tickets.go create mode 100644 internal/twdesk/types.go create mode 100644 internal/twdesk/users.go diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go new file mode 100644 index 0000000..a86883e --- /dev/null +++ b/internal/twdesk/companies.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodCompanyCreate toolsets.Method = "twdesk-create_company" + MethodCompanyUpdate toolsets.Method = "twdesk-update_company" + MethodCompanyDelete toolsets.Method = "twdesk-delete_company" + MethodCompanyGet toolsets.Method = "twdesk-get_company" + MethodCompanyList toolsets.Method = "twdesk-list_companies" +) diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go new file mode 100644 index 0000000..9775a84 --- /dev/null +++ b/internal/twdesk/customers.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodCustomerCreate toolsets.Method = "twdesk-create_customer" + MethodCustomerUpdate toolsets.Method = "twdesk-update_customer" + MethodCustomerDelete toolsets.Method = "twdesk-delete_customer" + MethodCustomerGet toolsets.Method = "twdesk-get_customer" + MethodCustomerList toolsets.Method = "twdesk-list_customers" +) diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go new file mode 100644 index 0000000..e2d7f9f --- /dev/null +++ b/internal/twdesk/priorities.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodPriorityCreate toolsets.Method = "twdesk-create_priority" + MethodPriorityUpdate toolsets.Method = "twdesk-update_priority" + MethodPriorityDelete toolsets.Method = "twdesk-delete_priority" + MethodPriorityGet toolsets.Method = "twdesk-get_priority" + MethodPriorityList toolsets.Method = "twdesk-list_priorities" +) diff --git a/internal/twdesk/sources.go b/internal/twdesk/sources.go new file mode 100644 index 0000000..bebf18d --- /dev/null +++ b/internal/twdesk/sources.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodSourceCreate toolsets.Method = "twdesk-create_source" + MethodSourceUpdate toolsets.Method = "twdesk-update_source" + MethodSourceDelete toolsets.Method = "twdesk-delete_source" + MethodSourceGet toolsets.Method = "twdesk-get_source" + MethodSourceList toolsets.Method = "twdesk-list_sources" +) diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go new file mode 100644 index 0000000..aeffff8 --- /dev/null +++ b/internal/twdesk/tags.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodTagCreate toolsets.Method = "twdesk-create_tag" + MethodTagUpdate toolsets.Method = "twdesk-update_tag" + MethodTagDelete toolsets.Method = "twdesk-delete_tag" + MethodTagGet toolsets.Method = "twdesk-get_tag" + MethodTagList toolsets.Method = "twdesk-list_tags" +) diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go new file mode 100644 index 0000000..1039c3e --- /dev/null +++ b/internal/twdesk/tickets.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodTicketCreate toolsets.Method = "twdesk-create_ticket" + MethodTicketUpdate toolsets.Method = "twdesk-update_ticket" + MethodTicketDelete toolsets.Method = "twdesk-delete_ticket" + MethodTicketGet toolsets.Method = "twdesk-get_ticket" + MethodTicketList toolsets.Method = "twdesk-list_tickets" +) diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go new file mode 100644 index 0000000..e67795e --- /dev/null +++ b/internal/twdesk/types.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodTypeCreate toolsets.Method = "twdesk-create_type" + MethodTypeUpdate toolsets.Method = "twdesk-update_type" + MethodTypeDelete toolsets.Method = "twdesk-delete_type" + MethodTypeGet toolsets.Method = "twdesk-get_type" + MethodTypeList toolsets.Method = "twdesk-list_types" +) diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go new file mode 100644 index 0000000..360a9a8 --- /dev/null +++ b/internal/twdesk/users.go @@ -0,0 +1,15 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodUserCreate toolsets.Method = "twdesk-create_user" + MethodUserUpdate toolsets.Method = "twdesk-update_user" + MethodUserDelete toolsets.Method = "twdesk-delete_user" + MethodUserGet toolsets.Method = "twdesk-get_user" + MethodUserList toolsets.Method = "twdesk-list_users" +) From e3d75ef1d52685fe665e6df31b98ae416f0c25cc Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 11:06:53 -0700 Subject: [PATCH 02/21] add call to make a tag --- cmd/mcp-stdio/main.go | 1 + go.mod | 2 ++ go.sum | 5 ++++ internal/config/resources.go | 7 +++++ internal/twdesk/companies.go | 8 ++++++ internal/twdesk/customers.go | 8 ++++++ internal/twdesk/priorities.go | 8 ++++++ internal/twdesk/sources.go | 8 ++++++ internal/twdesk/tags.go | 48 ++++++++++++++++++++++++++++++++++- internal/twdesk/tickets.go | 8 ++++++ internal/twdesk/types.go | 8 ++++++ internal/twdesk/users.go | 8 ++++++ 12 files changed, 118 insertions(+), 1 deletion(-) diff --git a/cmd/mcp-stdio/main.go b/cmd/mcp-stdio/main.go index 96b9c05..910e223 100644 --- a/cmd/mcp-stdio/main.go +++ b/cmd/mcp-stdio/main.go @@ -67,6 +67,7 @@ func main() { } func newMCPServer(resources config.Resources) (*server.MCPServer, error) { + // TODO: define a group for desk group := twprojects.DefaultToolsetGroup(readOnly, false, resources.TeamworkEngine()) if err := group.EnableToolsets(methods...); err != nil { return nil, fmt.Errorf("failed to enable toolsets: %w", err) diff --git a/go.mod b/go.mod index 338ce07..bd59133 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/getsentry/sentry-go/slog v0.35.3 github.com/mark3labs/mcp-go v0.39.1 github.com/teamwork/twapi-go-sdk v1.4.0 + github.com/teamwork/desksdkgo v0.0.0-20250814193340-a4429f1f7c89 ) require ( @@ -59,6 +60,7 @@ require ( github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect github.com/shirou/gopsutil/v4 v4.25.3 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/tinylib/msgp v1.2.5 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect diff --git a/go.sum b/go.sum index 04d24e5..46257f6 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/wo github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE= github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= @@ -175,6 +177,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/teamwork/twapi-go-sdk v1.4.0 h1:lajL5uJ7IacmuSP+2pYwxjzYpR1IMBM0Afd3iccAmzc= github.com/teamwork/twapi-go-sdk v1.4.0/go.mod h1:PTxcuGSCYS5UXWbSZEbXalWnVttScOr+ia5rM3uulRM= +github.com/teamwork/desksdkgo v0.0.0-20250814193340-a4429f1f7c89 h1:umaBFmccZtErgXsappc3zV15hk7mtFkKvxFb+nTgyng= +github.com/teamwork/desksdkgo v0.0.0-20250814193340-a4429f1f7c89/go.mod h1:C+I+CxzFx2jitb7QWlR/w2YjroGQ7C/5LW4hQkba2ko= github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= @@ -287,6 +291,7 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= diff --git a/internal/config/resources.go b/internal/config/resources.go index 53a0173..7a2ca44 100644 --- a/internal/config/resources.go +++ b/internal/config/resources.go @@ -6,6 +6,7 @@ import ( "os" "strings" + desksdk "github.com/teamwork/desksdkgo/client" twapi "github.com/teamwork/twapi-go-sdk" ) @@ -18,6 +19,7 @@ var Version = "dev" type Resources struct { teamworkHTTPClient *http.Client teamworkEngine *twapi.Engine + deskClient *desksdk.Client logger *slog.Logger // Info stores environment variables mappings. @@ -116,6 +118,11 @@ func (r *Resources) TeamworkEngine() *twapi.Engine { return r.teamworkEngine } +// DeskClient returns the Teamwork Desk Client for use. +func (r *Resources) DeskClient() *desksdk.Client { + return r.deskClient +} + func getEnv(key, fallback string) string { if value, ok := os.LookupEnv(key); ok { return value diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index a86883e..3e33982 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -13,3 +13,11 @@ const ( MethodCompanyGet toolsets.Method = "twdesk-get_company" MethodCompanyList toolsets.Method = "twdesk-list_companies" ) + +func init() { + toolsets.RegisterMethod(MethodCompanyCreate) + toolsets.RegisterMethod(MethodCompanyUpdate) + toolsets.RegisterMethod(MethodCompanyDelete) + toolsets.RegisterMethod(MethodCompanyGet) + toolsets.RegisterMethod(MethodCompanyList) +} diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index 9775a84..eef1eaf 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -13,3 +13,11 @@ const ( MethodCustomerGet toolsets.Method = "twdesk-get_customer" MethodCustomerList toolsets.Method = "twdesk-list_customers" ) + +func init() { + toolsets.RegisterMethod(MethodCustomerCreate) + toolsets.RegisterMethod(MethodCustomerUpdate) + toolsets.RegisterMethod(MethodCustomerDelete) + toolsets.RegisterMethod(MethodCustomerGet) + toolsets.RegisterMethod(MethodCustomerList) +} diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go index e2d7f9f..b588689 100644 --- a/internal/twdesk/priorities.go +++ b/internal/twdesk/priorities.go @@ -13,3 +13,11 @@ const ( MethodPriorityGet toolsets.Method = "twdesk-get_priority" MethodPriorityList toolsets.Method = "twdesk-list_priorities" ) + +func init() { + toolsets.RegisterMethod(MethodPriorityCreate) + toolsets.RegisterMethod(MethodPriorityUpdate) + toolsets.RegisterMethod(MethodPriorityDelete) + toolsets.RegisterMethod(MethodPriorityGet) + toolsets.RegisterMethod(MethodPriorityList) +} diff --git a/internal/twdesk/sources.go b/internal/twdesk/sources.go index bebf18d..706fbff 100644 --- a/internal/twdesk/sources.go +++ b/internal/twdesk/sources.go @@ -13,3 +13,11 @@ const ( MethodSourceGet toolsets.Method = "twdesk-get_source" MethodSourceList toolsets.Method = "twdesk-list_sources" ) + +func init() { + toolsets.RegisterMethod(MethodSourceCreate) + toolsets.RegisterMethod(MethodSourceUpdate) + toolsets.RegisterMethod(MethodSourceDelete) + toolsets.RegisterMethod(MethodSourceGet) + toolsets.RegisterMethod(MethodSourceList) +} diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index aeffff8..65eebc3 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -1,6 +1,15 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "fmt" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -13,3 +22,40 @@ const ( MethodTagGet toolsets.Method = "twdesk-get_tag" MethodTagList toolsets.Method = "twdesk-list_tags" ) + +func init() { + toolsets.RegisterMethod(MethodTagCreate) + toolsets.RegisterMethod(MethodTagUpdate) + toolsets.RegisterMethod(MethodTagDelete) + toolsets.RegisterMethod(MethodTagGet) + toolsets.RegisterMethod(MethodTagList) +} + +// TagCreate creates a tag in Teamwork.com. +func TagCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTagCreate), + mcp.WithDescription("Create a new tag in Teamwork Desk"), + mcp.WithString("name", + mcp.Required(), + mcp.Description("The name of the tag."), + ), + mcp.WithString("color", + mcp.Description("The color of the tag."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + tag, err := client.Tags.Create(ctx, &deskmodels.TagResponse{ + Tag: deskmodels.Tag{ + Name: request.GetString("name", ""), + Color: request.GetString("color", ""), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create tag: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Tag created successfully with ID %d", tag.Tag.ID)), nil + }, + } +} diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index 1039c3e..21d7db8 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -13,3 +13,11 @@ const ( MethodTicketGet toolsets.Method = "twdesk-get_ticket" MethodTicketList toolsets.Method = "twdesk-list_tickets" ) + +func init() { + toolsets.RegisterMethod(MethodTagCreate) + toolsets.RegisterMethod(MethodTagUpdate) + toolsets.RegisterMethod(MethodTagDelete) + toolsets.RegisterMethod(MethodTagGet) + toolsets.RegisterMethod(MethodTagList) +} diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index e67795e..14a7973 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -13,3 +13,11 @@ const ( MethodTypeGet toolsets.Method = "twdesk-get_type" MethodTypeList toolsets.Method = "twdesk-list_types" ) + +func init() { + toolsets.RegisterMethod(MethodTypeCreate) + toolsets.RegisterMethod(MethodTypeUpdate) + toolsets.RegisterMethod(MethodTypeDelete) + toolsets.RegisterMethod(MethodTypeGet) + toolsets.RegisterMethod(MethodTypeList) +} diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index 360a9a8..e619841 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -13,3 +13,11 @@ const ( MethodUserGet toolsets.Method = "twdesk-get_user" MethodUserList toolsets.Method = "twdesk-list_users" ) + +func init() { + toolsets.RegisterMethod(MethodUserCreate) + toolsets.RegisterMethod(MethodUserUpdate) + toolsets.RegisterMethod(MethodUserDelete) + toolsets.RegisterMethod(MethodUserGet) + toolsets.RegisterMethod(MethodUserList) +} From f5bd3d560967753ce52eab3afe4ae595a19b11a6 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 11:23:33 -0700 Subject: [PATCH 03/21] add listing tags --- internal/twdesk/tags.go | 95 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index 65eebc3..43ef5e1 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -3,6 +3,7 @@ package twdesk import ( "context" "fmt" + "net/url" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" @@ -31,7 +32,67 @@ func init() { toolsets.RegisterMethod(MethodTagList) } -// TagCreate creates a tag in Teamwork.com. +// TagGet finds a tag in Teamwork Desk. This will find it by ID +func TagGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTagGet), + mcp.WithDescription("Get a tag from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the tag to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + tag, err := client.Tags.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get tag: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Tag retrieved successfully: %s", tag.Tag.Name)), nil + }, + } +} + +// TagList returns a list of tags that apply to the filters in Teamwork Desk +func TagList(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTagList), + mcp.WithDescription("List all tags in Teamwork Desk"), + mcp.WithString("name", mcp.Description("The name of the tag to filter by.")), + mcp.WithString("color", mcp.Description("The color of the tag to filter by.")), + mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the tag list + name := request.GetString("name", "") + color := request.GetString("color", "") + inboxIDs := request.GetIntSlice("inboxIDs", []int{}) + + filter := deskclient.NewFilter() + if name != "" { + filter = filter.Eq("name", name) + } + if color != "" { + filter = filter.Eq("color", color) + } + if len(inboxIDs) > 0 { + filter = filter.In("inboxes.id", inboxIDs) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + + tags, err := client.Tags.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list tags: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Tags retrieved successfully: %v", tags)), nil + }, + } +} + +// TagCreate creates a tag in Teamwork Desk func TagCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagCreate), @@ -59,3 +120,35 @@ func TagCreate(client *deskclient.Client) server.ServerTool { }, } } + +// TagUpdate updates a tag in Teamwork Desk +func TagUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTagUpdate), + mcp.WithDescription("Update an existing tag in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the tag to update."), + ), + mcp.WithString("name", + mcp.Description("The new name of the tag."), + ), + mcp.WithString("color", + mcp.Description("The color of the tag."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _, err := client.Tags.Update(ctx, request.GetInt("id", 0), &deskmodels.TagResponse{ + Tag: deskmodels.Tag{ + Name: request.GetString("name", ""), + Color: request.GetString("color", ""), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create tag: %w", err) + } + + return mcp.NewToolResultText("Tag updated successfully"), nil + }, + } +} From 4d785cbf106cdcdc4e310664370c6f24a73d807f Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 11:46:37 -0700 Subject: [PATCH 04/21] add endpoint for listing tickets --- internal/twdesk/tags.go | 2 - internal/twdesk/tickets.go | 198 +++++++++++++++++++++++++++++++++++-- 2 files changed, 191 insertions(+), 9 deletions(-) diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index 43ef5e1..145baf4 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -19,7 +19,6 @@ import ( const ( MethodTagCreate toolsets.Method = "twdesk-create_tag" MethodTagUpdate toolsets.Method = "twdesk-update_tag" - MethodTagDelete toolsets.Method = "twdesk-delete_tag" MethodTagGet toolsets.Method = "twdesk-get_tag" MethodTagList toolsets.Method = "twdesk-list_tags" ) @@ -27,7 +26,6 @@ const ( func init() { toolsets.RegisterMethod(MethodTagCreate) toolsets.RegisterMethod(MethodTagUpdate) - toolsets.RegisterMethod(MethodTagDelete) toolsets.RegisterMethod(MethodTagGet) toolsets.RegisterMethod(MethodTagList) } diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index 21d7db8..a34cc1e 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -1,6 +1,18 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "encoding/json" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/helpers" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -9,15 +21,187 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodTicketCreate toolsets.Method = "twdesk-create_ticket" MethodTicketUpdate toolsets.Method = "twdesk-update_ticket" - MethodTicketDelete toolsets.Method = "twdesk-delete_ticket" MethodTicketGet toolsets.Method = "twdesk-get_ticket" MethodTicketList toolsets.Method = "twdesk-list_tickets" ) func init() { - toolsets.RegisterMethod(MethodTagCreate) - toolsets.RegisterMethod(MethodTagUpdate) - toolsets.RegisterMethod(MethodTagDelete) - toolsets.RegisterMethod(MethodTagGet) - toolsets.RegisterMethod(MethodTagList) + toolsets.RegisterMethod(MethodTicketCreate) + toolsets.RegisterMethod(MethodTicketUpdate) + toolsets.RegisterMethod(MethodTicketGet) + toolsets.RegisterMethod(MethodTicketList) +} + +// TicketGet finds a ticket in Teamwork Desk. This will find it by ID +func TicketGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTicketGet), + mcp.WithDescription("Get a ticket from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the ticket to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + ticket, err := client.Tickets.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get ticket: %w", err) + } + + encoded, err := json.Marshal(ticket) + if err != nil { + return nil, err + } + + return mcp.NewToolResultText(string(helpers.WebLinker(ctx, encoded, + helpers.WebLinkerWithIDPathBuilder("/desk/tickets"), + ))), nil + }, + } +} + +// TicketList returns a list of tickets that apply to the filters in Teamwork Desk +func TicketList(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTicketList), + mcp.WithDescription("List all tickets in Teamwork Desk"), + mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + mcp.WithArray("customerIDs", mcp.Description("The IDs of the customers to filter by.")), + mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), + mcp.WithArray("tagIDs", mcp.Description("The IDs of the tags to filter by.")), + mcp.WithArray("taskIDs", mcp.Description("The IDs of the tasks to filter by.")), + mcp.WithArray("projectsIDs", mcp.Description("The IDs of the projects to filter by.")), + mcp.WithArray("statusIDs", mcp.Description("The IDs of the statuses to filter by.")), + mcp.WithArray("priorityIDs", mcp.Description("The IDs of the priorities to filter by.")), + mcp.WithArray("slaIDs", mcp.Description("The IDs of the SLAs to filter by.")), + mcp.WithArray("userIDs", mcp.Description("The IDs of the users to filter by.")), + mcp.WithBoolean("shared", mcp.Description("Find tickets shared with me outside of inboxes I have access to")), + mcp.WithBoolean("slaBreached", mcp.Description("Find tickets where the SLA has been breached")), + mcp.WithNumber("page", mcp.Description("The page number to retrieve.")), + mcp.WithNumber("pageSize", mcp.Description("The number of tickets to retrieve per page.")), + mcp.WithString("orderBy", mcp.Description("The field to order the results by.")), + mcp.WithString("orderDirection", mcp.Description("The direction to order the results by.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the ticket list + inboxIDs := request.GetIntSlice("inboxIDs", []int{}) + customerIDs := request.GetIntSlice("customerIDs", []int{}) + companyIDs := request.GetIntSlice("companyIDs", []int{}) + tagIDs := request.GetIntSlice("tagIDs", []int{}) + taskIDs := request.GetIntSlice("taskIDs", []int{}) + projectsIDs := request.GetIntSlice("projectsIDs", []int{}) + statusIDs := request.GetIntSlice("statusIDs", []int{}) + priorityIDs := request.GetIntSlice("priorityIDs", []int{}) + slaIDs := request.GetIntSlice("slaIDs", []int{}) + userIDs := request.GetIntSlice("userIDs", []int{}) + shared := request.GetBool("shared", false) + slaBreached := request.GetBool("slaBreached", false) + + filter := deskclient.NewFilter() + + if len(inboxIDs) > 0 { + filter = filter.In("inboxes.id", inboxIDs) + } + + if len(customerIDs) > 0 { + filter = filter.In("customers.id", customerIDs) + } + + if len(companyIDs) > 0 { + filter = filter.In("companies.id", companyIDs) + } + + if len(tagIDs) > 0 { + filter = filter.In("tags.id", tagIDs) + } + + if len(taskIDs) > 0 { + filter = filter.In("tasks.id", taskIDs) + } + + if len(projectsIDs) > 0 { + filter = filter.In("projects.id", projectsIDs) + } + + if len(statusIDs) > 0 { + filter = filter.In("statuses.id", statusIDs) + } + + if len(priorityIDs) > 0 { + filter = filter.In("priorities.id", priorityIDs) + } + + if len(slaIDs) > 0 { + filter = filter.In("slas.id", slaIDs) + } + + if len(userIDs) > 0 { + filter = filter.In("users.id", userIDs) + } + + if shared { + filter = filter.Eq("shared", true) + } + + if slaBreached { + filter = filter.Eq("sla_breached", true) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + params.Set("page", fmt.Sprintf("%d", request.GetInt("page", 1))) + params.Set("pageSize", fmt.Sprintf("%d", request.GetInt("pageSize", 10))) + params.Set("orderBy", request.GetString("orderBy", "createdAt")) + params.Set("orderMode", request.GetString("orderDirection", "desc")) + + tickets, err := client.Tickets.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list tickets: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Tickets retrieved successfully: %v", tickets)), nil + }, + } +} + +// TicketCreate creates a ticket in Teamwork Desk +func TicketCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTicketCreate), + mcp.WithDescription("Create a new ticket in Teamwork Desk"), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + ticket, err := client.Tickets.Create(ctx, &deskmodels.TicketResponse{ + Ticket: deskmodels.Ticket{}, + }) + if err != nil { + return nil, fmt.Errorf("failed to create ticket: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Ticket created successfully with ID %d", ticket.Ticket.ID)), nil + }, + } +} + +// TicketUpdate updates a ticket in Teamwork Desk +func TicketUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTicketUpdate), + mcp.WithDescription("Update an existing ticket in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the ticket to update."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _, err := client.Tickets.Update(ctx, request.GetInt("id", 0), &deskmodels.TicketResponse{ + Ticket: deskmodels.Ticket{}, + }) + if err != nil { + return nil, fmt.Errorf("failed to create ticket: %w", err) + } + + return mcp.NewToolResultText("Ticket updated successfully"), nil + }, + } } From 9590b4975e11986f5fa95477a97b096ac5373816 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 12:14:55 -0700 Subject: [PATCH 05/21] setup meta --- internal/twdesk/companies.go | 130 ++++++++++++++++++++++++++++++++++- internal/twdesk/messages.go | 23 +++++++ internal/twdesk/meta.go | 24 +++++++ internal/twdesk/tickets.go | 43 ++++++------ 4 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 internal/twdesk/messages.go create mode 100644 internal/twdesk/meta.go diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index 3e33982..8b53f40 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -1,6 +1,16 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -9,7 +19,6 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodCompanyCreate toolsets.Method = "twdesk-create_company" MethodCompanyUpdate toolsets.Method = "twdesk-update_company" - MethodCompanyDelete toolsets.Method = "twdesk-delete_company" MethodCompanyGet toolsets.Method = "twdesk-get_company" MethodCompanyList toolsets.Method = "twdesk-list_companies" ) @@ -17,7 +26,122 @@ const ( func init() { toolsets.RegisterMethod(MethodCompanyCreate) toolsets.RegisterMethod(MethodCompanyUpdate) - toolsets.RegisterMethod(MethodCompanyDelete) toolsets.RegisterMethod(MethodCompanyGet) toolsets.RegisterMethod(MethodCompanyList) } + +// CompanyGet finds a company in Teamwork Desk. This will find it by ID +func CompanyGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCompanyGet), + mcp.WithDescription("Get a company from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the company to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + company, err := client.Companies.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get company: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Company retrieved successfully: %s", company.Company.Name)), nil + }, + } +} + +// CompanyList returns a list of companies that apply to the filters in Teamwork Desk +func CompanyList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all companies in Teamwork Desk"), + mcp.WithString("name", mcp.Description("The name of the company to filter by.")), + mcp.WithArray("domains", mcp.Description("The domains of the company to filter by.")), + mcp.WithString("kind", mcp.Description("The kind of the company to filter by."), mcp.Pattern("^(group|company)$")), + } + + opts = append(opts, PaginationOptions()...) + + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCompanyList), opts...), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the company list + name := request.GetString("name", "") + domains := request.GetStringSlice("domains", []string{}) + kind := request.GetString("kind", "") + + filter := deskclient.NewFilter() + if name != "" { + filter = filter.Eq("name", name) + } + + if kind != "" { + filter = filter.Eq("kind", kind) + } + + if len(domains) > 0 { + filter = filter.In("domains", domains) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + SetPagination(¶ms, request) + + companies, err := client.Companies.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list companies: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Companies retrieved successfully: %v", companies)), nil + }, + } +} + +// CompanyCreate creates a company in Teamwork Desk +func CompanyCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCompanyCreate), + mcp.WithDescription("Create a new company in Teamwork Desk"), + mcp.WithString("name", + mcp.Required(), + mcp.Description("The name of the company."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + company, err := client.Companies.Create(ctx, &deskmodels.CompanyResponse{ + Company: deskmodels.Company{}, + }) + if err != nil { + return nil, fmt.Errorf("failed to create company: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Company created successfully with ID %d", company.Company.ID)), nil + }, + } +} + +// CompanyUpdate updates a company in Teamwork Desk +func CompanyUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCompanyUpdate), + mcp.WithDescription("Update an existing company in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the company to update."), + ), + mcp.WithString("name", + mcp.Description("The new name of the company."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _, err := client.Companies.Update(ctx, request.GetInt("id", 0), &deskmodels.CompanyResponse{ + Company: deskmodels.Company{}, + }) + if err != nil { + return nil, fmt.Errorf("failed to create company: %w", err) + } + + return mcp.NewToolResultText("Company updated successfully"), nil + }, + } +} diff --git a/internal/twdesk/messages.go b/internal/twdesk/messages.go new file mode 100644 index 0000000..28463f3 --- /dev/null +++ b/internal/twdesk/messages.go @@ -0,0 +1,23 @@ +package twdesk + +import "github.com/teamwork/mcp/internal/toolsets" + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodMessageCreate toolsets.Method = "twdesk-create_message" + MethodMessageUpdate toolsets.Method = "twdesk-update_message" + MethodMessageDelete toolsets.Method = "twdesk-delete_message" + MethodMessageGet toolsets.Method = "twdesk-get_message" + MethodMessageList toolsets.Method = "twdesk-list_messages" +) + +func init() { + toolsets.RegisterMethod(MethodMessageCreate) + toolsets.RegisterMethod(MethodMessageUpdate) + toolsets.RegisterMethod(MethodMessageDelete) + toolsets.RegisterMethod(MethodMessageGet) + toolsets.RegisterMethod(MethodMessageList) +} diff --git a/internal/twdesk/meta.go b/internal/twdesk/meta.go new file mode 100644 index 0000000..c052160 --- /dev/null +++ b/internal/twdesk/meta.go @@ -0,0 +1,24 @@ +package twdesk + +import ( + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" +) + +func PaginationOptions() []mcp.ToolOption { + return []mcp.ToolOption{ + mcp.WithNumber("page", mcp.Description("The page number to retrieve.")), + mcp.WithNumber("pageSize", mcp.Description("The number of tickets to retrieve per page.")), + mcp.WithString("orderBy", mcp.Description("The field to order the results by.")), + mcp.WithString("orderDirection", mcp.Description("The direction to order the results by.")), + } +} + +func SetPagination(v *url.Values, request mcp.CallToolRequest) { + v.Set("page", fmt.Sprintf("%d", request.GetInt("page", 1))) + v.Set("pageSize", fmt.Sprintf("%d", request.GetInt("pageSize", 10))) + v.Set("orderBy", request.GetString("orderBy", "createdAt")) + v.Set("orderMode", request.GetString("orderDirection", "desc")) +} diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index a34cc1e..759d2a8 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -62,26 +62,26 @@ func TicketGet(client *deskclient.Client) server.ServerTool { // TicketList returns a list of tickets that apply to the filters in Teamwork Desk func TicketList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all tickets in Teamwork Desk"), + mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + mcp.WithArray("customerIDs", mcp.Description("The IDs of the customers to filter by.")), + mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), + mcp.WithArray("tagIDs", mcp.Description("The IDs of the tags to filter by.")), + mcp.WithArray("taskIDs", mcp.Description("The IDs of the tasks to filter by.")), + mcp.WithArray("projectsIDs", mcp.Description("The IDs of the projects to filter by.")), + mcp.WithArray("statusIDs", mcp.Description("The IDs of the statuses to filter by.")), + mcp.WithArray("priorityIDs", mcp.Description("The IDs of the priorities to filter by.")), + mcp.WithArray("slaIDs", mcp.Description("The IDs of the SLAs to filter by.")), + mcp.WithArray("userIDs", mcp.Description("The IDs of the users to filter by.")), + mcp.WithBoolean("shared", mcp.Description("Find tickets shared with me outside of inboxes I have access to")), + mcp.WithBoolean("slaBreached", mcp.Description("Find tickets where the SLA has been breached")), + } + + opts = append(opts, PaginationOptions()...) + return server.ServerTool{ - Tool: mcp.NewTool(string(MethodTicketList), - mcp.WithDescription("List all tickets in Teamwork Desk"), - mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), - mcp.WithArray("customerIDs", mcp.Description("The IDs of the customers to filter by.")), - mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), - mcp.WithArray("tagIDs", mcp.Description("The IDs of the tags to filter by.")), - mcp.WithArray("taskIDs", mcp.Description("The IDs of the tasks to filter by.")), - mcp.WithArray("projectsIDs", mcp.Description("The IDs of the projects to filter by.")), - mcp.WithArray("statusIDs", mcp.Description("The IDs of the statuses to filter by.")), - mcp.WithArray("priorityIDs", mcp.Description("The IDs of the priorities to filter by.")), - mcp.WithArray("slaIDs", mcp.Description("The IDs of the SLAs to filter by.")), - mcp.WithArray("userIDs", mcp.Description("The IDs of the users to filter by.")), - mcp.WithBoolean("shared", mcp.Description("Find tickets shared with me outside of inboxes I have access to")), - mcp.WithBoolean("slaBreached", mcp.Description("Find tickets where the SLA has been breached")), - mcp.WithNumber("page", mcp.Description("The page number to retrieve.")), - mcp.WithNumber("pageSize", mcp.Description("The number of tickets to retrieve per page.")), - mcp.WithString("orderBy", mcp.Description("The field to order the results by.")), - mcp.WithString("orderDirection", mcp.Description("The direction to order the results by.")), - ), + Tool: mcp.NewTool(string(MethodTicketList), opts...), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the ticket list inboxIDs := request.GetIntSlice("inboxIDs", []int{}) @@ -149,10 +149,7 @@ func TicketList(client *deskclient.Client) server.ServerTool { params := url.Values{} params.Set("filter", filter.Build()) - params.Set("page", fmt.Sprintf("%d", request.GetInt("page", 1))) - params.Set("pageSize", fmt.Sprintf("%d", request.GetInt("pageSize", 10))) - params.Set("orderBy", request.GetString("orderBy", "createdAt")) - params.Set("orderMode", request.GetString("orderDirection", "desc")) + SetPagination(¶ms, request) tickets, err := client.Tickets.List(ctx, params) if err != nil { From e6480f45323bc7b358e3157acb342a1bb065fdd6 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 12:30:15 -0700 Subject: [PATCH 06/21] update companies --- internal/twdesk/companies.go | 45 +++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index 8b53f40..91d58e7 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -132,10 +132,53 @@ func CompanyUpdate(client *deskclient.Client) server.ServerTool { mcp.WithString("name", mcp.Description("The new name of the company."), ), + mcp.WithString("description", + mcp.Description("The new description of the company."), + ), + mcp.WithString("details", + mcp.Description("The new details of the company."), + ), + mcp.WithString("industry", + mcp.Description("The new industry of the company."), + ), + mcp.WithString("website", + mcp.Description("The new website of the company."), + ), + mcp.WithString("permission", + mcp.Description("The new permission level of the company."), + ), + mcp.WithString("kind", + mcp.Description("The new kind of the company."), + ), + mcp.WithString("note", + mcp.Description("The new note for the company."), + ), + mcp.WithArray("domains", + mcp.Description("The new domains for the company."), + ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + domains := request.GetStringSlice("domains", []string{}) + domainEntities := make([]deskmodels.Domain, len(domains)) + for i, domain := range domains { + domainEntities[i] = deskmodels.Domain{ + Name: domain, + } + } _, err := client.Companies.Update(ctx, request.GetInt("id", 0), &deskmodels.CompanyResponse{ - Company: deskmodels.Company{}, + Company: deskmodels.Company{ + Name: request.GetString("name", ""), + Description: request.GetString("description", ""), + Details: request.GetString("details", ""), + Industry: request.GetString("industry", ""), + Website: request.GetString("website", ""), + Permission: request.GetString("permission", ""), + Kind: request.GetString("kind", ""), + Note: request.GetString("note", ""), + }, + Included: deskmodels.IncludedData{ + Domains: domainEntities, + }, }) if err != nil { return nil, fmt.Errorf("failed to create company: %w", err) From ce9495fdf63bcf1b4a58d633780d16bb663c968c Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Mon, 18 Aug 2025 12:38:01 -0700 Subject: [PATCH 07/21] add enum types --- internal/twdesk/companies.go | 58 ++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index 91d58e7..b4d658b 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -57,7 +57,7 @@ func CompanyList(client *deskclient.Client) server.ServerTool { mcp.WithDescription("List all companies in Teamwork Desk"), mcp.WithString("name", mcp.Description("The name of the company to filter by.")), mcp.WithArray("domains", mcp.Description("The domains of the company to filter by.")), - mcp.WithString("kind", mcp.Description("The kind of the company to filter by."), mcp.Pattern("^(group|company)$")), + mcp.WithString("kind", mcp.Description("The kind of the company to filter by."), mcp.Enum("company", "group")), } opts = append(opts, PaginationOptions()...) @@ -102,14 +102,64 @@ func CompanyCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyCreate), mcp.WithDescription("Create a new company in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the company to update."), + ), mcp.WithString("name", mcp.Required(), - mcp.Description("The name of the company."), + mcp.Description("The new name of the company."), + ), + mcp.WithString("description", + mcp.Description("The new description of the company."), + ), + mcp.WithString("details", + mcp.Description("The new details of the company."), + ), + mcp.WithString("industry", + mcp.Description("The new industry of the company."), + ), + mcp.WithString("website", + mcp.Description("The new website of the company."), + ), + mcp.WithString("permission", + mcp.Description("The new permission level of the company."), + mcp.Enum("own", "all"), + ), + mcp.WithString("kind", + mcp.Description("The new kind of the company."), + mcp.Enum("company", "group"), + ), + mcp.WithString("note", + mcp.Description("The new note for the company."), + ), + mcp.WithArray("domains", + mcp.Description("The new domains for the company."), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + domains := request.GetStringSlice("domains", []string{}) + domainEntities := make([]deskmodels.Domain, len(domains)) + for i, domain := range domains { + domainEntities[i] = deskmodels.Domain{ + Name: domain, + } + } + company, err := client.Companies.Create(ctx, &deskmodels.CompanyResponse{ - Company: deskmodels.Company{}, + Company: deskmodels.Company{ + Name: request.GetString("name", ""), + Description: request.GetString("description", ""), + Details: request.GetString("details", ""), + Industry: request.GetString("industry", ""), + Website: request.GetString("website", ""), + Permission: request.GetString("permission", ""), + Kind: request.GetString("kind", ""), + Note: request.GetString("note", ""), + }, + Included: deskmodels.IncludedData{ + Domains: domainEntities, + }, }) if err != nil { return nil, fmt.Errorf("failed to create company: %w", err) @@ -146,9 +196,11 @@ func CompanyUpdate(client *deskclient.Client) server.ServerTool { ), mcp.WithString("permission", mcp.Description("The new permission level of the company."), + mcp.Enum("own", "all"), ), mcp.WithString("kind", mcp.Description("The new kind of the company."), + mcp.Enum("company", "group"), ), mcp.WithString("note", mcp.Description("The new note for the company."), From 7bb9da296ec69842c2f247d74c16a20a6da0715a Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Tue, 19 Aug 2025 06:44:49 -0700 Subject: [PATCH 08/21] fetch and create customers --- internal/twdesk/customers.go | 256 ++++++++++++++++++++++++++++++++++- 1 file changed, 253 insertions(+), 3 deletions(-) diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index eef1eaf..7102ade 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -1,6 +1,16 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -9,7 +19,6 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodCustomerCreate toolsets.Method = "twdesk-create_customer" MethodCustomerUpdate toolsets.Method = "twdesk-update_customer" - MethodCustomerDelete toolsets.Method = "twdesk-delete_customer" MethodCustomerGet toolsets.Method = "twdesk-get_customer" MethodCustomerList toolsets.Method = "twdesk-list_customers" ) @@ -17,7 +26,248 @@ const ( func init() { toolsets.RegisterMethod(MethodCustomerCreate) toolsets.RegisterMethod(MethodCustomerUpdate) - toolsets.RegisterMethod(MethodCustomerDelete) toolsets.RegisterMethod(MethodCustomerGet) toolsets.RegisterMethod(MethodCustomerList) } + +// CustomerGet finds a customer in Teamwork Desk. This will find it by ID +func CustomerGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCustomerGet), + mcp.WithDescription("Get a customer from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the customer to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + customer, err := client.Customers.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get customer: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Customer retrieved successfully: %s", customer.Customer.FirstName)), nil + }, + } +} + +// CustomerList returns a list of customers that apply to the filters in Teamwork Desk +func CustomerList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all customers in Teamwork Desk"), + mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), + mcp.WithArray("companyNames", mcp.Description("The names of the companies to filter by.")), + mcp.WithArray("emails", mcp.Description("The emails of the customers to filter by.")), + } + + opts = append(opts, PaginationOptions()...) + + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCustomerList), opts...), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the customer list + companyIDs := request.GetIntSlice("companyIDs", []int{}) + companyNames := request.GetStringSlice("companyNames", []string{}) + emails := request.GetStringSlice("emails", []string{}) + + filter := deskclient.NewFilter() + if len(companyIDs) > 0 { + filter = filter.In("companies.id", companyIDs) + } + + if len(companyNames) > 0 { + filter = filter.In("companies.name", companyNames) + } + + if len(emails) > 0 { + filter = filter.In("contacts.value", emails) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + SetPagination(¶ms, request) + + customers, err := client.Customers.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list customers: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Customers retrieved successfully: %v", customers)), nil + }, + } +} + +// CustomerCreate creates a customer in Teamwork Desk +func CustomerCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCustomerCreate), + mcp.WithDescription("Create a new customer in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the customer to update."), + ), + mcp.WithString("firstName", + mcp.Description("The new first name of the customer."), + ), + mcp.WithString("lastName", + mcp.Description("The new last name of the customer."), + ), + mcp.WithString("email", + mcp.Description("The new email of the customer."), + ), + mcp.WithString("organization", + mcp.Description("The new organization of the customer."), + ), + mcp.WithString("extraData", + mcp.Description("The new extra data of the customer."), + ), + mcp.WithString("notes", + mcp.Description("The new notes of the customer."), + ), + mcp.WithString("linkedinURL", + mcp.Description("The new LinkedIn URL of the customer."), + ), + mcp.WithString("facebookURL", + mcp.Description("The new Facebook URL of the customer."), + ), + mcp.WithString("twitterHandle", + mcp.Description("The new Twitter handle of the customer."), + ), + mcp.WithString("jobTitle", + mcp.Description("The new job title of the customer."), + ), + mcp.WithString("phone", + mcp.Description("The new phone number of the customer."), + ), + mcp.WithString("mobile", + mcp.Description("The new mobile number of the customer."), + ), + mcp.WithString("address", + mcp.Description("The new address of the customer."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + domains := request.GetStringSlice("domains", []string{}) + domainEntities := make([]deskmodels.Domain, len(domains)) + for i, domain := range domains { + domainEntities[i] = deskmodels.Domain{ + Name: domain, + } + } + + customer, err := client.Customers.Create(ctx, &deskmodels.CustomerResponse{ + Customer: deskmodels.Customer{ + FirstName: request.GetString("firstName", ""), + LastName: request.GetString("lastName", ""), + Email: request.GetString("email", ""), + Organization: request.GetString("organization", ""), + ExtraData: request.GetString("extraData", ""), + Notes: request.GetString("notes", ""), + LinkedinURL: request.GetString("linkedinURL", ""), + FacebookURL: request.GetString("facebookURL", ""), + TwitterHandle: request.GetString("twitterHandle", ""), + JobTitle: request.GetString("jobTitle", ""), + Phone: request.GetString("phone", ""), + Mobile: request.GetString("mobile", ""), + Address: request.GetString("address", ""), + Trusted: request.GetBool("trusted", false), + }, + Included: deskmodels.IncludedData{ + Domains: domainEntities, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create customer: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Customer created successfully with ID %d", customer.Customer.ID)), nil + }, + } +} + +// CustomerUpdate updates a customer in Teamwork Desk +func CustomerUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodCustomerUpdate), + mcp.WithDescription("Update an existing customer in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the customer to update."), + ), + mcp.WithString("firstName", + mcp.Description("The new first name of the customer."), + ), + mcp.WithString("lastName", + mcp.Description("The new last name of the customer."), + ), + mcp.WithString("email", + mcp.Description("The new email of the customer."), + ), + mcp.WithString("organization", + mcp.Description("The new organization of the customer."), + ), + mcp.WithString("extraData", + mcp.Description("The new extra data of the customer."), + ), + mcp.WithString("notes", + mcp.Description("The new notes of the customer."), + ), + mcp.WithString("linkedinURL", + mcp.Description("The new LinkedIn URL of the customer."), + ), + mcp.WithString("facebookURL", + mcp.Description("The new Facebook URL of the customer."), + ), + mcp.WithString("twitterHandle", + mcp.Description("The new Twitter handle of the customer."), + ), + mcp.WithString("jobTitle", + mcp.Description("The new job title of the customer."), + ), + mcp.WithString("phone", + mcp.Description("The new phone number of the customer."), + ), + mcp.WithString("mobile", + mcp.Description("The new mobile number of the customer."), + ), + mcp.WithString("address", + mcp.Description("The new address of the customer."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + domains := request.GetStringSlice("domains", []string{}) + domainEntities := make([]deskmodels.Domain, len(domains)) + for i, domain := range domains { + domainEntities[i] = deskmodels.Domain{ + Name: domain, + } + } + _, err := client.Customers.Update(ctx, request.GetInt("id", 0), &deskmodels.CustomerResponse{ + Customer: deskmodels.Customer{ + FirstName: request.GetString("firstName", ""), + LastName: request.GetString("lastName", ""), + Email: request.GetString("email", ""), + Organization: request.GetString("organization", ""), + ExtraData: request.GetString("extraData", ""), + Notes: request.GetString("notes", ""), + LinkedinURL: request.GetString("linkedinURL", ""), + FacebookURL: request.GetString("facebookURL", ""), + TwitterHandle: request.GetString("twitterHandle", ""), + JobTitle: request.GetString("jobTitle", ""), + Phone: request.GetString("phone", ""), + Mobile: request.GetString("mobile", ""), + Address: request.GetString("address", ""), + Trusted: request.GetBool("trusted", false), + }, + Included: deskmodels.IncludedData{ + Domains: domainEntities, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create customer: %w", err) + } + + return mcp.NewToolResultText("Customer updated successfully"), nil + }, + } +} From d7d0ae36c3a82c5b2afc71ec3b9926025ddc4e3c Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Tue, 19 Aug 2025 07:10:28 -0700 Subject: [PATCH 09/21] add resources for statuses --- internal/twdesk/customers.go | 28 +++--- internal/twdesk/messages.go | 2 - internal/twdesk/priorities.go | 130 +++++++++++++++++++++++++++- internal/twdesk/sources.go | 23 ----- internal/twdesk/statuses.go | 158 ++++++++++++++++++++++++++++++++++ internal/twdesk/types.go | 2 - internal/twdesk/users.go | 2 - 7 files changed, 299 insertions(+), 46 deletions(-) delete mode 100644 internal/twdesk/sources.go create mode 100644 internal/twdesk/statuses.go diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index 7102ade..6afe301 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -101,49 +101,49 @@ func CustomerList(client *deskclient.Client) server.ServerTool { func CustomerCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerCreate), - mcp.WithDescription("Create a new customer in Teamwork Desk"), + mcp.WithDescription("Create a customer in Teamwork Desk"), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to update."), ), mcp.WithString("firstName", - mcp.Description("The new first name of the customer."), + mcp.Description("The first name of the customer."), ), mcp.WithString("lastName", - mcp.Description("The new last name of the customer."), + mcp.Description("The last name of the customer."), ), mcp.WithString("email", - mcp.Description("The new email of the customer."), + mcp.Description("The email of the customer."), ), mcp.WithString("organization", - mcp.Description("The new organization of the customer."), + mcp.Description("The organization of the customer."), ), mcp.WithString("extraData", - mcp.Description("The new extra data of the customer."), + mcp.Description("The extra data of the customer."), ), mcp.WithString("notes", - mcp.Description("The new notes of the customer."), + mcp.Description("The notes of the customer."), ), mcp.WithString("linkedinURL", - mcp.Description("The new LinkedIn URL of the customer."), + mcp.Description("The LinkedIn URL of the customer."), ), mcp.WithString("facebookURL", - mcp.Description("The new Facebook URL of the customer."), + mcp.Description("The Facebook URL of the customer."), ), mcp.WithString("twitterHandle", - mcp.Description("The new Twitter handle of the customer."), + mcp.Description("The Twitter handle of the customer."), ), mcp.WithString("jobTitle", - mcp.Description("The new job title of the customer."), + mcp.Description("The job title of the customer."), ), mcp.WithString("phone", - mcp.Description("The new phone number of the customer."), + mcp.Description("The phone number of the customer."), ), mcp.WithString("mobile", - mcp.Description("The new mobile number of the customer."), + mcp.Description("The mobile number of the customer."), ), mcp.WithString("address", - mcp.Description("The new address of the customer."), + mcp.Description("The address of the customer."), ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { diff --git a/internal/twdesk/messages.go b/internal/twdesk/messages.go index 28463f3..594b58c 100644 --- a/internal/twdesk/messages.go +++ b/internal/twdesk/messages.go @@ -9,7 +9,6 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodMessageCreate toolsets.Method = "twdesk-create_message" MethodMessageUpdate toolsets.Method = "twdesk-update_message" - MethodMessageDelete toolsets.Method = "twdesk-delete_message" MethodMessageGet toolsets.Method = "twdesk-get_message" MethodMessageList toolsets.Method = "twdesk-list_messages" ) @@ -17,7 +16,6 @@ const ( func init() { toolsets.RegisterMethod(MethodMessageCreate) toolsets.RegisterMethod(MethodMessageUpdate) - toolsets.RegisterMethod(MethodMessageDelete) toolsets.RegisterMethod(MethodMessageGet) toolsets.RegisterMethod(MethodMessageList) } diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go index b588689..7a4b5e4 100644 --- a/internal/twdesk/priorities.go +++ b/internal/twdesk/priorities.go @@ -1,6 +1,16 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -9,7 +19,6 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodPriorityCreate toolsets.Method = "twdesk-create_priority" MethodPriorityUpdate toolsets.Method = "twdesk-update_priority" - MethodPriorityDelete toolsets.Method = "twdesk-delete_priority" MethodPriorityGet toolsets.Method = "twdesk-get_priority" MethodPriorityList toolsets.Method = "twdesk-list_priorities" ) @@ -17,7 +26,122 @@ const ( func init() { toolsets.RegisterMethod(MethodPriorityCreate) toolsets.RegisterMethod(MethodPriorityUpdate) - toolsets.RegisterMethod(MethodPriorityDelete) toolsets.RegisterMethod(MethodPriorityGet) toolsets.RegisterMethod(MethodPriorityList) } + +// PriorityGet finds a priority in Teamwork Desk. This will find it by ID +func PriorityGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodPriorityGet), + mcp.WithDescription("Get a priority from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the priority to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + priority, err := client.TicketPriorities.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get priority: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Priority retrieved successfully: %s", priority.TicketPriority.Name)), nil + }, + } +} + +// PriorityList returns a list of priorities that apply to the filters in Teamwork Desk +func PriorityList(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodPriorityList), + mcp.WithDescription("List all priorities in Teamwork Desk"), + mcp.WithArray("name", mcp.Description("The name of the priority to filter by.")), + mcp.WithArray("color", mcp.Description("The color of the priority to filter by.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the priority list + name := request.GetStringSlice("name", []string{}) + color := request.GetStringSlice("color", []string{}) + + filter := deskclient.NewFilter() + if len(name) > 0 { + filter = filter.In("name", name) + } + if len(color) > 0 { + filter = filter.In("color", color) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + + priorities, err := client.TicketPriorities.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list priorities: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Priorities retrieved successfully: %v", priorities)), nil + }, + } +} + +// PriorityCreate creates a priority in Teamwork Desk +func PriorityCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodPriorityCreate), + mcp.WithDescription("Create a new priority in Teamwork Desk"), + mcp.WithString("name", + mcp.Required(), + mcp.Description("The name of the priority."), + ), + mcp.WithString("color", + mcp.Description("The color of the priority."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + priority, err := client.TicketPriorities.Create(ctx, &deskmodels.TicketPriorityResponse{ + TicketPriority: deskmodels.TicketPriority{ + Name: request.GetString("name", ""), + Color: request.GetString("color", ""), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create priority: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Priority created successfully with ID %d", priority.TicketPriority.ID)), nil + }, + } +} + +// PriorityUpdate updates a priority in Teamwork Desk +func PriorityUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodPriorityUpdate), + mcp.WithDescription("Update an existing priority in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the priority to update."), + ), + mcp.WithString("name", + mcp.Description("The new name of the priority."), + ), + mcp.WithString("color", + mcp.Description("The color of the priority."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _, err := client.TicketPriorities.Update(ctx, request.GetInt("id", 0), &deskmodels.TicketPriorityResponse{ + TicketPriority: deskmodels.TicketPriority{ + Name: request.GetString("name", ""), + Color: request.GetString("color", ""), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create priority: %w", err) + } + + return mcp.NewToolResultText("Priority updated successfully"), nil + }, + } +} diff --git a/internal/twdesk/sources.go b/internal/twdesk/sources.go deleted file mode 100644 index 706fbff..0000000 --- a/internal/twdesk/sources.go +++ /dev/null @@ -1,23 +0,0 @@ -package twdesk - -import "github.com/teamwork/mcp/internal/toolsets" - -// List of methods available in the Teamwork.com MCP service. -// -// The naming convention for methods follows a pattern described here: -// https://github.com/github/github-mcp-server/issues/333 -const ( - MethodSourceCreate toolsets.Method = "twdesk-create_source" - MethodSourceUpdate toolsets.Method = "twdesk-update_source" - MethodSourceDelete toolsets.Method = "twdesk-delete_source" - MethodSourceGet toolsets.Method = "twdesk-get_source" - MethodSourceList toolsets.Method = "twdesk-list_sources" -) - -func init() { - toolsets.RegisterMethod(MethodSourceCreate) - toolsets.RegisterMethod(MethodSourceUpdate) - toolsets.RegisterMethod(MethodSourceDelete) - toolsets.RegisterMethod(MethodSourceGet) - toolsets.RegisterMethod(MethodSourceList) -} diff --git a/internal/twdesk/statuses.go b/internal/twdesk/statuses.go new file mode 100644 index 0000000..5e70d67 --- /dev/null +++ b/internal/twdesk/statuses.go @@ -0,0 +1,158 @@ +package twdesk + +import ( + "context" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/toolsets" +) + +// List of methods available in the Teamwork.com MCP service. +// +// The naming convention for methods follows a pattern described here: +// https://github.com/github/github-mcp-server/issues/333 +const ( + MethodStatusCreate toolsets.Method = "twdesk-create_status" + MethodStatusUpdate toolsets.Method = "twdesk-update_status" + MethodStatusGet toolsets.Method = "twdesk-get_status" + MethodStatusList toolsets.Method = "twdesk-list_statuses" +) + +func init() { + toolsets.RegisterMethod(MethodStatusCreate) + toolsets.RegisterMethod(MethodStatusUpdate) + toolsets.RegisterMethod(MethodStatusGet) + toolsets.RegisterMethod(MethodStatusList) +} + +// StatusGet finds a status in Teamwork Desk. This will find it by ID +func StatusGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodStatusGet), + mcp.WithDescription("Get a status from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the status to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + status, err := client.TicketStatuses.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get status: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Status retrieved successfully: %s", status.TicketStatus.Name)), nil + }, + } +} + +// StatusList returns a list of statuses that apply to the filters in Teamwork Desk +func StatusList(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodStatusList), + mcp.WithDescription("List all statuses in Teamwork Desk"), + mcp.WithArray("name", mcp.Description("The name of the status to filter by.")), + mcp.WithArray("color", mcp.Description("The color of the status to filter by.")), + mcp.WithArray("code", mcp.Description("The code of the status to filter by.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the status list + name := request.GetStringSlice("name", []string{}) + color := request.GetStringSlice("color", []string{}) + code := request.GetStringSlice("code", []string{}) + + filter := deskclient.NewFilter() + if len(name) > 0 { + filter = filter.In("name", name) + } + if len(color) > 0 { + filter = filter.In("color", color) + } + if len(code) > 0 { + filter = filter.In("code", code) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + + statuses, err := client.TicketStatuses.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list statuses: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Statuses retrieved successfully: %v", statuses)), nil + }, + } +} + +// StatusCreate creates a status in Teamwork Desk +func StatusCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodStatusCreate), + mcp.WithDescription("Create a new status in Teamwork Desk"), + mcp.WithString("name", + mcp.Required(), + mcp.Description("The name of the status."), + ), + mcp.WithString("color", + mcp.Description("The color of the status."), + ), + mcp.WithNumber("displayOrder", mcp.Description("The display order of the status.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + status, err := client.TicketStatuses.Create(ctx, &deskmodels.TicketStatusResponse{ + TicketStatus: deskmodels.TicketStatus{ + Name: request.GetString("name", ""), + Color: request.GetString("color", ""), + DisplayOrder: request.GetInt("displayOrder", 0), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create status: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Status created successfully with ID %d", status.TicketStatus.ID)), nil + }, + } +} + +// StatusUpdate updates a status in Teamwork Desk +func StatusUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodStatusUpdate), + mcp.WithDescription("Update an existing status in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the status to update."), + ), + mcp.WithString("name", + mcp.Description("The new name of the status."), + ), + mcp.WithString("color", + mcp.Description("The color of the status."), + ), + mcp.WithNumber("displayOrder", + mcp.Description("The display order of the status."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _, err := client.TicketStatuses.Update(ctx, request.GetInt("id", 0), &deskmodels.TicketStatusResponse{ + TicketStatus: deskmodels.TicketStatus{ + Name: request.GetString("name", ""), + Color: request.GetString("color", ""), + DisplayOrder: request.GetInt("displayOrder", 0), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create status: %w", err) + } + + return mcp.NewToolResultText("Status updated successfully"), nil + }, + } +} diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index 14a7973..ec5b6d8 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -9,7 +9,6 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodTypeCreate toolsets.Method = "twdesk-create_type" MethodTypeUpdate toolsets.Method = "twdesk-update_type" - MethodTypeDelete toolsets.Method = "twdesk-delete_type" MethodTypeGet toolsets.Method = "twdesk-get_type" MethodTypeList toolsets.Method = "twdesk-list_types" ) @@ -17,7 +16,6 @@ const ( func init() { toolsets.RegisterMethod(MethodTypeCreate) toolsets.RegisterMethod(MethodTypeUpdate) - toolsets.RegisterMethod(MethodTypeDelete) toolsets.RegisterMethod(MethodTypeGet) toolsets.RegisterMethod(MethodTypeList) } diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index e619841..3e345c9 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -9,7 +9,6 @@ import "github.com/teamwork/mcp/internal/toolsets" const ( MethodUserCreate toolsets.Method = "twdesk-create_user" MethodUserUpdate toolsets.Method = "twdesk-update_user" - MethodUserDelete toolsets.Method = "twdesk-delete_user" MethodUserGet toolsets.Method = "twdesk-get_user" MethodUserList toolsets.Method = "twdesk-list_users" ) @@ -17,7 +16,6 @@ const ( func init() { toolsets.RegisterMethod(MethodUserCreate) toolsets.RegisterMethod(MethodUserUpdate) - toolsets.RegisterMethod(MethodUserDelete) toolsets.RegisterMethod(MethodUserGet) toolsets.RegisterMethod(MethodUserList) } From 70a91dabc0a3578c4ece06e7119da4e312526e65 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Tue, 19 Aug 2025 07:40:36 -0700 Subject: [PATCH 10/21] add resources for types --- internal/twdesk/types.go | 134 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index ec5b6d8..6050325 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -1,6 +1,16 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + deskmodels "github.com/teamwork/desksdkgo/models" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -19,3 +29,125 @@ func init() { toolsets.RegisterMethod(MethodTypeGet) toolsets.RegisterMethod(MethodTypeList) } + +// TypeGet finds a type in Teamwork Desk. This will find it by ID +func TypeGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTypeGet), + mcp.WithDescription("Get a type from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the type to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + t, err := client.TicketTypes.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get type: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Type retrieved successfully: %s", t.TicketType.Name)), nil + }, + } +} + +// TypeList returns a list of types that apply to the filters in Teamwork Desk +func TypeList(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTypeList), + mcp.WithDescription("List all types in Teamwork Desk"), + mcp.WithArray("name", mcp.Description("The name of the type to filter by.")), + mcp.WithArray("inboxIDs", mcp.Description("The inbox IDs of the type to filter by.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the type list + name := request.GetStringSlice("name", []string{}) + inboxIDs := request.GetStringSlice("inboxIDs", []string{}) + + filter := deskclient.NewFilter() + if len(name) > 0 { + filter = filter.In("name", name) + } + if len(inboxIDs) > 0 { + filter = filter.In("inboxes.id", inboxIDs) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + + types, err := client.TicketTypes.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list types: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Types retrieved successfully: %v", types)), nil + }, + } +} + +// TypeCreate creates a type in Teamwork Desk +func TypeCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTypeCreate), + mcp.WithDescription("Create a new type in Teamwork Desk"), + mcp.WithString("name", + mcp.Required(), + mcp.Description("The name of the type."), + ), + mcp.WithNumber("displayOrder", mcp.Description("The display order of the type.")), + mcp.WithBoolean("enabledForFutureInboxes", + mcp.Description("Whether the type is enabled for future inboxes."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + t, err := client.TicketTypes.Create(ctx, &deskmodels.TicketTypeResponse{ + TicketType: deskmodels.TicketType{ + Name: request.GetString("name", ""), + DisplayOrder: request.GetInt("displayOrder", 0), + EnabledForFutureInboxes: request.GetBool("enabledForFutureInboxes", false), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create type: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Type created successfully with ID %d", t.TicketType.ID)), nil + }, + } +} + +// TypeUpdate updates a type in Teamwork Desk +func TypeUpdate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodTypeUpdate), + mcp.WithDescription("Update an existing type in Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the type to update."), + ), + mcp.WithString("name", + mcp.Description("The new name of the type."), + ), + mcp.WithNumber("displayOrder", + mcp.Description("The display order of the type."), + ), + mcp.WithBoolean("enabledForFutureInboxes", + mcp.Description("Whether the type is enabled for future inboxes."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _, err := client.TicketTypes.Update(ctx, request.GetInt("id", 0), &deskmodels.TicketTypeResponse{ + TicketType: deskmodels.TicketType{ + Name: request.GetString("name", ""), + DisplayOrder: request.GetInt("displayOrder", 0), + EnabledForFutureInboxes: request.GetBool("enabledForFutureInboxes", false), + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to create type: %w", err) + } + + return mcp.NewToolResultText("Type updated successfully"), nil + }, + } +} From 5f7879c75a698de33cec390e99ec82664227ef62 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Tue, 19 Aug 2025 08:33:07 -0700 Subject: [PATCH 11/21] fetch users --- internal/twdesk/companies.go | 4 +- internal/twdesk/customers.go | 4 +- internal/twdesk/meta.go | 4 +- internal/twdesk/tickets.go | 12 ++++-- internal/twdesk/users.go | 84 +++++++++++++++++++++++++++++++++--- 5 files changed, 92 insertions(+), 16 deletions(-) diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index b4d658b..81e5ed1 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -60,7 +60,7 @@ func CompanyList(client *deskclient.Client) server.ServerTool { mcp.WithString("kind", mcp.Description("The kind of the company to filter by."), mcp.Enum("company", "group")), } - opts = append(opts, PaginationOptions()...) + opts = append(opts, paginationOptions()...) return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyList), opts...), @@ -85,7 +85,7 @@ func CompanyList(client *deskclient.Client) server.ServerTool { params := url.Values{} params.Set("filter", filter.Build()) - SetPagination(¶ms, request) + setPagination(¶ms, request) companies, err := client.Companies.List(ctx, params) if err != nil { diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index 6afe301..a1c2e63 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -60,7 +60,7 @@ func CustomerList(client *deskclient.Client) server.ServerTool { mcp.WithArray("emails", mcp.Description("The emails of the customers to filter by.")), } - opts = append(opts, PaginationOptions()...) + opts = append(opts, paginationOptions()...) return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerList), opts...), @@ -85,7 +85,7 @@ func CustomerList(client *deskclient.Client) server.ServerTool { params := url.Values{} params.Set("filter", filter.Build()) - SetPagination(¶ms, request) + setPagination(¶ms, request) customers, err := client.Customers.List(ctx, params) if err != nil { diff --git a/internal/twdesk/meta.go b/internal/twdesk/meta.go index c052160..9a57171 100644 --- a/internal/twdesk/meta.go +++ b/internal/twdesk/meta.go @@ -7,7 +7,7 @@ import ( "github.com/mark3labs/mcp-go/mcp" ) -func PaginationOptions() []mcp.ToolOption { +func paginationOptions() []mcp.ToolOption { return []mcp.ToolOption{ mcp.WithNumber("page", mcp.Description("The page number to retrieve.")), mcp.WithNumber("pageSize", mcp.Description("The number of tickets to retrieve per page.")), @@ -16,7 +16,7 @@ func PaginationOptions() []mcp.ToolOption { } } -func SetPagination(v *url.Values, request mcp.CallToolRequest) { +func setPagination(v *url.Values, request mcp.CallToolRequest) { v.Set("page", fmt.Sprintf("%d", request.GetInt("page", 1))) v.Set("pageSize", fmt.Sprintf("%d", request.GetInt("pageSize", 10))) v.Set("orderBy", request.GetString("orderBy", "createdAt")) diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index 759d2a8..8aba0eb 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -78,7 +78,7 @@ func TicketList(client *deskclient.Client) server.ServerTool { mcp.WithBoolean("slaBreached", mcp.Description("Find tickets where the SLA has been breached")), } - opts = append(opts, PaginationOptions()...) + opts = append(opts, paginationOptions()...) return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketList), opts...), @@ -149,7 +149,7 @@ func TicketList(client *deskclient.Client) server.ServerTool { params := url.Values{} params.Set("filter", filter.Build()) - SetPagination(¶ms, request) + setPagination(¶ms, request) tickets, err := client.Tickets.List(ctx, params) if err != nil { @@ -166,10 +166,16 @@ func TicketCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketCreate), mcp.WithDescription("Create a new ticket in Teamwork Desk"), + mcp.WithString("subject", mcp.Required(), mcp.Description("The subject of the ticket.")), + mcp.WithString("description", mcp.Description("The description of the ticket.")), + mcp.WithString("priority", mcp.Description("The priority of the ticket.")), + mcp.WithString("status", mcp.Description("The status of the ticket.")), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { ticket, err := client.Tickets.Create(ctx, &deskmodels.TicketResponse{ - Ticket: deskmodels.Ticket{}, + Ticket: deskmodels.Ticket{ + Subject: request.GetString("subject", ""), + }, }) if err != nil { return nil, fmt.Errorf("failed to create ticket: %w", err) diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index 3e345c9..ce9a130 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -1,21 +1,91 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "fmt" + "net/url" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // // The naming convention for methods follows a pattern described here: // https://github.com/github/github-mcp-server/issues/333 const ( - MethodUserCreate toolsets.Method = "twdesk-create_user" - MethodUserUpdate toolsets.Method = "twdesk-update_user" - MethodUserGet toolsets.Method = "twdesk-get_user" - MethodUserList toolsets.Method = "twdesk-list_users" + MethodUserGet toolsets.Method = "twdesk-get_user" + MethodUserList toolsets.Method = "twdesk-list_users" ) func init() { - toolsets.RegisterMethod(MethodUserCreate) - toolsets.RegisterMethod(MethodUserUpdate) toolsets.RegisterMethod(MethodUserGet) toolsets.RegisterMethod(MethodUserList) } + +// UserGet finds a user in Teamwork Desk. This will find it by ID +func UserGet(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodUserGet), + mcp.WithDescription("Get a user from Teamwork Desk"), + mcp.WithString("id", + mcp.Required(), + mcp.Description("The ID of the user to retrieve."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + user, err := client.Users.Get(ctx, request.GetInt("id", 0)) + if err != nil { + return nil, fmt.Errorf("failed to get user: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("User retrieved successfully: %s", user.User.FirstName)), nil + }, + } +} + +// UserList returns a list of users that apply to the filters in Teamwork Desk +func UserList(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodUserList), + mcp.WithDescription("List all users in Teamwork Desk"), + mcp.WithArray("firstName", mcp.Description("The first names of the users to filter by.")), + mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), + mcp.WithArray("email", mcp.Description("The email addresses of the users to filter by.")), + mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + // Apply filters to the user list + firstNames := request.GetStringSlice("firstName", []string{}) + lastNames := request.GetStringSlice("lastName", []string{}) + emails := request.GetStringSlice("email", []string{}) + inboxIDs := request.GetIntSlice("inboxIDs", []int{}) + + filter := deskclient.NewFilter() + if len(firstNames) > 0 { + filter = filter.In("first_name", firstNames) + } + if len(lastNames) > 0 { + filter = filter.In("last_name", lastNames) + } + if len(emails) > 0 { + filter = filter.In("email", emails) + } + if len(inboxIDs) > 0 { + filter = filter.In("inboxes.id", inboxIDs) + } + + params := url.Values{} + params.Set("filter", filter.Build()) + + users, err := client.Users.List(ctx, params) + if err != nil { + return nil, fmt.Errorf("failed to list users: %w", err) + } + + return mcp.NewToolResultText(fmt.Sprintf("Users retrieved successfully: %v", users)), nil + }, + } +} From bc5326b6f85184e58d459e179a780f35aff4d044 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Tue, 19 Aug 2025 08:39:24 -0700 Subject: [PATCH 12/21] filter part time --- internal/twdesk/users.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index ce9a130..cf05428 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -55,6 +55,7 @@ func UserList(client *deskclient.Client) server.ServerTool { mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), mcp.WithArray("email", mcp.Description("The email addresses of the users to filter by.")), mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + mcp.WithBoolean("isPartTime", mcp.Description("Whether to include part-time users in the results.")), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the user list @@ -65,10 +66,10 @@ func UserList(client *deskclient.Client) server.ServerTool { filter := deskclient.NewFilter() if len(firstNames) > 0 { - filter = filter.In("first_name", firstNames) + filter = filter.In("firstName", firstNames) } if len(lastNames) > 0 { - filter = filter.In("last_name", lastNames) + filter = filter.In("lastName", lastNames) } if len(emails) > 0 { filter = filter.In("email", emails) @@ -77,6 +78,11 @@ func UserList(client *deskclient.Client) server.ServerTool { filter = filter.In("inboxes.id", inboxIDs) } + isPartTime := request.GetBool("isPartTime", false) + if isPartTime { + filter = filter.Eq("isPartTime", true) + } + params := url.Values{} params.Set("filter", filter.Build()) From 40bb2dd40e7aff476ad68195334851ae980c3658 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Tue, 19 Aug 2025 08:44:11 -0700 Subject: [PATCH 13/21] add pagination --- internal/twdesk/priorities.go | 14 +++++++++----- internal/twdesk/statuses.go | 16 ++++++++++------ internal/twdesk/tags.go | 16 ++++++++++------ internal/twdesk/types.go | 13 ++++++++----- internal/twdesk/users.go | 20 ++++++++++++-------- 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go index 7a4b5e4..7d57afc 100644 --- a/internal/twdesk/priorities.go +++ b/internal/twdesk/priorities.go @@ -53,12 +53,16 @@ func PriorityGet(client *deskclient.Client) server.ServerTool { // PriorityList returns a list of priorities that apply to the filters in Teamwork Desk func PriorityList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all priorities in Teamwork Desk"), + mcp.WithArray("name", mcp.Description("The name of the priority to filter by.")), + mcp.WithArray("color", mcp.Description("The color of the priority to filter by.")), + } + + opts = append(opts, paginationOptions()...) + return server.ServerTool{ - Tool: mcp.NewTool(string(MethodPriorityList), - mcp.WithDescription("List all priorities in Teamwork Desk"), - mcp.WithArray("name", mcp.Description("The name of the priority to filter by.")), - mcp.WithArray("color", mcp.Description("The color of the priority to filter by.")), - ), + Tool: mcp.NewTool(string(MethodPriorityList), opts...), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the priority list name := request.GetStringSlice("name", []string{}) diff --git a/internal/twdesk/statuses.go b/internal/twdesk/statuses.go index 5e70d67..87f1fab 100644 --- a/internal/twdesk/statuses.go +++ b/internal/twdesk/statuses.go @@ -53,13 +53,17 @@ func StatusGet(client *deskclient.Client) server.ServerTool { // StatusList returns a list of statuses that apply to the filters in Teamwork Desk func StatusList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all statuses in Teamwork Desk"), + mcp.WithArray("name", mcp.Description("The name of the status to filter by.")), + mcp.WithArray("color", mcp.Description("The color of the status to filter by.")), + mcp.WithArray("code", mcp.Description("The code of the status to filter by.")), + } + + opts = append(opts, paginationOptions()...) + return server.ServerTool{ - Tool: mcp.NewTool(string(MethodStatusList), - mcp.WithDescription("List all statuses in Teamwork Desk"), - mcp.WithArray("name", mcp.Description("The name of the status to filter by.")), - mcp.WithArray("color", mcp.Description("The color of the status to filter by.")), - mcp.WithArray("code", mcp.Description("The code of the status to filter by.")), - ), + Tool: mcp.NewTool(string(MethodStatusList), opts...), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the status list name := request.GetStringSlice("name", []string{}) diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index 145baf4..2925e20 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -53,13 +53,17 @@ func TagGet(client *deskclient.Client) server.ServerTool { // TagList returns a list of tags that apply to the filters in Teamwork Desk func TagList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all tags in Teamwork Desk"), + mcp.WithString("name", mcp.Description("The name of the tag to filter by.")), + mcp.WithString("color", mcp.Description("The color of the tag to filter by.")), + mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + } + + opts = append(opts, paginationOptions()...) + return server.ServerTool{ - Tool: mcp.NewTool(string(MethodTagList), - mcp.WithDescription("List all tags in Teamwork Desk"), - mcp.WithString("name", mcp.Description("The name of the tag to filter by.")), - mcp.WithString("color", mcp.Description("The color of the tag to filter by.")), - mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), - ), + Tool: mcp.NewTool(string(MethodTagList), opts...), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the tag list name := request.GetString("name", "") diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index 6050325..f24ad29 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -53,12 +53,15 @@ func TypeGet(client *deskclient.Client) server.ServerTool { // TypeList returns a list of types that apply to the filters in Teamwork Desk func TypeList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all types in Teamwork Desk"), + mcp.WithArray("name", mcp.Description("The name of the type to filter by.")), + mcp.WithArray("inboxIDs", mcp.Description("The inbox IDs of the type to filter by.")), + } + + opts = append(opts, paginationOptions()...) return server.ServerTool{ - Tool: mcp.NewTool(string(MethodTypeList), - mcp.WithDescription("List all types in Teamwork Desk"), - mcp.WithArray("name", mcp.Description("The name of the type to filter by.")), - mcp.WithArray("inboxIDs", mcp.Description("The inbox IDs of the type to filter by.")), - ), + Tool: mcp.NewTool(string(MethodTypeList), opts...), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the type list name := request.GetStringSlice("name", []string{}) diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index cf05428..51ab746 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -48,15 +48,19 @@ func UserGet(client *deskclient.Client) server.ServerTool { // UserList returns a list of users that apply to the filters in Teamwork Desk func UserList(client *deskclient.Client) server.ServerTool { + opts := []mcp.ToolOption{ + mcp.WithDescription("List all users in Teamwork Desk"), + mcp.WithArray("firstName", mcp.Description("The first names of the users to filter by.")), + mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), + mcp.WithArray("email", mcp.Description("The email addresses of the users to filter by.")), + mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), + mcp.WithBoolean("isPartTime", mcp.Description("Whether to include part-time users in the results.")), + } + + opts = append(opts, paginationOptions()...) + return server.ServerTool{ - Tool: mcp.NewTool(string(MethodUserList), - mcp.WithDescription("List all users in Teamwork Desk"), - mcp.WithArray("firstName", mcp.Description("The first names of the users to filter by.")), - mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), - mcp.WithArray("email", mcp.Description("The email addresses of the users to filter by.")), - mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), - mcp.WithBoolean("isPartTime", mcp.Description("Whether to include part-time users in the results.")), - ), + Tool: mcp.NewTool(string(MethodUserList), opts...), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { // Apply filters to the user list firstNames := request.GetStringSlice("firstName", []string{}) From 69dda5bfa7e8f58c7287ca52413fa785dc2aa1f3 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 05:59:23 -0700 Subject: [PATCH 14/21] enable desk toolsets --- cmd/mcp-stdio/main.go | 16 +++++++---- internal/config/config.go | 20 +++++++++++--- internal/twdesk/desk.go | 9 +++++++ internal/twdesk/messages.go | 37 +++++++++++++++++++++----- internal/twdesk/tools.go | 53 +++++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 internal/twdesk/desk.go create mode 100644 internal/twdesk/tools.go diff --git a/cmd/mcp-stdio/main.go b/cmd/mcp-stdio/main.go index 910e223..382e82f 100644 --- a/cmd/mcp-stdio/main.go +++ b/cmd/mcp-stdio/main.go @@ -16,6 +16,7 @@ import ( "github.com/teamwork/mcp/internal/auth" "github.com/teamwork/mcp/internal/config" "github.com/teamwork/mcp/internal/toolsets" + "github.com/teamwork/mcp/internal/twdesk" "github.com/teamwork/mcp/internal/twprojects" "github.com/teamwork/twapi-go-sdk/session" ) @@ -67,12 +68,17 @@ func main() { } func newMCPServer(resources config.Resources) (*server.MCPServer, error) { - // TODO: define a group for desk - group := twprojects.DefaultToolsetGroup(readOnly, false, resources.TeamworkEngine()) - if err := group.EnableToolsets(methods...); err != nil { - return nil, fmt.Errorf("failed to enable toolsets: %w", err) + projectsGroup := twprojects.DefaultToolsetGroup(readOnly, false, resources.TeamworkEngine()) + if err := projectsGroup.EnableToolsets(methods...); err != nil { + return nil, fmt.Errorf("failed to enable projects toolsets: %w", err) } - return config.NewMCPServer(resources, group), nil + + deskGroup := twdesk.DefaultToolsetGroup(resources.DeskClient()) + if err := deskGroup.EnableToolsets(methods...); err != nil { + return nil, fmt.Errorf("failed to enable desk toolsets: %w", err) + } + + return config.NewMCPServer(resources, projectsGroup, deskGroup), nil } func mcpError(logger *slog.Logger, err error, code int) { diff --git a/internal/config/config.go b/internal/config/config.go index 82d9337..42f855d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -136,13 +136,27 @@ func Load(logOutput io.Writer) (Resources, func()) { // NewMCPServer creates a new MCP server with the given resources and toolset // group. -func NewMCPServer(resources Resources, group *toolsets.ToolsetGroup) *server.MCPServer { +func NewMCPServer(resources Resources, groups ...*toolsets.ToolsetGroup) *server.MCPServer { + // Determine if any group has tools + hasTools := false + for _, group := range groups { + if group.HasTools() { + hasTools = true + break + } + } + mcpServer := server.NewMCPServer(mcpName, strings.TrimPrefix(resources.Info.Version, "v"), server.WithRecovery(), - server.WithToolCapabilities(group.HasTools()), + server.WithToolCapabilities(hasTools), server.WithLogging(), ) - group.RegisterAll(mcpServer) + + // Register all toolset groups + for _, group := range groups { + group.RegisterAll(mcpServer) + } + return mcpServer } diff --git a/internal/twdesk/desk.go b/internal/twdesk/desk.go new file mode 100644 index 0000000..0dbbde6 --- /dev/null +++ b/internal/twdesk/desk.go @@ -0,0 +1,9 @@ +package twdesk + +const projectDescription = "Teamwork Desk is a customer support helpdesk solution that enables teams to manage " + + "customer inquiries, support tickets, and service requests in a centralized platform. It provides tools for " + + "ticket management, customer communication, knowledge base creation, and performance tracking. Support agents " + + "can efficiently handle customer issues through multiple channels including email, and chat, while " + + "maintaining a complete history of customer interactions. With features like automated workflows, SLA tracking, " + + "ticket prioritization, and team collaboration tools, Teamwork Desk helps support teams deliver exceptional " + + "customer service, resolve issues faster, and maintain high levels of customer satisfaction." diff --git a/internal/twdesk/messages.go b/internal/twdesk/messages.go index 594b58c..018d6ae 100644 --- a/internal/twdesk/messages.go +++ b/internal/twdesk/messages.go @@ -1,6 +1,14 @@ package twdesk -import "github.com/teamwork/mcp/internal/toolsets" +import ( + "context" + "errors" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + "github.com/teamwork/mcp/internal/toolsets" +) // List of methods available in the Teamwork.com MCP service. // @@ -8,14 +16,29 @@ import "github.com/teamwork/mcp/internal/toolsets" // https://github.com/github/github-mcp-server/issues/333 const ( MethodMessageCreate toolsets.Method = "twdesk-create_message" - MethodMessageUpdate toolsets.Method = "twdesk-update_message" - MethodMessageGet toolsets.Method = "twdesk-get_message" - MethodMessageList toolsets.Method = "twdesk-list_messages" ) func init() { toolsets.RegisterMethod(MethodMessageCreate) - toolsets.RegisterMethod(MethodMessageUpdate) - toolsets.RegisterMethod(MethodMessageGet) - toolsets.RegisterMethod(MethodMessageList) +} + +// MessageCreate replies to a ticket in Teamwork Desk. TODO: Still need to +// define the client for this. +func MessageCreate(client *deskclient.Client) server.ServerTool { + return server.ServerTool{ + Tool: mcp.NewTool(string(MethodMessageCreate), + mcp.WithDescription("Craft a reply to a ticket in Teamwork Desk"), + mcp.WithNumber("ticketID", + mcp.Required(), + mcp.Description("The ID of the ticket that the message will be sent to."), + ), + mcp.WithString("body", + mcp.Required(), + mcp.Description("The body of the message."), + ), + ), + Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + return nil, errors.New("not implemented") + }, + } } diff --git a/internal/twdesk/tools.go b/internal/twdesk/tools.go new file mode 100644 index 0000000..cd72592 --- /dev/null +++ b/internal/twdesk/tools.go @@ -0,0 +1,53 @@ +package twdesk + +import ( + "github.com/mark3labs/mcp-go/server" + deskclient "github.com/teamwork/desksdkgo/client" + "github.com/teamwork/mcp/internal/toolsets" +) + +// DefaultToolsetGroup creates a default ToolsetGroup for Teamwork Projects. +func DefaultToolsetGroup(client *deskclient.Client) *toolsets.ToolsetGroup { + readTools := []server.ServerTool{ + CompanyGet(client), + CompanyList(client), + CustomerGet(client), + CustomerList(client), + PriorityGet(client), + PriorityList(client), + StatusGet(client), + StatusList(client), + TagGet(client), + TagList(client), + TicketGet(client), + TicketList(client), + TypeGet(client), + TypeList(client), + UserGet(client), + UserList(client), + } + + writeTools := []server.ServerTool{ + CompanyCreate(client), + CompanyUpdate(client), + CustomerCreate(client), + CustomerUpdate(client), + MessageCreate(client), + PriorityCreate(client), + PriorityUpdate(client), + StatusCreate(client), + StatusUpdate(client), + TagCreate(client), + TagUpdate(client), + TicketCreate(client), + TicketUpdate(client), + TypeCreate(client), + TypeUpdate(client), + } + + group := toolsets.NewToolsetGroup(false) + group.AddToolset(toolsets.NewToolset("desk", projectDescription). + AddWriteTools(writeTools...). + AddReadTools(readTools...)) + return group +} From f9d7e1d0353ad1502a97d70f164ccad97e1030f7 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 06:13:26 -0700 Subject: [PATCH 15/21] setup desk resource --- internal/config/config.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/config/config.go b/internal/config/config.go index 42f855d..3fb7574 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -20,6 +20,7 @@ import ( "github.com/mark3labs/mcp-go/client/transport" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" + desksdk "github.com/teamwork/desksdkgo/client" "github.com/teamwork/mcp/internal/request" "github.com/teamwork/mcp/internal/toolsets" twapi "github.com/teamwork/twapi-go-sdk" @@ -116,6 +117,11 @@ func Load(logOutput io.Writer) (Resources, func()) { twapi.WithLogger(resources.logger), ) + resources.deskClient = desksdk.NewClient( + resources.Info.APIURL, + desksdk.WithAPIKey(resources.Info.BearerToken), + ) + if resources.Info.DatadogAPM.Enabled { if err := startDatadog(resources); err != nil { resources.logger.Error("failed to start datadog tracer", From b4a1f1e8d3e96bfb5707d5d79626cb4750f23654 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 06:22:12 -0700 Subject: [PATCH 16/21] enable desk http mcp --- cmd/mcp-http/main.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cmd/mcp-http/main.go b/cmd/mcp-http/main.go index 652431d..1930612 100644 --- a/cmd/mcp-http/main.go +++ b/cmd/mcp-http/main.go @@ -23,6 +23,7 @@ import ( "github.com/teamwork/mcp/internal/config" "github.com/teamwork/mcp/internal/request" "github.com/teamwork/mcp/internal/toolsets" + "github.com/teamwork/mcp/internal/twdesk" "github.com/teamwork/mcp/internal/twprojects" "github.com/teamwork/twapi-go-sdk/session" ) @@ -91,11 +92,17 @@ func main() { } func newMCPServer(resources config.Resources) (*server.MCPServer, error) { - group := twprojects.DefaultToolsetGroup(false, false, resources.TeamworkEngine()) - if err := group.EnableToolsets(toolsets.MethodAll); err != nil { + projectsGroup := twprojects.DefaultToolsetGroup(false, false, resources.TeamworkEngine()) + if err := projectsGroup.EnableToolsets(toolsets.MethodAll); err != nil { return nil, fmt.Errorf("failed to enable toolsets: %w", err) } - return config.NewMCPServer(resources, group), nil + + deskGroup := twdesk.DefaultToolsetGroup(resources.DeskClient()) + if err := deskGroup.EnableToolsets(toolsets.MethodAll); err != nil { + return nil, fmt.Errorf("failed to enable desk toolsets: %w", err) + } + + return config.NewMCPServer(resources, projectsGroup, deskGroup), nil } func newRouter(resources config.Resources) *http.ServeMux { From 40682834bdf88f4661a47d68424fb7fa6e514535 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 06:26:27 -0700 Subject: [PATCH 17/21] fix lint --- internal/twdesk/messages.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/twdesk/messages.go b/internal/twdesk/messages.go index 018d6ae..4440482 100644 --- a/internal/twdesk/messages.go +++ b/internal/twdesk/messages.go @@ -38,6 +38,9 @@ func MessageCreate(client *deskclient.Client) server.ServerTool { ), ), Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + _ = client // TODO: use the client to create the message + _ = ctx + _ = request return nil, errors.New("not implemented") }, } From 061e1fe1b6893c0499ca56133ac0003678af4bf6 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 06:39:15 -0700 Subject: [PATCH 18/21] mark tools as readonly --- internal/twdesk/companies.go | 2 ++ internal/twdesk/customers.go | 2 ++ internal/twdesk/priorities.go | 2 ++ internal/twdesk/statuses.go | 2 ++ internal/twdesk/tags.go | 2 ++ internal/twdesk/tickets.go | 2 ++ internal/twdesk/types.go | 2 ++ internal/twdesk/users.go | 2 ++ 8 files changed, 16 insertions(+) diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index 81e5ed1..56e4a0a 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -35,6 +35,7 @@ func CompanyGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyGet), mcp.WithDescription("Get a company from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the company to retrieve."), @@ -55,6 +56,7 @@ func CompanyGet(client *deskclient.Client) server.ServerTool { func CompanyList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all companies in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("name", mcp.Description("The name of the company to filter by.")), mcp.WithArray("domains", mcp.Description("The domains of the company to filter by.")), mcp.WithString("kind", mcp.Description("The kind of the company to filter by."), mcp.Enum("company", "group")), diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index a1c2e63..6a722e2 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -35,6 +35,7 @@ func CustomerGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerGet), mcp.WithDescription("Get a customer from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to retrieve."), @@ -55,6 +56,7 @@ func CustomerGet(client *deskclient.Client) server.ServerTool { func CustomerList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all customers in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), mcp.WithArray("companyNames", mcp.Description("The names of the companies to filter by.")), mcp.WithArray("emails", mcp.Description("The emails of the customers to filter by.")), diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go index 7d57afc..dadf182 100644 --- a/internal/twdesk/priorities.go +++ b/internal/twdesk/priorities.go @@ -35,6 +35,7 @@ func PriorityGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityGet), mcp.WithDescription("Get a priority from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the priority to retrieve."), @@ -55,6 +56,7 @@ func PriorityGet(client *deskclient.Client) server.ServerTool { func PriorityList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all priorities in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the priority to filter by.")), mcp.WithArray("color", mcp.Description("The color of the priority to filter by.")), } diff --git a/internal/twdesk/statuses.go b/internal/twdesk/statuses.go index 87f1fab..72a617e 100644 --- a/internal/twdesk/statuses.go +++ b/internal/twdesk/statuses.go @@ -35,6 +35,7 @@ func StatusGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusGet), mcp.WithDescription("Get a status from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the status to retrieve."), @@ -55,6 +56,7 @@ func StatusGet(client *deskclient.Client) server.ServerTool { func StatusList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all statuses in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the status to filter by.")), mcp.WithArray("color", mcp.Description("The color of the status to filter by.")), mcp.WithArray("code", mcp.Description("The code of the status to filter by.")), diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index 2925e20..464adb4 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -35,6 +35,7 @@ func TagGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagGet), mcp.WithDescription("Get a tag from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the tag to retrieve."), @@ -55,6 +56,7 @@ func TagGet(client *deskclient.Client) server.ServerTool { func TagList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all tags in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("name", mcp.Description("The name of the tag to filter by.")), mcp.WithString("color", mcp.Description("The color of the tag to filter by.")), mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index 8aba0eb..057fdc9 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -37,6 +37,7 @@ func TicketGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketGet), mcp.WithDescription("Get a ticket from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the ticket to retrieve."), @@ -64,6 +65,7 @@ func TicketGet(client *deskclient.Client) server.ServerTool { func TicketList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all tickets in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), mcp.WithArray("customerIDs", mcp.Description("The IDs of the customers to filter by.")), mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index f24ad29..f6d8071 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -35,6 +35,7 @@ func TypeGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeGet), mcp.WithDescription("Get a type from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the type to retrieve."), @@ -55,6 +56,7 @@ func TypeGet(client *deskclient.Client) server.ServerTool { func TypeList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all types in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the type to filter by.")), mcp.WithArray("inboxIDs", mcp.Description("The inbox IDs of the type to filter by.")), } diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index 51ab746..0d1e5a3 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -30,6 +30,7 @@ func UserGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodUserGet), mcp.WithDescription("Get a user from Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the user to retrieve."), @@ -50,6 +51,7 @@ func UserGet(client *deskclient.Client) server.ServerTool { func UserList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ mcp.WithDescription("List all users in Teamwork Desk"), + mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("firstName", mcp.Description("The first names of the users to filter by.")), mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), mcp.WithArray("email", mcp.Description("The email addresses of the users to filter by.")), From 5bbc4d4e752264028c621eef744587d6f82d1c7f Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 07:12:29 -0700 Subject: [PATCH 19/21] update descriptions --- internal/twdesk/companies.go | 8 ++++---- internal/twdesk/customers.go | 8 ++++---- internal/twdesk/messages.go | 2 +- internal/twdesk/priorities.go | 8 ++++---- internal/twdesk/statuses.go | 8 ++++---- internal/twdesk/tags.go | 8 ++++---- internal/twdesk/tickets.go | 8 ++++---- internal/twdesk/types.go | 8 ++++---- internal/twdesk/users.go | 4 ++-- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index 56e4a0a..49fafe3 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -34,7 +34,7 @@ func init() { func CompanyGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyGet), - mcp.WithDescription("Get a company from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific company in Teamwork Desk by its ID. Useful for auditing company records, troubleshooting ticket associations, or integrating Desk company data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +55,7 @@ func CompanyGet(client *deskclient.Client) server.ServerTool { // CompanyList returns a list of companies that apply to the filters in Teamwork Desk func CompanyList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all companies in Teamwork Desk"), + mcp.WithDescription("List all companies in Teamwork Desk, with optional filters for name, domains, and kind. Enables users to audit, analyze, or synchronize company configurations for ticket management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("name", mcp.Description("The name of the company to filter by.")), mcp.WithArray("domains", mcp.Description("The domains of the company to filter by.")), @@ -103,7 +103,7 @@ func CompanyList(client *deskclient.Client) server.ServerTool { func CompanyCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyCreate), - mcp.WithDescription("Create a new company in Teamwork Desk"), + mcp.WithDescription("Create a new company in Teamwork Desk by specifying its name, domains, and other attributes. Useful for onboarding new organizations, customizing Desk for business relationships, or adapting support processes."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the company to update."), @@ -176,7 +176,7 @@ func CompanyCreate(client *deskclient.Client) server.ServerTool { func CompanyUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyUpdate), - mcp.WithDescription("Update an existing company in Teamwork Desk"), + mcp.WithDescription("Update an existing company in Teamwork Desk by ID, allowing changes to its name, domains, and other attributes. Supports evolving business relationships, rebranding, or correcting company records for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the company to update."), diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index 6a722e2..21ab938 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -34,7 +34,7 @@ func init() { func CustomerGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerGet), - mcp.WithDescription("Get a customer from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific customer in Teamwork Desk by their ID. Useful for auditing customer records, troubleshooting ticket associations, or integrating Desk customer data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +55,7 @@ func CustomerGet(client *deskclient.Client) server.ServerTool { // CustomerList returns a list of customers that apply to the filters in Teamwork Desk func CustomerList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all customers in Teamwork Desk"), + mcp.WithDescription("List all customers in Teamwork Desk, with optional filters for company, email, and other attributes. Enables users to audit, analyze, or synchronize customer configurations for ticket management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), mcp.WithArray("companyNames", mcp.Description("The names of the companies to filter by.")), @@ -103,7 +103,7 @@ func CustomerList(client *deskclient.Client) server.ServerTool { func CustomerCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerCreate), - mcp.WithDescription("Create a customer in Teamwork Desk"), + mcp.WithDescription("Create a new customer in Teamwork Desk by specifying their name, contact details, and other attributes. Useful for onboarding new clients, customizing Desk for business relationships, or adapting support processes."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to update."), @@ -191,7 +191,7 @@ func CustomerCreate(client *deskclient.Client) server.ServerTool { func CustomerUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerUpdate), - mcp.WithDescription("Update an existing customer in Teamwork Desk"), + mcp.WithDescription("Update an existing customer in Teamwork Desk by ID, allowing changes to their name, contact details, and other attributes. Supports evolving business relationships, correcting customer records, or improving ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to update."), diff --git a/internal/twdesk/messages.go b/internal/twdesk/messages.go index 4440482..67046fe 100644 --- a/internal/twdesk/messages.go +++ b/internal/twdesk/messages.go @@ -27,7 +27,7 @@ func init() { func MessageCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodMessageCreate), - mcp.WithDescription("Craft a reply to a ticket in Teamwork Desk"), + mcp.WithDescription("Send a reply message to a ticket in Teamwork Desk by specifying the ticket ID and message body. Useful for automating ticket responses, integrating external communication systems, or customizing support workflows."), mcp.WithNumber("ticketID", mcp.Required(), mcp.Description("The ID of the ticket that the message will be sent to."), diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go index dadf182..af2f08c 100644 --- a/internal/twdesk/priorities.go +++ b/internal/twdesk/priorities.go @@ -34,7 +34,7 @@ func init() { func PriorityGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityGet), - mcp.WithDescription("Get a priority from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific priority in Teamwork Desk by its ID. Useful for inspecting priority attributes, troubleshooting ticket routing, or integrating Desk priority data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +55,7 @@ func PriorityGet(client *deskclient.Client) server.ServerTool { // PriorityList returns a list of priorities that apply to the filters in Teamwork Desk func PriorityList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all priorities in Teamwork Desk"), + mcp.WithDescription("List all available priorities in Teamwork Desk, with optional filters for name and color. Enables users to audit, analyze, or synchronize priority configurations for ticket management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the priority to filter by.")), mcp.WithArray("color", mcp.Description("The color of the priority to filter by.")), @@ -95,7 +95,7 @@ func PriorityList(client *deskclient.Client) server.ServerTool { func PriorityCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityCreate), - mcp.WithDescription("Create a new priority in Teamwork Desk"), + mcp.WithDescription("Create a new priority in Teamwork Desk by specifying its name and color. Useful for customizing ticket workflows, introducing new escalation levels, or adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the priority."), @@ -124,7 +124,7 @@ func PriorityCreate(client *deskclient.Client) server.ServerTool { func PriorityUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityUpdate), - mcp.WithDescription("Update an existing priority in Teamwork Desk"), + mcp.WithDescription("Update an existing priority in Teamwork Desk by ID, allowing changes to its name and color. Supports evolving support policies, rebranding, or correcting priority attributes for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the priority to update."), diff --git a/internal/twdesk/statuses.go b/internal/twdesk/statuses.go index 72a617e..4da1732 100644 --- a/internal/twdesk/statuses.go +++ b/internal/twdesk/statuses.go @@ -34,7 +34,7 @@ func init() { func StatusGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusGet), - mcp.WithDescription("Get a status from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific status in Teamwork Desk by its ID. Useful for auditing status usage, troubleshooting ticket workflows, or integrating Desk status data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +55,7 @@ func StatusGet(client *deskclient.Client) server.ServerTool { // StatusList returns a list of statuses that apply to the filters in Teamwork Desk func StatusList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all statuses in Teamwork Desk"), + mcp.WithDescription("List all statuses in Teamwork Desk, with optional filters for name, color, and code. Enables users to audit, analyze, or synchronize status configurations for ticket management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the status to filter by.")), mcp.WithArray("color", mcp.Description("The color of the status to filter by.")), @@ -100,7 +100,7 @@ func StatusList(client *deskclient.Client) server.ServerTool { func StatusCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusCreate), - mcp.WithDescription("Create a new status in Teamwork Desk"), + mcp.WithDescription("Create a new status in Teamwork Desk by specifying its name, color, and display order. Useful for customizing ticket workflows, introducing new resolution states, or adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the status."), @@ -131,7 +131,7 @@ func StatusCreate(client *deskclient.Client) server.ServerTool { func StatusUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusUpdate), - mcp.WithDescription("Update an existing status in Teamwork Desk"), + mcp.WithDescription("Update an existing status in Teamwork Desk by ID, allowing changes to its name, color, and display order. Supports evolving support policies, rebranding, or correcting status attributes for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the status to update."), diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index 464adb4..8cb1b48 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -34,7 +34,7 @@ func init() { func TagGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagGet), - mcp.WithDescription("Get a tag from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific tag in Teamwork Desk by its ID. Useful for auditing tag usage, troubleshooting ticket categorization, or integrating Desk tag data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +55,7 @@ func TagGet(client *deskclient.Client) server.ServerTool { // TagList returns a list of tags that apply to the filters in Teamwork Desk func TagList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all tags in Teamwork Desk"), + mcp.WithDescription("List all tags in Teamwork Desk, with optional filters for name, color, and inbox association. Enables users to audit, analyze, or synchronize tag configurations for ticket management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("name", mcp.Description("The name of the tag to filter by.")), mcp.WithString("color", mcp.Description("The color of the tag to filter by.")), @@ -100,7 +100,7 @@ func TagList(client *deskclient.Client) server.ServerTool { func TagCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagCreate), - mcp.WithDescription("Create a new tag in Teamwork Desk"), + mcp.WithDescription("Create a new tag in Teamwork Desk by specifying its name and color. Useful for customizing ticket workflows, introducing new categories, or adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the tag."), @@ -129,7 +129,7 @@ func TagCreate(client *deskclient.Client) server.ServerTool { func TagUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagUpdate), - mcp.WithDescription("Update an existing tag in Teamwork Desk"), + mcp.WithDescription("Update an existing tag in Teamwork Desk by ID, allowing changes to its name and color. Supports evolving support policies, rebranding, or correcting tag attributes for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the tag to update."), diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index 057fdc9..05023a3 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -36,7 +36,7 @@ func init() { func TicketGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketGet), - mcp.WithDescription("Get a ticket from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific ticket in Teamwork Desk by its ID. Useful for auditing ticket records, troubleshooting support workflows, or integrating Desk ticket data into automation and reporting systems."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -64,7 +64,7 @@ func TicketGet(client *deskclient.Client) server.ServerTool { // TicketList returns a list of tickets that apply to the filters in Teamwork Desk func TicketList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all tickets in Teamwork Desk"), + mcp.WithDescription("List all tickets in Teamwork Desk, with extensive filters for inbox, customer, company, tag, status, priority, SLA, user, and more. Enables users to audit, analyze, or synchronize ticket data for support management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), mcp.WithArray("customerIDs", mcp.Description("The IDs of the customers to filter by.")), @@ -167,7 +167,7 @@ func TicketList(client *deskclient.Client) server.ServerTool { func TicketCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketCreate), - mcp.WithDescription("Create a new ticket in Teamwork Desk"), + mcp.WithDescription("Create a new ticket in Teamwork Desk by specifying subject, description, priority, and status. Useful for automating ticket creation, integrating external systems, or customizing support workflows."), mcp.WithString("subject", mcp.Required(), mcp.Description("The subject of the ticket.")), mcp.WithString("description", mcp.Description("The description of the ticket.")), mcp.WithString("priority", mcp.Description("The priority of the ticket.")), @@ -192,7 +192,7 @@ func TicketCreate(client *deskclient.Client) server.ServerTool { func TicketUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketUpdate), - mcp.WithDescription("Update an existing ticket in Teamwork Desk"), + mcp.WithDescription("Update an existing ticket in Teamwork Desk by ID, allowing changes to its attributes. Supports evolving support processes, correcting ticket records, or integrating with automation systems for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the ticket to update."), diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index f6d8071..34d43c1 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -34,7 +34,7 @@ func init() { func TypeGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeGet), - mcp.WithDescription("Get a type from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific ticket type in Teamwork Desk by its ID. Useful for auditing type usage, troubleshooting ticket categorization, or integrating Desk type data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +55,7 @@ func TypeGet(client *deskclient.Client) server.ServerTool { // TypeList returns a list of types that apply to the filters in Teamwork Desk func TypeList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all types in Teamwork Desk"), + mcp.WithDescription("List all ticket types in Teamwork Desk, with optional filters for name and inbox association. Enables users to audit, analyze, or synchronize type configurations for ticket management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the type to filter by.")), mcp.WithArray("inboxIDs", mcp.Description("The inbox IDs of the type to filter by.")), @@ -94,7 +94,7 @@ func TypeList(client *deskclient.Client) server.ServerTool { func TypeCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeCreate), - mcp.WithDescription("Create a new type in Teamwork Desk"), + mcp.WithDescription("Create a new ticket type in Teamwork Desk by specifying its name, display order, and future inbox settings. Useful for customizing ticket workflows, introducing new categories, or adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the type."), @@ -125,7 +125,7 @@ func TypeCreate(client *deskclient.Client) server.ServerTool { func TypeUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeUpdate), - mcp.WithDescription("Update an existing type in Teamwork Desk"), + mcp.WithDescription("Update an existing ticket type in Teamwork Desk by ID, allowing changes to its name, display order, and future inbox settings. Supports evolving support policies, rebranding, or correcting type attributes for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the type to update."), diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index 0d1e5a3..2083755 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -29,7 +29,7 @@ func init() { func UserGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodUserGet), - mcp.WithDescription("Get a user from Teamwork Desk"), + mcp.WithDescription("Retrieve detailed information about a specific user in Teamwork Desk by their ID. Useful for auditing user records, troubleshooting ticket assignments, or integrating Desk user data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -50,7 +50,7 @@ func UserGet(client *deskclient.Client) server.ServerTool { // UserList returns a list of users that apply to the filters in Teamwork Desk func UserList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all users in Teamwork Desk"), + mcp.WithDescription("List all users in Teamwork Desk, with optional filters for name, email, inbox, and part-time status. Enables users to audit, analyze, or synchronize user configurations for support management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("firstName", mcp.Description("The first names of the users to filter by.")), mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), From b9f5f8ab5d645500da65eb495034fbe80f28bd5b Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 10:04:11 -0700 Subject: [PATCH 20/21] fix formatting --- internal/twdesk/companies.go | 20 ++++++++++++++++---- internal/twdesk/customers.go | 19 +++++++++++++++---- internal/twdesk/messages.go | 5 ++++- internal/twdesk/priorities.go | 19 +++++++++++++++---- internal/twdesk/statuses.go | 20 ++++++++++++++++---- internal/twdesk/tags.go | 19 +++++++++++++++---- internal/twdesk/tickets.go | 19 +++++++++++++++---- internal/twdesk/types.go | 20 ++++++++++++++++---- internal/twdesk/users.go | 10 ++++++++-- 9 files changed, 120 insertions(+), 31 deletions(-) diff --git a/internal/twdesk/companies.go b/internal/twdesk/companies.go index 49fafe3..fd7d5ee 100644 --- a/internal/twdesk/companies.go +++ b/internal/twdesk/companies.go @@ -34,7 +34,10 @@ func init() { func CompanyGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyGet), - mcp.WithDescription("Retrieve detailed information about a specific company in Teamwork Desk by its ID. Useful for auditing company records, troubleshooting ticket associations, or integrating Desk company data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific company in Teamwork Desk by its ID. "+ + "Useful for auditing company records, troubleshooting ticket associations, or "+ + "integrating Desk company data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +58,10 @@ func CompanyGet(client *deskclient.Client) server.ServerTool { // CompanyList returns a list of companies that apply to the filters in Teamwork Desk func CompanyList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all companies in Teamwork Desk, with optional filters for name, domains, and kind. Enables users to audit, analyze, or synchronize company configurations for ticket management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all companies in Teamwork Desk, with optional filters for name, domains, and kind. " + + "Enables users to audit, analyze, or synchronize company configurations for ticket management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("name", mcp.Description("The name of the company to filter by.")), mcp.WithArray("domains", mcp.Description("The domains of the company to filter by.")), @@ -103,7 +109,10 @@ func CompanyList(client *deskclient.Client) server.ServerTool { func CompanyCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyCreate), - mcp.WithDescription("Create a new company in Teamwork Desk by specifying its name, domains, and other attributes. Useful for onboarding new organizations, customizing Desk for business relationships, or adapting support processes."), + mcp.WithDescription( + "Create a new company in Teamwork Desk by specifying its name, domains, and other attributes. "+ + "Useful for onboarding new organizations, customizing Desk for business relationships, or "+ + "adapting support processes."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the company to update."), @@ -176,7 +185,10 @@ func CompanyCreate(client *deskclient.Client) server.ServerTool { func CompanyUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCompanyUpdate), - mcp.WithDescription("Update an existing company in Teamwork Desk by ID, allowing changes to its name, domains, and other attributes. Supports evolving business relationships, rebranding, or correcting company records for improved ticket handling."), + mcp.WithDescription( + "Update an existing company in Teamwork Desk by ID, allowing changes to its name, domains, and other attributes. "+ + "Supports evolving business relationships, rebranding, or correcting company records for improved "+ + "ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the company to update."), diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index 21ab938..7de12b5 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -34,7 +34,10 @@ func init() { func CustomerGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerGet), - mcp.WithDescription("Retrieve detailed information about a specific customer in Teamwork Desk by their ID. Useful for auditing customer records, troubleshooting ticket associations, or integrating Desk customer data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific customer in Teamwork Desk by their ID. "+ + "Useful for auditing customer records, troubleshooting ticket associations, or "+ + "integrating Desk customer data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +58,10 @@ func CustomerGet(client *deskclient.Client) server.ServerTool { // CustomerList returns a list of customers that apply to the filters in Teamwork Desk func CustomerList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all customers in Teamwork Desk, with optional filters for company, email, and other attributes. Enables users to audit, analyze, or synchronize customer configurations for ticket management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all customers in Teamwork Desk, with optional filters for company, email, and other attributes. " + + "Enables users to audit, analyze, or synchronize customer configurations for ticket management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("companyIDs", mcp.Description("The IDs of the companies to filter by.")), mcp.WithArray("companyNames", mcp.Description("The names of the companies to filter by.")), @@ -103,7 +109,10 @@ func CustomerList(client *deskclient.Client) server.ServerTool { func CustomerCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerCreate), - mcp.WithDescription("Create a new customer in Teamwork Desk by specifying their name, contact details, and other attributes. Useful for onboarding new clients, customizing Desk for business relationships, or adapting support processes."), + mcp.WithDescription( + "Create a new customer in Teamwork Desk by specifying their name, contact details, and other attributes. "+ + "Useful for onboarding new clients, customizing Desk for business relationships, or "+ + "adapting support processes."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to update."), @@ -191,7 +200,9 @@ func CustomerCreate(client *deskclient.Client) server.ServerTool { func CustomerUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerUpdate), - mcp.WithDescription("Update an existing customer in Teamwork Desk by ID, allowing changes to their name, contact details, and other attributes. Supports evolving business relationships, correcting customer records, or improving ticket handling."), + mcp.WithDescription( + "Update an existing customer in Teamwork Desk by ID, allowing changes to their name, contact details, and other attributes. "+ + "Supports evolving business relationships, correcting customer records, or improving ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to update."), diff --git a/internal/twdesk/messages.go b/internal/twdesk/messages.go index 67046fe..5b931f4 100644 --- a/internal/twdesk/messages.go +++ b/internal/twdesk/messages.go @@ -27,7 +27,10 @@ func init() { func MessageCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodMessageCreate), - mcp.WithDescription("Send a reply message to a ticket in Teamwork Desk by specifying the ticket ID and message body. Useful for automating ticket responses, integrating external communication systems, or customizing support workflows."), + mcp.WithDescription( + "Send a reply message to a ticket in Teamwork Desk by specifying the ticket ID and message body. "+ + "Useful for automating ticket responses, integrating external communication systems, or "+ + "customizing support workflows."), mcp.WithNumber("ticketID", mcp.Required(), mcp.Description("The ID of the ticket that the message will be sent to."), diff --git a/internal/twdesk/priorities.go b/internal/twdesk/priorities.go index af2f08c..b315a33 100644 --- a/internal/twdesk/priorities.go +++ b/internal/twdesk/priorities.go @@ -34,7 +34,10 @@ func init() { func PriorityGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityGet), - mcp.WithDescription("Retrieve detailed information about a specific priority in Teamwork Desk by its ID. Useful for inspecting priority attributes, troubleshooting ticket routing, or integrating Desk priority data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific priority in Teamwork Desk by its ID. "+ + "Useful for inspecting priority attributes, troubleshooting ticket routing, or "+ + "integrating Desk priority data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +58,10 @@ func PriorityGet(client *deskclient.Client) server.ServerTool { // PriorityList returns a list of priorities that apply to the filters in Teamwork Desk func PriorityList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all available priorities in Teamwork Desk, with optional filters for name and color. Enables users to audit, analyze, or synchronize priority configurations for ticket management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all available priorities in Teamwork Desk, with optional filters for name and color. " + + "Enables users to audit, analyze, or synchronize priority configurations for ticket management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the priority to filter by.")), mcp.WithArray("color", mcp.Description("The color of the priority to filter by.")), @@ -95,7 +101,9 @@ func PriorityList(client *deskclient.Client) server.ServerTool { func PriorityCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityCreate), - mcp.WithDescription("Create a new priority in Teamwork Desk by specifying its name and color. Useful for customizing ticket workflows, introducing new escalation levels, or adapting Desk to evolving support processes."), + mcp.WithDescription( + "Create a new priority in Teamwork Desk by specifying its name and color. Useful for customizing "+ + "ticket workflows, introducing new escalation levels, or adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the priority."), @@ -124,7 +132,10 @@ func PriorityCreate(client *deskclient.Client) server.ServerTool { func PriorityUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodPriorityUpdate), - mcp.WithDescription("Update an existing priority in Teamwork Desk by ID, allowing changes to its name and color. Supports evolving support policies, rebranding, or correcting priority attributes for improved ticket handling."), + mcp.WithDescription( + "Update an existing priority in Teamwork Desk by ID, allowing changes to its name and color. "+ + "Supports evolving support policies, rebranding, or correcting priority attributes for improved "+ + "ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the priority to update."), diff --git a/internal/twdesk/statuses.go b/internal/twdesk/statuses.go index 4da1732..b72973f 100644 --- a/internal/twdesk/statuses.go +++ b/internal/twdesk/statuses.go @@ -34,7 +34,10 @@ func init() { func StatusGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusGet), - mcp.WithDescription("Retrieve detailed information about a specific status in Teamwork Desk by its ID. Useful for auditing status usage, troubleshooting ticket workflows, or integrating Desk status data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific status in Teamwork Desk by its ID. "+ + "Useful for auditing status usage, troubleshooting ticket workflows, or "+ + "integrating Desk status data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +58,10 @@ func StatusGet(client *deskclient.Client) server.ServerTool { // StatusList returns a list of statuses that apply to the filters in Teamwork Desk func StatusList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all statuses in Teamwork Desk, with optional filters for name, color, and code. Enables users to audit, analyze, or synchronize status configurations for ticket management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all statuses in Teamwork Desk, with optional filters for name, color, and code. " + + "Enables users to audit, analyze, or synchronize status configurations for ticket management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the status to filter by.")), mcp.WithArray("color", mcp.Description("The color of the status to filter by.")), @@ -100,7 +106,10 @@ func StatusList(client *deskclient.Client) server.ServerTool { func StatusCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusCreate), - mcp.WithDescription("Create a new status in Teamwork Desk by specifying its name, color, and display order. Useful for customizing ticket workflows, introducing new resolution states, or adapting Desk to evolving support processes."), + mcp.WithDescription( + "Create a new status in Teamwork Desk by specifying its name, color, and display order. "+ + "Useful for customizing ticket workflows, introducing new resolution states, or "+ + "adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the status."), @@ -131,7 +140,10 @@ func StatusCreate(client *deskclient.Client) server.ServerTool { func StatusUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodStatusUpdate), - mcp.WithDescription("Update an existing status in Teamwork Desk by ID, allowing changes to its name, color, and display order. Supports evolving support policies, rebranding, or correcting status attributes for improved ticket handling."), + mcp.WithDescription( + "Update an existing status in Teamwork Desk by ID, allowing changes to its name, color, and display order. "+ + "Supports evolving support policies, rebranding, or correcting status attributes for improved "+ + "ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the status to update."), diff --git a/internal/twdesk/tags.go b/internal/twdesk/tags.go index 8cb1b48..aee3331 100644 --- a/internal/twdesk/tags.go +++ b/internal/twdesk/tags.go @@ -34,7 +34,10 @@ func init() { func TagGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagGet), - mcp.WithDescription("Retrieve detailed information about a specific tag in Teamwork Desk by its ID. Useful for auditing tag usage, troubleshooting ticket categorization, or integrating Desk tag data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific tag in Teamwork Desk by its ID. "+ + "Useful for auditing tag usage, troubleshooting ticket categorization, or "+ + "integrating Desk tag data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +58,10 @@ func TagGet(client *deskclient.Client) server.ServerTool { // TagList returns a list of tags that apply to the filters in Teamwork Desk func TagList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all tags in Teamwork Desk, with optional filters for name, color, and inbox association. Enables users to audit, analyze, or synchronize tag configurations for ticket management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all tags in Teamwork Desk, with optional filters for name, color, and inbox association. " + + "Enables users to audit, analyze, or synchronize tag configurations for ticket management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("name", mcp.Description("The name of the tag to filter by.")), mcp.WithString("color", mcp.Description("The color of the tag to filter by.")), @@ -100,7 +106,9 @@ func TagList(client *deskclient.Client) server.ServerTool { func TagCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagCreate), - mcp.WithDescription("Create a new tag in Teamwork Desk by specifying its name and color. Useful for customizing ticket workflows, introducing new categories, or adapting Desk to evolving support processes."), + mcp.WithDescription( + "Create a new tag in Teamwork Desk by specifying its name and color. Useful for customizing "+ + "ticket workflows, introducing new categories, or adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the tag."), @@ -129,7 +137,10 @@ func TagCreate(client *deskclient.Client) server.ServerTool { func TagUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTagUpdate), - mcp.WithDescription("Update an existing tag in Teamwork Desk by ID, allowing changes to its name and color. Supports evolving support policies, rebranding, or correcting tag attributes for improved ticket handling."), + mcp.WithDescription( + "Update an existing tag in Teamwork Desk by ID, allowing changes to its name and color. "+ + "Supports evolving support policies, rebranding, or correcting tag attributes for improved "+ + "ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the tag to update."), diff --git a/internal/twdesk/tickets.go b/internal/twdesk/tickets.go index 05023a3..98fc427 100644 --- a/internal/twdesk/tickets.go +++ b/internal/twdesk/tickets.go @@ -36,7 +36,10 @@ func init() { func TicketGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketGet), - mcp.WithDescription("Retrieve detailed information about a specific ticket in Teamwork Desk by its ID. Useful for auditing ticket records, troubleshooting support workflows, or integrating Desk ticket data into automation and reporting systems."), + mcp.WithDescription( + "Retrieve detailed information about a specific ticket in Teamwork Desk by its ID. "+ + "Useful for auditing ticket records, troubleshooting support workflows, or "+ + "integrating Desk ticket data into automation and reporting systems."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -64,7 +67,10 @@ func TicketGet(client *deskclient.Client) server.ServerTool { // TicketList returns a list of tickets that apply to the filters in Teamwork Desk func TicketList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all tickets in Teamwork Desk, with extensive filters for inbox, customer, company, tag, status, priority, SLA, user, and more. Enables users to audit, analyze, or synchronize ticket data for support management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all tickets in Teamwork Desk, with extensive filters for inbox, customer, company, tag, status, " + + "priority, SLA, user, and more. Enables users to audit, analyze, or synchronize ticket data for support " + + "management, reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("inboxIDs", mcp.Description("The IDs of the inboxes to filter by.")), mcp.WithArray("customerIDs", mcp.Description("The IDs of the customers to filter by.")), @@ -167,7 +173,9 @@ func TicketList(client *deskclient.Client) server.ServerTool { func TicketCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketCreate), - mcp.WithDescription("Create a new ticket in Teamwork Desk by specifying subject, description, priority, and status. Useful for automating ticket creation, integrating external systems, or customizing support workflows."), + mcp.WithDescription( + "Create a new ticket in Teamwork Desk by specifying subject, description, priority, and status. "+ + "Useful for automating ticket creation, integrating external systems, or customizing support workflows."), mcp.WithString("subject", mcp.Required(), mcp.Description("The subject of the ticket.")), mcp.WithString("description", mcp.Description("The description of the ticket.")), mcp.WithString("priority", mcp.Description("The priority of the ticket.")), @@ -192,7 +200,10 @@ func TicketCreate(client *deskclient.Client) server.ServerTool { func TicketUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTicketUpdate), - mcp.WithDescription("Update an existing ticket in Teamwork Desk by ID, allowing changes to its attributes. Supports evolving support processes, correcting ticket records, or integrating with automation systems for improved ticket handling."), + mcp.WithDescription( + "Update an existing ticket in Teamwork Desk by ID, allowing changes to its attributes. "+ + "Supports evolving support processes, correcting ticket records, or integrating with automation "+ + "systems for improved ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the ticket to update."), diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index 34d43c1..db2afb9 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -34,7 +34,10 @@ func init() { func TypeGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeGet), - mcp.WithDescription("Retrieve detailed information about a specific ticket type in Teamwork Desk by its ID. Useful for auditing type usage, troubleshooting ticket categorization, or integrating Desk type data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific ticket type in Teamwork Desk by its ID. "+ + "Useful for auditing type usage, troubleshooting ticket categorization, or "+ + "integrating Desk type data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -55,7 +58,10 @@ func TypeGet(client *deskclient.Client) server.ServerTool { // TypeList returns a list of types that apply to the filters in Teamwork Desk func TypeList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all ticket types in Teamwork Desk, with optional filters for name and inbox association. Enables users to audit, analyze, or synchronize type configurations for ticket management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all ticket types in Teamwork Desk, with optional filters for name and inbox association. " + + "Enables users to audit, analyze, or synchronize type configurations for ticket management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("name", mcp.Description("The name of the type to filter by.")), mcp.WithArray("inboxIDs", mcp.Description("The inbox IDs of the type to filter by.")), @@ -94,7 +100,10 @@ func TypeList(client *deskclient.Client) server.ServerTool { func TypeCreate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeCreate), - mcp.WithDescription("Create a new ticket type in Teamwork Desk by specifying its name, display order, and future inbox settings. Useful for customizing ticket workflows, introducing new categories, or adapting Desk to evolving support processes."), + mcp.WithDescription( + "Create a new ticket type in Teamwork Desk by specifying its name, display order, and future inbox settings. "+ + "Useful for customizing ticket workflows, introducing new categories, or "+ + "adapting Desk to evolving support processes."), mcp.WithString("name", mcp.Required(), mcp.Description("The name of the type."), @@ -125,7 +134,10 @@ func TypeCreate(client *deskclient.Client) server.ServerTool { func TypeUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeUpdate), - mcp.WithDescription("Update an existing ticket type in Teamwork Desk by ID, allowing changes to its name, display order, and future inbox settings. Supports evolving support policies, rebranding, or correcting type attributes for improved ticket handling."), + mcp.WithDescription( + "Update an existing ticket type in Teamwork Desk by ID, allowing changes to its name, display order, and future inbox settings. "+ + "Supports evolving support policies, rebranding, or correcting type attributes for improved "+ + "ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the type to update."), diff --git a/internal/twdesk/users.go b/internal/twdesk/users.go index 2083755..f400261 100644 --- a/internal/twdesk/users.go +++ b/internal/twdesk/users.go @@ -29,7 +29,10 @@ func init() { func UserGet(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodUserGet), - mcp.WithDescription("Retrieve detailed information about a specific user in Teamwork Desk by their ID. Useful for auditing user records, troubleshooting ticket assignments, or integrating Desk user data into automation workflows."), + mcp.WithDescription( + "Retrieve detailed information about a specific user in Teamwork Desk by their ID. "+ + "Useful for auditing user records, troubleshooting ticket assignments, or "+ + "integrating Desk user data into automation workflows."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithString("id", mcp.Required(), @@ -50,7 +53,10 @@ func UserGet(client *deskclient.Client) server.ServerTool { // UserList returns a list of users that apply to the filters in Teamwork Desk func UserList(client *deskclient.Client) server.ServerTool { opts := []mcp.ToolOption{ - mcp.WithDescription("List all users in Teamwork Desk, with optional filters for name, email, inbox, and part-time status. Enables users to audit, analyze, or synchronize user configurations for support management, reporting, or integration scenarios."), + mcp.WithDescription( + "List all users in Teamwork Desk, with optional filters for name, email, inbox, and part-time status. " + + "Enables users to audit, analyze, or synchronize user configurations for support management, " + + "reporting, or integration scenarios."), mcp.WithReadOnlyHintAnnotation(true), mcp.WithArray("firstName", mcp.Description("The first names of the users to filter by.")), mcp.WithArray("lastName", mcp.Description("The last names of the users to filter by.")), From 58ce5bffe97be974016756022363f99202d46de6 Mon Sep 17 00:00:00 2001 From: Brandon Hansen Date: Fri, 19 Sep 2025 10:07:37 -0700 Subject: [PATCH 21/21] fix formatting --- internal/twdesk/customers.go | 5 +++-- internal/twdesk/types.go | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/twdesk/customers.go b/internal/twdesk/customers.go index 7de12b5..91b889d 100644 --- a/internal/twdesk/customers.go +++ b/internal/twdesk/customers.go @@ -201,8 +201,9 @@ func CustomerUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodCustomerUpdate), mcp.WithDescription( - "Update an existing customer in Teamwork Desk by ID, allowing changes to their name, contact details, and other attributes. "+ - "Supports evolving business relationships, correcting customer records, or improving ticket handling."), + "Update an existing customer in Teamwork Desk by ID, allowing changes to their name, "+ + "contact details, and other attributes. Supports evolving business relationships, "+ + "correcting customer records, or improving ticket handling."), mcp.WithString("id", mcp.Required(), mcp.Description("The ID of the customer to update."), diff --git a/internal/twdesk/types.go b/internal/twdesk/types.go index db2afb9..e4b0a18 100644 --- a/internal/twdesk/types.go +++ b/internal/twdesk/types.go @@ -135,8 +135,9 @@ func TypeUpdate(client *deskclient.Client) server.ServerTool { return server.ServerTool{ Tool: mcp.NewTool(string(MethodTypeUpdate), mcp.WithDescription( - "Update an existing ticket type in Teamwork Desk by ID, allowing changes to its name, display order, and future inbox settings. "+ - "Supports evolving support policies, rebranding, or correcting type attributes for improved "+ + "Update an existing ticket type in Teamwork Desk by ID, allowing changes to its name, display order, "+ + "and future inbox settings. Supports evolving support policies, rebranding, or correcting "+ + "type attributes for improved "+ "ticket handling."), mcp.WithString("id", mcp.Required(),