@@ -35,10 +35,26 @@ func RegisterCoderListWorkspaces(ta ToolAdder, client *codersdk.Client) {
35
35
ta .AddTool (toolCoderListWorkspaces , handleCoderListWorkspaces (client ))
36
36
}
37
37
38
+ func RegisterCoderGetWorkspace (ta ToolAdder , client * codersdk.Client ) {
39
+ ta .AddTool (toolCoderGetWorkspace , handleCoderGetWorkspace (client ))
40
+ }
41
+
38
42
func RegisterCoderWorkspaceExec (ta ToolAdder , client * codersdk.Client ) {
39
43
ta .AddTool (toolCoderWorkspaceExec , handleCoderWorkspaceExec (client ))
40
44
}
41
45
46
+ func RegisterCoderListTemplates (ta ToolAdder , client * codersdk.Client ) {
47
+ ta .AddTool (toolCoderListTemplates , handleCoderListTemplates (client ))
48
+ }
49
+
50
+ func RegisterCoderStartWorkspace (ta ToolAdder , client * codersdk.Client ) {
51
+ ta .AddTool (toolCoderStartWorkspace , handleCoderStartWorkspace (client ))
52
+ }
53
+
54
+ func RegisterCoderStopWorkspace (ta ToolAdder , client * codersdk.Client ) {
55
+ ta .AddTool (toolCoderStopWorkspace , handleCoderStopWorkspace (client ))
56
+ }
57
+
42
58
var (
43
59
toolCoderReportTask = mcp .NewTool ("coder_report_task" ,
44
60
mcp .WithDescription (`Report progress on a task.` ),
@@ -61,16 +77,31 @@ Good Summaries:
61
77
mcp .WithNumber (`offset` , mcp .Description (`The offset to start listing workspaces from. Defaults to 0.` ), mcp .DefaultNumber (0 )),
62
78
mcp .WithNumber (`limit` , mcp .Description (`The maximum number of workspaces to list. Defaults to 10.` ), mcp .DefaultNumber (10 )),
63
79
)
80
+ toolCoderGetWorkspace = mcp .NewTool ("coder_get_workspace" ,
81
+ mcp .WithDescription (`Get information about a workspace on a given Coder deployment.` ),
82
+ mcp .WithString ("workspace" , mcp .Description (`The workspace ID or name to get.` ), mcp .Required ()),
83
+ )
64
84
toolCoderWorkspaceExec = mcp .NewTool ("coder_workspace_exec" ,
65
85
mcp .WithDescription (`Execute a command in a remote workspace on a given Coder deployment.` ),
66
86
// Required parameters.
67
87
mcp .WithString ("workspace" , mcp .Description (`The workspace ID or name in which to execute the command in. The workspace must be running.` ), mcp .Required ()),
68
88
mcp .WithString ("command" , mcp .Description (`The command to execute. Changing the working directory is not currently supported, so you may need to preface the command with 'cd /some/path && <my-command>'.` ), mcp .Required ()),
69
89
)
90
+ toolCoderListTemplates = mcp .NewTool ("coder_list_templates" ,
91
+ mcp .WithDescription (`List all templates on a given Coder deployment.` ),
92
+ )
93
+ toolCoderStartWorkspace = mcp .NewTool ("coder_start_workspace" ,
94
+ mcp .WithDescription (`Start a workspace on a given Coder deployment.` ),
95
+ mcp .WithString ("workspace" , mcp .Description (`The workspace ID or name to start.` ), mcp .Required ()),
96
+ )
97
+ toolCoderStopWorkspace = mcp .NewTool ("coder_stop_workspace" ,
98
+ mcp .WithDescription (`Stop a workspace on a given Coder deployment.` ),
99
+ mcp .WithString ("workspace" , mcp .Description (`The workspace ID or name to stop.` ), mcp .Required ()),
100
+ )
70
101
)
71
102
72
103
// Example payload:
73
- // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_report_task", "arguments": {"summary": "I'm working on the login page.", "link": "https://github.com/coder/coder/pull/1234", "emoji": "🔍", "done": false, "coder_url": "http://localhost:3000", "coder_session_token": "REDACTED" }}}
104
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_report_task", "arguments": {"summary": "I'm working on the login page.", "link": "https://github.com/coder/coder/pull/1234", "emoji": "🔍", "done": false}}}
74
105
func handleCoderReportTask (log slog.Logger , client * codersdk.Client ) mcpserver.ToolHandlerFunc {
75
106
return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
76
107
if client == nil {
@@ -123,7 +154,7 @@ func handleCoderReportTask(log slog.Logger, client *codersdk.Client) mcpserver.T
123
154
}
124
155
125
156
// Example payload:
126
- // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_whoami", "arguments": {"coder_url": "http://localhost:3000", "coder_session_token": "REDACTED" }}}
157
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_whoami", "arguments": {}}}
127
158
func handleCoderWhoami (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
128
159
return func (ctx context.Context , _ mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
129
160
if client == nil {
@@ -148,7 +179,7 @@ func handleCoderWhoami(client *codersdk.Client) mcpserver.ToolHandlerFunc {
148
179
}
149
180
150
181
// Example payload:
151
- // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_list_workspaces", "arguments": {"owner": "me", "offset": 0, "limit": 10, "coder_url": "http://localhost:3000", "coder_session_token": "REDACTED" }}}
182
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_list_workspaces", "arguments": {"owner": "me", "offset": 0, "limit": 10}}}
152
183
func handleCoderListWorkspaces (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
153
184
return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
154
185
if client == nil {
@@ -194,7 +225,39 @@ func handleCoderListWorkspaces(client *codersdk.Client) mcpserver.ToolHandlerFun
194
225
}
195
226
196
227
// Example payload:
197
- // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_workspace_exec", "arguments": {"workspace": "dev", "command": "ps -ef", "coder_url": "http://localhost:3000", "coder_session_token": "REDACTED"}}}
228
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_get_workspace", "arguments": {"workspace": "dev"}}}
229
+ func handleCoderGetWorkspace (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
230
+ return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
231
+ if client == nil {
232
+ return nil , xerrors .New ("developer error: client is required" )
233
+ }
234
+ args := request .Params .Arguments
235
+
236
+ wsArg , ok := args ["workspace" ].(string )
237
+ if ! ok {
238
+ return nil , xerrors .New ("workspace is required" )
239
+ }
240
+
241
+ workspace , err := getWorkspaceByIDOrOwnerName (ctx , client , wsArg )
242
+ if err != nil {
243
+ return nil , xerrors .Errorf ("failed to fetch workspace: %w" , err )
244
+ }
245
+
246
+ workspaceJSON , err := json .Marshal (workspace )
247
+ if err != nil {
248
+ return nil , xerrors .Errorf ("failed to encode workspace: %w" , err )
249
+ }
250
+
251
+ return & mcp.CallToolResult {
252
+ Content : []mcp.Content {
253
+ mcp .NewTextContent (string (workspaceJSON )),
254
+ },
255
+ }, nil
256
+ }
257
+ }
258
+
259
+ // Example payload:
260
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_workspace_exec", "arguments": {"workspace": "dev", "command": "ps -ef"}}}
198
261
func handleCoderWorkspaceExec (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
199
262
return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
200
263
if client == nil {
@@ -265,6 +328,123 @@ func handleCoderWorkspaceExec(client *codersdk.Client) mcpserver.ToolHandlerFunc
265
328
}
266
329
}
267
330
331
+ // Example payload:
332
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_list_templates", "arguments": {}}}
333
+ func handleCoderListTemplates (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
334
+ return func (ctx context.Context , _ mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
335
+ if client == nil {
336
+ return nil , xerrors .New ("developer error: client is required" )
337
+ }
338
+ templates , err := client .Templates (ctx , codersdk.TemplateFilter {})
339
+ if err != nil {
340
+ return nil , xerrors .Errorf ("failed to fetch templates: %w" , err )
341
+ }
342
+
343
+ templateJSON , err := json .Marshal (templates )
344
+ if err != nil {
345
+ return nil , xerrors .Errorf ("failed to encode templates: %w" , err )
346
+ }
347
+
348
+ return & mcp.CallToolResult {
349
+ Content : []mcp.Content {
350
+ mcp .NewTextContent (string (templateJSON )),
351
+ },
352
+ }, nil
353
+ }
354
+ }
355
+
356
+ // Example payload:
357
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_start_workspace", "arguments": {"workspace": "dev"}}}
358
+ func handleCoderStartWorkspace (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
359
+ return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
360
+ if client == nil {
361
+ return nil , xerrors .New ("developer error: client is required" )
362
+ }
363
+
364
+ args := request .Params .Arguments
365
+
366
+ wsArg , ok := args ["workspace" ].(string )
367
+ if ! ok {
368
+ return nil , xerrors .New ("workspace is required" )
369
+ }
370
+
371
+ workspace , err := getWorkspaceByIDOrOwnerName (ctx , client , wsArg )
372
+ if err != nil {
373
+ return nil , xerrors .Errorf ("failed to fetch workspace: %w" , err )
374
+ }
375
+
376
+ switch workspace .LatestBuild .Status {
377
+ case codersdk .WorkspaceStatusPending , codersdk .WorkspaceStatusStarting , codersdk .WorkspaceStatusRunning , codersdk .WorkspaceStatusCanceling :
378
+ return nil , xerrors .Errorf ("workspace is %s" , workspace .LatestBuild .Status )
379
+ }
380
+
381
+ wb , err := client .CreateWorkspaceBuild (ctx , workspace .ID , codersdk.CreateWorkspaceBuildRequest {
382
+ Transition : codersdk .WorkspaceTransitionStart ,
383
+ })
384
+ if err != nil {
385
+ return nil , xerrors .Errorf ("failed to start workspace: %w" , err )
386
+ }
387
+
388
+ resp := map [string ]any {"status" : wb .Status , "transition" : wb .Transition }
389
+ respJSON , err := json .Marshal (resp )
390
+ if err != nil {
391
+ return nil , xerrors .Errorf ("failed to encode workspace build: %w" , err )
392
+ }
393
+
394
+ return & mcp.CallToolResult {
395
+ Content : []mcp.Content {
396
+ mcp .NewTextContent (string (respJSON )),
397
+ },
398
+ }, nil
399
+ }
400
+ }
401
+
402
+ // Example payload:
403
+ // {"jsonrpc":"2.0","id":1,"method":"tools/call", "params": {"name": "coder_stop_workspace", "arguments": {"workspace": "dev"}}}
404
+ func handleCoderStopWorkspace (client * codersdk.Client ) mcpserver.ToolHandlerFunc {
405
+ return func (ctx context.Context , request mcp.CallToolRequest ) (* mcp.CallToolResult , error ) {
406
+ if client == nil {
407
+ return nil , xerrors .New ("developer error: client is required" )
408
+ }
409
+
410
+ args := request .Params .Arguments
411
+
412
+ wsArg , ok := args ["workspace" ].(string )
413
+ if ! ok {
414
+ return nil , xerrors .New ("workspace is required" )
415
+ }
416
+
417
+ workspace , err := getWorkspaceByIDOrOwnerName (ctx , client , wsArg )
418
+ if err != nil {
419
+ return nil , xerrors .Errorf ("failed to fetch workspace: %w" , err )
420
+ }
421
+
422
+ switch workspace .LatestBuild .Status {
423
+ case codersdk .WorkspaceStatusPending , codersdk .WorkspaceStatusStopping , codersdk .WorkspaceStatusStopped , codersdk .WorkspaceStatusCanceling :
424
+ return nil , xerrors .Errorf ("workspace is %s" , workspace .LatestBuild .Status )
425
+ }
426
+
427
+ wb , err := client .CreateWorkspaceBuild (ctx , workspace .ID , codersdk.CreateWorkspaceBuildRequest {
428
+ Transition : codersdk .WorkspaceTransitionStop ,
429
+ })
430
+ if err != nil {
431
+ return nil , xerrors .Errorf ("failed to stop workspace: %w" , err )
432
+ }
433
+
434
+ resp := map [string ]any {"status" : wb .Status , "transition" : wb .Transition }
435
+ respJSON , err := json .Marshal (resp )
436
+ if err != nil {
437
+ return nil , xerrors .Errorf ("failed to encode workspace build: %w" , err )
438
+ }
439
+
440
+ return & mcp.CallToolResult {
441
+ Content : []mcp.Content {
442
+ mcp .NewTextContent (string (respJSON )),
443
+ },
444
+ }, nil
445
+ }
446
+ }
447
+
268
448
func getWorkspaceByIDOrOwnerName (ctx context.Context , client * codersdk.Client , identifier string ) (codersdk.Workspace , error ) {
269
449
if wsid , err := uuid .Parse (identifier ); err == nil {
270
450
return client .Workspace (ctx , wsid )
0 commit comments