Skip to content

Commit 70b3786

Browse files
committed
[setup] Introduce --auto-approve=[tool-list | "allow-read-only"]
Tool: gitpod/catfood.gitpod.cloud
1 parent a2a3034 commit 70b3786

File tree

3 files changed

+95
-43
lines changed

3 files changed

+95
-43
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ export LINEAR_API_KEY=your_linear_api_key
7878
# Set up with write access enabled
7979
./linear-mcp-go setup --write-access
8080

81+
# Set up with auto-approval for read-only tools
82+
./linear-mcp-go setup --auto-approve=allow-read-only
83+
84+
# Set up with specific tools auto-approved
85+
./linear-mcp-go setup --auto-approve=linear_get_issue,linear_search_issues
86+
87+
# Set up with write access and auto-approval for read-only tools
88+
./linear-mcp-go setup --write-access --auto-approve=allow-read-only
89+
8190
# Set up for a different tool (only "cline" supported for now)
8291
./linear-mcp-go setup --tool=cline
8392
```
@@ -86,6 +95,11 @@ This command:
8695
1. Checks if the Linear MCP binary is already installed
8796
2. Copies the current binary to the installation directory if needed
8897
3. Configures the AI assistant to use the Linear MCP server
98+
4. Sets up auto-approval for specified tools if requested
99+
100+
The `--auto-approve` flag can be used to specify which tools should be auto-approved in the Cline configuration:
101+
- `--auto-approve=allow-read-only`: Auto-approves all read-only tools (`linear_search_issues`, `linear_get_user_issues`, `linear_get_issue`, `linear_get_teams`)
102+
- `--auto-approve=tool1,tool2,...`: Auto-approves the specified comma-separated list of tools
89103

90104
Currently supported AI assistants:
91105
- Cline (VSCode extension)

cmd/setup.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"runtime"
1111
"strings"
1212

13+
"github.com/geropl/linear-mcp-go/pkg/server"
1314
"github.com/spf13/cobra"
1415
)
1516

@@ -23,6 +24,7 @@ Currently supported tools: cline`,
2324
Run: func(cmd *cobra.Command, args []string) {
2425
tool, _ := cmd.Flags().GetString("tool")
2526
writeAccess, _ := cmd.Flags().GetBool("write-access")
27+
autoApprove, _ := cmd.Flags().GetString("auto-approve")
2628

2729
// Check if the Linear API key is provided in the environment
2830
apiKey := os.Getenv("LINEAR_API_KEY")
@@ -60,7 +62,7 @@ Currently supported tools: cline`,
6062
// Set up the tool-specific configuration
6163
switch strings.ToLower(tool) {
6264
case "cline":
63-
if err := setupCline(binaryPath, apiKey, writeAccess); err != nil {
65+
if err := setupCline(binaryPath, apiKey, writeAccess, autoApprove); err != nil {
6466
fmt.Printf("Error setting up Cline: %v\n", err)
6567
os.Exit(1)
6668
}
@@ -80,6 +82,7 @@ func init() {
8082
// Add flags to the setup command
8183
setupCmd.Flags().String("tool", "cline", "The AI assistant tool to set up for (default: cline)")
8284
setupCmd.Flags().Bool("write-access", false, "Enable write operations (default: false)")
85+
setupCmd.Flags().String("auto-approve", "", "Comma-separated list of tool names to auto-approve, or 'allow-read-only' to auto-approve all read-only tools")
8386
}
8487

8588
// checkBinary checks if the Linear MCP binary is already on the path
@@ -154,7 +157,7 @@ func copySelfToBinaryPath(binaryPath string) error {
154157
}
155158

156159
// setupCline sets up the Linear MCP server for Cline
157-
func setupCline(binaryPath, apiKey string, writeAccess bool) error {
160+
func setupCline(binaryPath, apiKey string, writeAccess bool, autoApprove string) error {
158161
// Determine the Cline config directory
159162
homeDir, err := os.UserHomeDir()
160163
if err != nil {
@@ -183,6 +186,25 @@ func setupCline(binaryPath, apiKey string, writeAccess bool) error {
183186
serverArgs = append(serverArgs, "--write-access=true")
184187
}
185188

189+
// Process auto-approve flag
190+
autoApproveTools := []string{}
191+
if autoApprove != "" {
192+
if autoApprove == "allow-read-only" {
193+
// Get the list of read-only tools
194+
for k := range server.GetReadOnlyToolNames() {
195+
autoApproveTools = append(autoApproveTools, k)
196+
}
197+
} else {
198+
// Split comma-separated list
199+
for _, tool := range strings.Split(autoApprove, ",") {
200+
trimmedTool := strings.TrimSpace(tool)
201+
if trimmedTool != "" {
202+
autoApproveTools = append(autoApproveTools, trimmedTool)
203+
}
204+
}
205+
}
206+
}
207+
186208
// Create the MCP settings file
187209
settingsPath := filepath.Join(configDir, "cline_mcp_settings.json")
188210
newSettings := map[string]interface{}{
@@ -192,7 +214,7 @@ func setupCline(binaryPath, apiKey string, writeAccess bool) error {
192214
"args": serverArgs,
193215
"env": map[string]string{"LINEAR_API_KEY": apiKey},
194216
"disabled": false,
195-
"autoApprove": []string{},
217+
"autoApprove": autoApproveTools,
196218
},
197219
},
198220
}

pkg/server/tools.go

Lines changed: 56 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -478,9 +478,28 @@ func GetTeamsHandler(linearClient *linear.LinearClient) func(ctx context.Context
478478
}
479479
}
480480

481+
// GetReadOnlyToolNames returns the names of all read-only tools
482+
func GetReadOnlyToolNames() map[string]bool {
483+
return map[string]bool{
484+
"linear_search_issues": true,
485+
"linear_get_user_issues": true,
486+
"linear_get_issue": true,
487+
"linear_get_teams": true,
488+
}
489+
}
490+
481491
// RegisterTools registers all Linear tools with the MCP server
482492
func RegisterTools(s *server.MCPServer, linearClient *linear.LinearClient, writeAccess bool) {
483-
// Register read-only tools (always available)
493+
// Register tools, based on writeAccess
494+
addTool := func(tool mcp.Tool, handler server.ToolHandlerFunc) {
495+
if !writeAccess {
496+
if readOnly := GetReadOnlyToolNames()[tool.Name]; !readOnly {
497+
// Skip registering write tools if write access is disabled
498+
return
499+
}
500+
}
501+
s.AddTool(tool, handler)
502+
}
484503

485504
// Search Issues Tool (read-only)
486505
searchIssuesTool := mcp.NewTool("linear_search_issues",
@@ -495,7 +514,7 @@ func RegisterTools(s *server.MCPServer, linearClient *linear.LinearClient, write
495514
mcp.WithBoolean("includeArchived", mcp.Description("Include archived issues in results (default: false)")),
496515
mcp.WithNumber("limit", mcp.Description("Max results to return (default: 10)")),
497516
)
498-
s.AddTool(searchIssuesTool, SearchIssuesHandler(linearClient))
517+
addTool(searchIssuesTool, SearchIssuesHandler(linearClient))
499518

500519
// Get User Issues Tool (read-only)
501520
getUserIssuesTool := mcp.NewTool("linear_get_user_issues",
@@ -504,54 +523,51 @@ func RegisterTools(s *server.MCPServer, linearClient *linear.LinearClient, write
504523
mcp.WithBoolean("includeArchived", mcp.Description("Include archived issues in results")),
505524
mcp.WithNumber("limit", mcp.Description("Maximum number of issues to return (default: 50)")),
506525
)
507-
s.AddTool(getUserIssuesTool, GetUserIssuesHandler(linearClient))
526+
addTool(getUserIssuesTool, GetUserIssuesHandler(linearClient))
508527

509528
// Get Issue Tool (read-only)
510529
getIssueTool := mcp.NewTool("linear_get_issue",
511530
mcp.WithDescription("Retrieves a single Linear issue by its ID. Returns detailed information about the issue including title, description, priority, status, assignee, team, full comment history (including nested comments), related issues, and all attachments (pull requests, design files, documents, etc.)."),
512531
mcp.WithString("issueId", mcp.Required(), mcp.Description("ID of the issue to retrieve")),
513532
)
514-
s.AddTool(getIssueTool, GetIssueHandler(linearClient))
533+
addTool(getIssueTool, GetIssueHandler(linearClient))
515534

516535
// Get Teams Tool (read-only)
517536
getTeamsTool := mcp.NewTool("linear_get_teams",
518537
mcp.WithDescription("Retrieves Linear teams with an optional name filter. If no name is provided, returns all teams. Returns team details including ID, name, and key."),
519538
mcp.WithString("name", mcp.Description("Optional team name filter. Returns teams whose names contain this string.")),
520539
)
521-
s.AddTool(getTeamsTool, GetTeamsHandler(linearClient))
522-
523-
// Register write tools (only if writeAccess is true)
524-
if writeAccess {
525-
// Create Issue Tool
526-
createIssueTool := mcp.NewTool("linear_create_issue",
527-
mcp.WithDescription("Creates a new Linear issue with specified details. Use this to create tickets for tasks, bugs, or feature requests. Returns the created issue's identifier and URL. Required fields are title and teamId, with optional description, priority (0-4, where 0 is no priority and 1 is urgent), and status."),
528-
mcp.WithString("title", mcp.Required(), mcp.Description("Issue title")),
529-
mcp.WithString("teamId", mcp.Required(), mcp.Description("Team ID")),
530-
mcp.WithString("description", mcp.Description("Issue description")),
531-
mcp.WithNumber("priority", mcp.Description("Priority (0-4)")),
532-
mcp.WithString("status", mcp.Description("Issue status")),
533-
)
534-
s.AddTool(createIssueTool, CreateIssueHandler(linearClient))
535-
536-
// Update Issue Tool
537-
updateIssueTool := mcp.NewTool("linear_update_issue",
538-
mcp.WithDescription("Updates an existing Linear issue's properties. Use this to modify issue details like title, description, priority, or status. Requires the issue ID and accepts any combination of updatable fields. Returns the updated issue's identifier and URL."),
539-
mcp.WithString("id", mcp.Required(), mcp.Description("Issue ID")),
540-
mcp.WithString("title", mcp.Description("New title")),
541-
mcp.WithString("description", mcp.Description("New description")),
542-
mcp.WithNumber("priority", mcp.Description("New priority (0-4)")),
543-
mcp.WithString("status", mcp.Description("New status")),
544-
)
545-
s.AddTool(updateIssueTool, UpdateIssueHandler(linearClient))
546-
547-
// Add Comment Tool
548-
addCommentTool := mcp.NewTool("linear_add_comment",
549-
mcp.WithDescription("Adds a comment to an existing Linear issue. Supports markdown formatting in the comment body. Can optionally specify a custom user name and avatar for the comment. Returns the created comment's details including its URL."),
550-
mcp.WithString("issueId", mcp.Required(), mcp.Description("ID of the issue to comment on")),
551-
mcp.WithString("body", mcp.Required(), mcp.Description("Comment text in markdown format")),
552-
mcp.WithString("createAsUser", mcp.Description("Optional custom username to show for the comment")),
553-
mcp.WithString("displayIconUrl", mcp.Description("Optional avatar URL for the comment")),
554-
)
555-
s.AddTool(addCommentTool, AddCommentHandler(linearClient))
556-
}
540+
addTool(getTeamsTool, GetTeamsHandler(linearClient))
541+
542+
// Create Issue Tool
543+
createIssueTool := mcp.NewTool("linear_create_issue",
544+
mcp.WithDescription("Creates a new Linear issue with specified details. Use this to create tickets for tasks, bugs, or feature requests. Returns the created issue's identifier and URL. Required fields are title and teamId, with optional description, priority (0-4, where 0 is no priority and 1 is urgent), and status."),
545+
mcp.WithString("title", mcp.Required(), mcp.Description("Issue title")),
546+
mcp.WithString("teamId", mcp.Required(), mcp.Description("Team ID")),
547+
mcp.WithString("description", mcp.Description("Issue description")),
548+
mcp.WithNumber("priority", mcp.Description("Priority (0-4)")),
549+
mcp.WithString("status", mcp.Description("Issue status")),
550+
)
551+
addTool(createIssueTool, CreateIssueHandler(linearClient))
552+
553+
// Update Issue Tool
554+
updateIssueTool := mcp.NewTool("linear_update_issue",
555+
mcp.WithDescription("Updates an existing Linear issue's properties. Use this to modify issue details like title, description, priority, or status. Requires the issue ID and accepts any combination of updatable fields. Returns the updated issue's identifier and URL."),
556+
mcp.WithString("id", mcp.Required(), mcp.Description("Issue ID")),
557+
mcp.WithString("title", mcp.Description("New title")),
558+
mcp.WithString("description", mcp.Description("New description")),
559+
mcp.WithNumber("priority", mcp.Description("New priority (0-4)")),
560+
mcp.WithString("status", mcp.Description("New status")),
561+
)
562+
addTool(updateIssueTool, UpdateIssueHandler(linearClient))
563+
564+
// Add Comment Tool
565+
addCommentTool := mcp.NewTool("linear_add_comment",
566+
mcp.WithDescription("Adds a comment to an existing Linear issue. Supports markdown formatting in the comment body. Can optionally specify a custom user name and avatar for the comment. Returns the created comment's details including its URL."),
567+
mcp.WithString("issueId", mcp.Required(), mcp.Description("ID of the issue to comment on")),
568+
mcp.WithString("body", mcp.Required(), mcp.Description("Comment text in markdown format")),
569+
mcp.WithString("createAsUser", mcp.Description("Optional custom username to show for the comment")),
570+
mcp.WithString("displayIconUrl", mcp.Description("Optional avatar URL for the comment")),
571+
)
572+
addTool(addCommentTool, AddCommentHandler(linearClient))
557573
}

0 commit comments

Comments
 (0)