claude_swarm 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dc1ac6dbcc7eac54e907f574c827580275e147e42e92122d467809addb614ae9
4
- data.tar.gz: d579b2a49bc0323c4704736597c1d3a9bf8e24a29c0bfe63bd21969560b4dbf9
3
+ metadata.gz: b71b3ce3aebae7de091dd130cda9fc4c474f538f444be15f1ee5b07501210db6
4
+ data.tar.gz: 846ac3f979b5912ccbc70f1810b760f625e75b1c7e905d5d1199b893d0bc3400
5
5
  SHA512:
6
- metadata.gz: 20398e7167acc7f88c611da67a0d613fbfba9e225701c5dd1681f2a2ffac12bd81bbf9e4463c45564df0046edd17f3a299edbfedd1c79b0a463127c53853283f
7
- data.tar.gz: be84c91452a5cc0e00ec725e9e4f3a75b8e4b94f72438a796a3a6e8f787c4c8a2c46ad857bb661c50971c2fda25ca51fe2e25fc650c3025001292dba054f293b
6
+ metadata.gz: 9f891010506baba80420357aad85a7fde6704c2ed99155e9cf32fd38cc35efd007ac19fe0e8a1df59b5f1df2d24980f7f0150551629667b42254d02bf77c4ddc
7
+ data.tar.gz: 29f913521f66298ee24dbdff1b8a043d3a00abe0723080b953cb3dbc127ac6e5ecb35a8b857f61af062db9d570f3c42a90e874dcb114b42f6bee163162f96a0a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## [0.1.16]
2
+
3
+ ### Changed
4
+ - **Breaking change**: Removed custom permission MCP server in favor of Claude's native `mcp__MCP_NAME` pattern
5
+ - Connected instances are now automatically added to allowed tools as `mcp__<instance_name>`
6
+ - CLI parameter `--tools` renamed to `--allowed-tools` for consistency with YAML configuration
7
+ - MCP generator no longer creates permission MCP server configurations
8
+
9
+ ### Removed
10
+ - Removed `PermissionMcpServer` and `PermissionTool` classes
11
+ - Removed `tools-mcp` CLI command
12
+ - Removed regex tool pattern syntax - use Claude Code patterns instead
13
+ - Removed `--permission-prompt-tool` flag from orchestrator
14
+ - Removed permission logging to `permissions.log`
15
+
16
+ ### Migration Guide
17
+ - Replace custom tool patterns with Claude Code's native patterns in your YAML files:
18
+ - `"Bash(npm:*)"` → Use `Bash` and Claude Code's built-in command restrictions
19
+ - `"Edit(*.js)"` → Use `Edit` and Claude Code's built-in file restrictions
20
+ - For fine-grained tool control, use Claude Code's native patterns:
21
+ - `mcp__<server_name>__<tool_name>` for specific tools from an MCP server
22
+ - `mcp__<server_name>` to allow all tools from an MCP server
23
+ - Connected instances are automatically accessible via `mcp__<instance_name>` pattern
24
+ - See Claude Code's documentation for full details on supported tool patterns
25
+
1
26
  ## [0.1.15]
2
27
 
3
28
  ### Changed
data/CLAUDE.md CHANGED
@@ -54,7 +54,7 @@ The gem is fully implemented with the following components:
54
54
 
55
55
  1. **YAML Configuration**: Define swarms with instances, connections, tools, and MCP servers
56
56
  2. **Inter-Instance Communication**: Instances connect via MCP using `claude mcp serve` with `-p` flag
57
- 3. **Tool Restrictions**: Support for pattern-based tool restrictions (e.g., `Bash(npm:*)`)
57
+ 3. **Tool Restrictions**: Support for tool restrictions using Claude's native pattern (connections are available as `mcp__instance_name`)
58
58
  4. **Multiple MCP Types**: Supports both stdio and SSE MCP server types
59
59
  5. **Automatic MCP Generation**: Creates `.claude-swarm/` directory with MCP configs
60
60
  6. **Custom System Prompts**: Each instance can have a custom prompt via `--append-system-prompt`
@@ -63,9 +63,10 @@ The gem is fully implemented with the following components:
63
63
 
64
64
  1. User creates a `claude-swarm.yml` file defining the swarm topology
65
65
  2. Running `claude-swarm` parses the configuration and validates it
66
- 3. MCP configuration files are generated for each instance in `.claude-swarm/`
66
+ 3. MCP configuration files are generated for each instance in a session directory
67
67
  4. The main instance is launched with `exec`, replacing the current process
68
68
  5. Connected instances are available as MCP servers to the main instance
69
+ 6. When an instance has connections, those connections are automatically added to its allowed tools as `mcp__<connection_name>`
69
70
 
70
71
  ### Configuration Example
71
72
 
@@ -85,15 +86,18 @@ swarm:
85
86
  directory: ./frontend
86
87
  model: sonnet
87
88
  prompt: "You specialize in frontend development with React"
88
- tools: [Edit, Write, "Bash(npm:*)"]
89
+ tools: [Edit, Write, Bash]
89
90
  ```
90
91
 
91
92
  ## Testing
92
93
 
93
- The gem includes basic tests for version number and CLI existence. Additional tests should be added for:
94
+ The gem includes comprehensive tests covering:
94
95
  - Configuration parsing and validation
95
- - MCP generation logic
96
+ - MCP generation logic with connections
96
97
  - Error handling scenarios
98
+ - CLI command functionality
99
+ - Session restoration
100
+ - Vibe mode behavior
97
101
 
98
102
  ## Dependencies
99
103
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Claude Swarm
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=0.1.14)](https://badge.fury.io/rb/claude_swarm)
3
+ [![Gem Version](https://badge.fury.io/rb/claude_swarm.svg?cache_bust=0.1.15)](https://badge.fury.io/rb/claude_swarm)
4
4
  [![CI](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml/badge.svg)](https://github.com/parruda/claude-swarm/actions/workflows/ci.yml)
5
5
 
6
6
  Claude Swarm orchestrates multiple Claude Code instances as a collaborative AI development team. It enables running AI agents with specialized roles, tools, and directory contexts, communicating via MCP (Model Context Protocol) in a tree-like hierarchy. Define your swarm topology in simple YAML and let Claude instances delegate tasks through connected instances. Perfect for complex projects requiring specialized AI agents for frontend, backend, testing, DevOps, or research tasks.
@@ -113,7 +113,7 @@ swarm:
113
113
  directory: ./web-frontend/src
114
114
  model: opus
115
115
  prompt: "You specialize in React components and state management"
116
- allowed_tools: [Edit, Write, "Bash(npm:*)"]
116
+ allowed_tools: [Edit, Write, Bash]
117
117
 
118
118
  css_expert:
119
119
  description: "CSS specialist handling styling and responsive design"
@@ -142,7 +142,7 @@ swarm:
142
142
  directory: ./api-server/db
143
143
  model: opus
144
144
  prompt: "You handle database schema and migrations"
145
- allowed_tools: [Edit, Write, "Bash(psql:*, migrate:*)"]
145
+ allowed_tools: [Edit, Write, Bash]
146
146
 
147
147
  mobile_lead:
148
148
  description: "Mobile team lead coordinating cross-platform development"
@@ -157,21 +157,21 @@ swarm:
157
157
  directory: ./mobile-app/ios
158
158
  model: opus
159
159
  prompt: "You develop the iOS application"
160
- allowed_tools: [Edit, Write, "Bash(xcodebuild:*, pod:*)"]
160
+ allowed_tools: [Edit, Write, Bash]
161
161
 
162
162
  android_dev:
163
163
  description: "Android developer creating native Android apps"
164
164
  directory: ./mobile-app/android
165
165
  model: opus
166
166
  prompt: "You develop the Android application"
167
- allowed_tools: [Edit, Write, "Bash(gradle:*, adb:*)"]
167
+ allowed_tools: [Edit, Write, Bash]
168
168
 
169
169
  devops:
170
170
  description: "DevOps engineer managing CI/CD and infrastructure"
171
171
  directory: ./infrastructure
172
172
  model: opus
173
173
  prompt: "You handle CI/CD and infrastructure"
174
- allowed_tools: [Read, Edit, "Bash(docker:*, kubectl:*)"]
174
+ allowed_tools: [Read, Edit, Bash]
175
175
  ```
176
176
 
177
177
  In this setup:
@@ -179,6 +179,7 @@ In this setup:
179
179
  - Each team lead can work with their specialized developers
180
180
  - Each instance is independent - connections create separate MCP server instances
181
181
  - Teams work in isolated directories with role-appropriate tools
182
+ - Connected instances are accessible via MCP tools like `mcp__frontend_lead__task`, `mcp__backend_lead__task`, etc.
182
183
 
183
184
 
184
185
  ### Configuration Format
@@ -272,23 +273,22 @@ allowed_tools:
272
273
  - WebFetch # Fetch web content
273
274
  - WebSearch # Search the web
274
275
 
275
- disallowed_tools: # Optional: explicitly deny specific tools
276
- - "Write(*.md)" # Don't allow writing markdown files
277
- - "Bash(rm:*)" # Don't allow rm commands
276
+ # Note: Pattern-based tool restrictions have been deprecated.
277
+ # Use allowed_tools and disallowed_tools with tool names only.
278
278
  ```
279
279
 
280
280
  Tools are passed to Claude using the `--allowedTools` and `--disallowedTools` flags with comma-separated values. Disallowed tools take precedence over allowed tools.
281
281
 
282
- #### Tool Restrictions
283
-
284
- You can restrict tools with pattern-based filters:
282
+ #### Available Tools
285
283
 
286
284
  ```yaml
287
285
  allowed_tools:
288
- - Read # Unrestricted read access
289
- - Edit # Unrestricted edit access
290
- - "Bash(npm:*)" # Only allow npm commands
291
- - "Bash(git:*, make:*)" # Only allow git and make commands
286
+ - Read # File reading
287
+ - Edit # File editing
288
+ - Write # File creation
289
+ - Bash # Command execution
290
+ - WebFetch # Fetch web content
291
+ - WebSearch # Search the web
292
292
  ```
293
293
 
294
294
  ### Examples
@@ -419,7 +419,7 @@ swarm:
419
419
  description: "Worker with restricted permissions"
420
420
  directory: ./sensitive
421
421
  model: sonnet
422
- allowed_tools: [Read, "Bash(ls:*)"] # Only allow read and ls commands
422
+ allowed_tools: [Read, Bash] # Allow read and bash commands
423
423
 
424
424
  trusted_worker:
425
425
  description: "Trusted worker with more permissions"
@@ -457,9 +457,8 @@ claude-swarm list-sessions --limit 20
457
457
  # Show version
458
458
  claude-swarm version
459
459
 
460
- # Start permission MCP server (for testing/debugging)
461
- claude-swarm tools-mcp --allowed-tools 'mcp__frontend__*,mcp__backend__*'
462
- claude-swarm tools-mcp --allowed-tools 'Read,Edit' --disallowed-tools 'Edit(*.log)'
460
+ # Note: The permission MCP server has been deprecated.
461
+ # Tool permissions are now handled through allowed_tools and disallowed_tools in your configuration.
463
462
 
464
463
  # Internal command for MCP server (used by connected instances)
465
464
  claude-swarm mcp-serve INSTANCE_NAME --config CONFIG_FILE --session-timestamp TIMESTAMP
@@ -477,7 +476,7 @@ All session files are organized in `~/.claude-swarm/sessions/{project}/{timestam
477
476
  - `{instance_name}.mcp.json`: MCP configuration files
478
477
  - `session.log`: Human-readable request/response tracking
479
478
  - `session.log.json`: All events in JSONL format (one JSON per line)
480
- - `permissions.log`: Permission checks and decisions
479
+ # Note: permissions.log is no longer generated as the permission MCP server has been deprecated
481
480
 
482
481
  #### Listing Sessions
483
482
  View your previous Claude Swarm sessions:
@@ -535,15 +534,11 @@ This will:
535
534
  2. **MCP Generation**: For each instance, it generates an MCP configuration file that includes:
536
535
  - Any explicitly defined MCP servers
537
536
  - MCP servers for each connected instance (using `claude-swarm mcp-serve`)
538
- - A permission MCP server (unless using `--vibe` mode)
539
- 3. **Tool Permissions**: Claude Swarm automatically manages tool permissions:
540
- - Each instance's configured tools are allowed via the permission MCP
541
- - Supports wildcard patterns (e.g., `mcp__frontend__*` allows all frontend MCP tools)
537
+ 3. **Tool Permissions**: Claude Swarm manages tool permissions through configuration:
538
+ - Each instance's `allowed_tools` specifies which tools it can use
539
+ - Connected instances are accessible via `mcp__<instance_name>__*` pattern
542
540
  - Disallowed tools take precedence over allowed tools for fine-grained control
543
- - Eliminates the need to manually accept each tool or use global `--vibe` mode
544
541
  - Per-instance `vibe: true` skips all permission checks for that specific instance
545
- - The permission MCP uses `--permission-prompt-tool` to check tool access
546
- - Permission decisions are logged to `~/.claude-swarm/sessions/{project}/{timestamp}/permissions.log`
547
542
  4. **Session Persistence**: Claude Swarm automatically tracks session state:
548
543
  - Generates a shared session path for all instances
549
544
  - Each instance's Claude session ID is captured and saved
data/claude-swarm.yml CHANGED
@@ -18,7 +18,7 @@ swarm:
18
18
  directory: .
19
19
  model: sonnet
20
20
  prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
21
- allowed_tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
21
+ allowed_tools: [Read, Edit, Write, Bash]
22
22
 
23
23
  backend_dev:
24
24
  description: "Backend developer focusing on APIs, databases, and server architecture"
@@ -32,7 +32,7 @@ swarm:
32
32
  # directory: .
33
33
  # model: sonnet
34
34
  # prompt: "You specialize in infrastructure, CI/CD, containerization, and deployment"
35
- # allowed_tools: [Read, Edit, Write, "Bash(docker:*)", "Bash(kubectl:*)", "Bash(terraform:*)"]
35
+ # allowed_tools: [Read, Edit, Write, Bash]
36
36
 
37
37
  # qa_engineer:
38
38
  # description: "QA engineer ensuring quality through comprehensive testing"
@@ -58,7 +58,7 @@ swarm:
58
58
  allowed_tools:
59
59
  - Edit
60
60
  - Write
61
- - "Bash(npm:*, yarn:*, jest:*)"
61
+ - Bash
62
62
 
63
63
  ui_designer:
64
64
  description: "UI/UX specialist creating responsive designs and managing design system"
@@ -91,7 +91,7 @@ swarm:
91
91
  allowed_tools:
92
92
  - Edit
93
93
  - Write
94
- - "Bash(go:*, docker:*, make:*)"
94
+ - Bash
95
95
 
96
96
  api_gateway_dev:
97
97
  description: "API gateway developer managing request routing and rate limiting"
@@ -101,7 +101,7 @@ swarm:
101
101
  allowed_tools:
102
102
  - Edit
103
103
  - Write
104
- - "Bash(go:*, docker:*, kong:*)"
104
+ - Bash
105
105
 
106
106
  core_service_dev:
107
107
  description: "Core business logic service developer"
@@ -111,7 +111,7 @@ swarm:
111
111
  allowed_tools:
112
112
  - Edit
113
113
  - Write
114
- - "Bash(python:*, pip:*, pytest:*, docker:*)"
114
+ - Bash
115
115
 
116
116
  shared_lib_dev:
117
117
  description: "Shared libraries developer maintaining common code across services"
@@ -122,7 +122,7 @@ swarm:
122
122
  allowed_tools:
123
123
  - Edit
124
124
  - Write
125
- - "Bash(npm:*, go:*, python:*)"
125
+ - Bash
126
126
 
127
127
  # Mobile Team
128
128
  mobile_lead:
@@ -143,7 +143,7 @@ swarm:
143
143
  allowed_tools:
144
144
  - Edit
145
145
  - Write
146
- - "Bash(swift:*, xcodebuild:*, pod:*, fastlane:*)"
146
+ - Bash
147
147
 
148
148
  android_senior:
149
149
  description: "Senior Android developer creating Kotlin applications"
@@ -153,7 +153,7 @@ swarm:
153
153
  allowed_tools:
154
154
  - Edit
155
155
  - Write
156
- - "Bash(gradle:*, adb:*, fastlane:*)"
156
+ - Bash
157
157
 
158
158
  # Data Team
159
159
  data_lead:
@@ -175,7 +175,7 @@ swarm:
175
175
  allowed_tools:
176
176
  - Edit
177
177
  - Write
178
- - "Bash(python:*, airflow:*, spark:*, dbt:*)"
178
+ - Bash
179
179
 
180
180
  ml_engineer:
181
181
  description: "ML engineer developing and deploying machine learning models"
@@ -185,7 +185,7 @@ swarm:
185
185
  allowed_tools:
186
186
  - Edit
187
187
  - Write
188
- - "Bash(python:*, jupyter:*, mlflow:*, docker:*)"
188
+ - Bash
189
189
 
190
190
  analytics_dev:
191
191
  description: "Analytics developer creating dashboards and reports"
@@ -195,7 +195,7 @@ swarm:
195
195
  allowed_tools:
196
196
  - Edit
197
197
  - Write
198
- - "Bash(python:*, sql:*)"
198
+ - Bash
199
199
 
200
200
  # DevOps & Infrastructure
201
201
  devops_lead:
@@ -217,7 +217,7 @@ swarm:
217
217
  allowed_tools:
218
218
  - Edit
219
219
  - Write
220
- - "Bash(kubectl:*, helm:*, prometheus:*, grafana:*)"
220
+ - Bash
221
221
 
222
222
  platform_engineer:
223
223
  description: "Platform engineer managing Kubernetes and container infrastructure"
@@ -227,7 +227,7 @@ swarm:
227
227
  allowed_tools:
228
228
  - Edit
229
229
  - Write
230
- - "Bash(kubectl:*, helm:*, docker:*, istioctl:*)"
230
+ - Bash
231
231
 
232
232
  cloud_architect:
233
233
  description: "Cloud architect managing AWS/GCP infrastructure and costs"
@@ -237,7 +237,7 @@ swarm:
237
237
  allowed_tools:
238
238
  - Edit
239
239
  - Write
240
- - "Bash(terraform:*, aws:*, gcloud:*)"
240
+ - Bash
241
241
 
242
242
  # Quality & Security
243
243
  qa_lead:
@@ -259,7 +259,7 @@ swarm:
259
259
  allowed_tools:
260
260
  - Edit
261
261
  - Write
262
- - "Bash(npm:*, cypress:*, playwright:*)"
262
+ - Bash
263
263
 
264
264
  backend_qa:
265
265
  description: "Backend QA engineer creating API tests and integration tests"
@@ -269,7 +269,7 @@ swarm:
269
269
  allowed_tools:
270
270
  - Edit
271
271
  - Write
272
- - "Bash(python:*, pytest:*, postman:*, k6:*)"
272
+ - Bash
273
273
 
274
274
  mobile_qa:
275
275
  description: "Mobile QA engineer testing iOS and Android applications"
@@ -279,7 +279,7 @@ swarm:
279
279
  allowed_tools:
280
280
  - Edit
281
281
  - Write
282
- - "Bash(appium:*, xctest:*, espresso:*)"
282
+ - Bash
283
283
 
284
284
  security_lead:
285
285
  description: "Security lead ensuring application and infrastructure security"
@@ -290,4 +290,4 @@ swarm:
290
290
  allowed_tools:
291
291
  - Read
292
292
  - Edit
293
- - "Bash(trivy:*, snyk:*, owasp:*)"
293
+ - Bash
@@ -70,7 +70,7 @@ swarm:
70
70
  - Read
71
71
  - Edit
72
72
  - Write
73
- - "Bash(bundle:*)"
73
+ - Bash
74
74
 
75
75
  integration_test_expert:
76
76
  description: "Integration test specialist for controllers, API endpoints, and request specs"
@@ -111,7 +111,7 @@ swarm:
111
111
  - Read
112
112
  - Edit
113
113
  - Write
114
- - "Bash(bundle:*, rails:*)"
114
+ - Bash
115
115
 
116
116
  system_test_expert:
117
117
  description: "System test specialist for end-to-end user flows and JavaScript interactions"
@@ -154,7 +154,7 @@ swarm:
154
154
  - Read
155
155
  - Edit
156
156
  - Write
157
- - "Bash(bundle:*)"
157
+ - Bash
158
158
 
159
159
  factory_specialist:
160
160
  description: "Test data specialist managing factories, traits, and test data generation"
@@ -198,7 +198,7 @@ swarm:
198
198
  - Read
199
199
  - Edit
200
200
  - Write
201
- - "Bash(bundle:*)"
201
+ - Bash
202
202
 
203
203
  test_quality_reviewer:
204
204
  description: "Test quality assurance specialist ensuring best practices, coverage, and maintainability"
@@ -243,4 +243,4 @@ swarm:
243
243
  Provide actionable feedback to improve test quality and coverage.
244
244
  allowed_tools:
245
245
  - Read
246
- - "Bash(bundle:*, rubocop:*)"
246
+ - Bash
@@ -278,10 +278,18 @@ module ClaudeSwarm
278
278
  if @vibe
279
279
  cmd_array << "--dangerously-skip-permissions"
280
280
  else
281
+ # Build allowed tools list including MCP connections
282
+ allowed_tools = options[:allowed_tools] ? Array(options[:allowed_tools]).dup : []
283
+
284
+ # Add mcp__instance_name for each connection if we have instance info
285
+ options[:connections]&.each do |connection_name|
286
+ allowed_tools << "mcp__#{connection_name}"
287
+ end
288
+
281
289
  # Add allowed tools if any
282
- if options[:allowed_tools]
283
- tools = Array(options[:allowed_tools]).join(",")
284
- cmd_array += ["--allowedTools", tools]
290
+ if allowed_tools.any?
291
+ tools_str = allowed_tools.join(",")
292
+ cmd_array += ["--allowedTools", tools_str]
285
293
  end
286
294
 
287
295
  # Add disallowed tools if any
@@ -289,9 +297,6 @@ module ClaudeSwarm
289
297
  disallowed_tools = Array(options[:disallowed_tools]).join(",")
290
298
  cmd_array += ["--disallowedTools", disallowed_tools]
291
299
  end
292
-
293
- # Add permission prompt tool if not in vibe mode
294
- cmd_array += ["--permission-prompt-tool", "mcp__permissions__check_permission"]
295
300
  end
296
301
 
297
302
  cmd_array
@@ -6,7 +6,6 @@ require_relative "configuration"
6
6
  require_relative "mcp_generator"
7
7
  require_relative "orchestrator"
8
8
  require_relative "claude_mcp_server"
9
- require_relative "permission_mcp_server"
10
9
 
11
10
  module ClaudeSwarm
12
11
  class CLI < Thor
@@ -78,10 +77,12 @@ module ClaudeSwarm
78
77
  desc: "System prompt for the instance"
79
78
  method_option :description, type: :string,
80
79
  desc: "Description of the instance's role"
81
- method_option :tools, aliases: "-t", type: :array,
82
- desc: "Allowed tools for the instance"
80
+ method_option :allowed_tools, aliases: "-t", type: :array,
81
+ desc: "Allowed tools for the instance"
83
82
  method_option :disallowed_tools, type: :array,
84
83
  desc: "Disallowed tools for the instance"
84
+ method_option :connections, type: :array,
85
+ desc: "Connections to other instances"
85
86
  method_option :mcp_config_path, type: :string,
86
87
  desc: "Path to MCP configuration file"
87
88
  method_option :debug, type: :boolean, default: false,
@@ -103,8 +104,9 @@ module ClaudeSwarm
103
104
  model: options[:model],
104
105
  prompt: options[:prompt],
105
106
  description: options[:description],
106
- tools: options[:tools] || [],
107
+ allowed_tools: options[:allowed_tools] || [],
107
108
  disallowed_tools: options[:disallowed_tools] || [],
109
+ connections: options[:connections] || [],
108
110
  mcp_config_path: options[:mcp_config_path],
109
111
  vibe: options[:vibe],
110
112
  instance_id: options[:instance_id],
@@ -265,22 +267,6 @@ module ClaudeSwarm
265
267
  say " claude-swarm --session-id <session-id>", :cyan
266
268
  end
267
269
 
268
- desc "tools-mcp", "Start a permission management MCP server for tool access control"
269
- method_option :allowed_tools, aliases: "-t", type: :string,
270
- desc: "Comma-separated list of allowed tool patterns (supports wildcards)"
271
- method_option :disallowed_tools, type: :string,
272
- desc: "Comma-separated list of disallowed tool patterns (supports wildcards)"
273
- method_option :debug, type: :boolean, default: false,
274
- desc: "Enable debug output"
275
- def tools_mcp
276
- server = PermissionMcpServer.new(allowed_tools: options[:allowed_tools], disallowed_tools: options[:disallowed_tools])
277
- server.start
278
- rescue StandardError => e
279
- error "Error starting permission MCP server: #{e.message}"
280
- error e.backtrace.join("\n") if options[:debug]
281
- exit 1
282
- end
283
-
284
270
  default_task :start
285
271
 
286
272
  private
@@ -68,9 +68,6 @@ module ClaudeSwarm
68
68
  )
69
69
  end
70
70
 
71
- # Add permission MCP server if not in vibe mode (global or instance-specific)
72
- mcp_servers["permissions"] = build_permission_mcp_config(instance[:tools], instance[:disallowed_tools]) unless @vibe || instance[:vibe]
73
-
74
71
  config = {
75
72
  "instance_id" => @instance_ids[name],
76
73
  "instance_name" => name,
@@ -115,10 +112,12 @@ module ClaudeSwarm
115
112
 
116
113
  args.push("--description", instance[:description]) if instance[:description]
117
114
 
118
- args.push("--tools", instance[:tools].join(",")) if instance[:tools] && !instance[:tools].empty?
115
+ args.push("--allowed-tools", instance[:allowed_tools].join(",")) if instance[:allowed_tools] && !instance[:allowed_tools].empty?
119
116
 
120
117
  args.push("--disallowed-tools", instance[:disallowed_tools].join(",")) if instance[:disallowed_tools] && !instance[:disallowed_tools].empty?
121
118
 
119
+ args.push("--connections", instance[:connections].join(",")) if instance[:connections] && !instance[:connections].empty?
120
+
122
121
  args.push("--mcp-config-path", mcp_config_path(name))
123
122
 
124
123
  args.push("--calling-instance", calling_instance) if calling_instance
@@ -162,23 +161,5 @@ module ClaudeSwarm
162
161
  # Skip invalid state files
163
162
  end
164
163
  end
165
-
166
- def build_permission_mcp_config(allowed_tools, disallowed_tools)
167
- exe_path = "claude-swarm"
168
-
169
- args = ["tools-mcp"]
170
-
171
- # Add allowed tools if specified
172
- args.push("--allowed-tools", allowed_tools.join(",")) if allowed_tools && !allowed_tools.empty?
173
-
174
- # Add disallowed tools if specified
175
- args.push("--disallowed-tools", disallowed_tools.join(",")) if disallowed_tools && !disallowed_tools.empty?
176
-
177
- {
178
- "type" => "stdio",
179
- "command" => exe_path,
180
- "args" => args
181
- }
182
- end
183
164
  end
184
165
  end
@@ -91,7 +91,7 @@ module ClaudeSwarm
91
91
  puts "🚀 Launching main instance: #{@config.main_instance}"
92
92
  puts " Model: #{main_instance[:model]}"
93
93
  puts " Directory: #{main_instance[:directory]}"
94
- puts " Allowed tools: #{main_instance[:tools].join(", ")}" if main_instance[:tools].any?
94
+ puts " Allowed tools: #{main_instance[:allowed_tools].join(", ")}" if main_instance[:allowed_tools].any?
95
95
  puts " Disallowed tools: #{main_instance[:disallowed_tools].join(", ")}" if main_instance[:disallowed_tools]&.any?
96
96
  puts " Connections: #{main_instance[:connections].join(", ")}" if main_instance[:connections].any?
97
97
  puts " 😎 Vibe mode ON for this instance" if main_instance[:vibe]
@@ -208,9 +208,17 @@ module ClaudeSwarm
208
208
  if @vibe || instance[:vibe]
209
209
  parts << "--dangerously-skip-permissions"
210
210
  else
211
+ # Build allowed tools list including MCP connections
212
+ allowed_tools = instance[:allowed_tools].dup
213
+
214
+ # Add mcp__instance_name for each connection
215
+ instance[:connections].each do |connection_name|
216
+ allowed_tools << "mcp__#{connection_name}"
217
+ end
218
+
211
219
  # Add allowed tools if any
212
- if instance[:tools].any?
213
- tools_str = instance[:tools].join(",")
220
+ if allowed_tools.any?
221
+ tools_str = allowed_tools.join(",")
214
222
  parts << "--allowedTools"
215
223
  parts << tools_str
216
224
  end
@@ -221,10 +229,6 @@ module ClaudeSwarm
221
229
  parts << "--disallowedTools"
222
230
  parts << disallowed_tools_str
223
231
  end
224
-
225
- # Add permission prompt tool unless in vibe mode
226
- parts << "--permission-prompt-tool"
227
- parts << "mcp__permissions__check_permission"
228
232
  end
229
233
 
230
234
  if instance[:prompt]
@@ -4,7 +4,7 @@ module ClaudeSwarm
4
4
  class TaskTool < FastMcp::Tool
5
5
  tool_name "task"
6
6
  description "Execute a task using Claude Code. There is no description parameter."
7
- annotations(readOnlyHint: true, openWorldHint: false, destructiveHint: false)
7
+ annotations(read_only_hint: true, open_world_hint: false, destructive_hint: false)
8
8
 
9
9
  arguments do
10
10
  required(:prompt).filled(:string).description("The task or question for the agent")
@@ -22,11 +22,14 @@ module ClaudeSwarm
22
22
  }
23
23
 
24
24
  # Add allowed tools from instance config
25
- options[:allowed_tools] = instance_config[:tools] if instance_config[:tools]&.any?
25
+ options[:allowed_tools] = instance_config[:allowed_tools] if instance_config[:allowed_tools]&.any?
26
26
 
27
27
  # Add disallowed tools from instance config
28
28
  options[:disallowed_tools] = instance_config[:disallowed_tools] if instance_config[:disallowed_tools]&.any?
29
29
 
30
+ # Add connections from instance config
31
+ options[:connections] = instance_config[:connections] if instance_config[:connections]&.any?
32
+
30
33
  response = executor.execute(prompt, options)
31
34
 
32
35
  # Return just the result text as expected by MCP
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "0.1.15"
4
+ VERSION = "0.1.16"
5
5
  end
data/lib/claude_swarm.rb CHANGED
@@ -7,8 +7,6 @@ require_relative "claude_swarm/mcp_generator"
7
7
  require_relative "claude_swarm/orchestrator"
8
8
  require_relative "claude_swarm/claude_code_executor"
9
9
  require_relative "claude_swarm/claude_mcp_server"
10
- require_relative "claude_swarm/permission_tool"
11
- require_relative "claude_swarm/permission_mcp_server"
12
10
  require_relative "claude_swarm/session_path"
13
11
  require_relative "claude_swarm/session_info_tool"
14
12
  require_relative "claude_swarm/reset_session_tool"
data/llms.txt CHANGED
@@ -225,10 +225,10 @@ cat ~/.claude-swarm/sessions/PROJECT/TIMESTAMP/session.log.json
225
225
  - Check `main:` references valid instance key
226
226
  **"Circular dependency detected"**
227
227
  - Remove circular connections between instances
228
- **"Permission denied for tool X"**
228
+ **"Tool not allowed"**
229
229
  - Check `allowed_tools` includes the tool
230
230
  - Check `disallowed_tools` doesn't block it
231
- - Use `--vibe` to skip permissions (dangerous)
231
+ - Use `vibe: true` to skip all checks (dangerous)
232
232
  **"MCP server failed to start"**
233
233
  - Check the command/URL is correct
234
234
  - Verify MCP server is installed
@@ -362,12 +362,11 @@ bundle exec rake release
362
362
  - `CLAUDE_SWARM_HOME`: Override session storage location (default: ~/.claude-swarm)
363
363
  - `ANTHROPIC_MODEL`: Default Claude model if not specified
364
364
  ## Security Considerations
365
- - Tool restrictions enforced at permission layer
366
- - All permissions logged for audit
365
+ - Tool restrictions enforced through configuration
367
366
  - Vibe mode bypasses ALL restrictions - use carefully
368
367
  - Session files may contain sensitive data
369
368
  - Each instance runs with its directory context
370
- - MCP servers inherit instance permissions
369
+ - MCP servers inherit instance tool configurations
371
370
  ## Limitations
372
371
  - Tree hierarchy only (no circular dependencies)
373
372
  - Session restoration is experimental
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude_swarm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Arruda
@@ -59,6 +59,7 @@ files:
59
59
  - README.md
60
60
  - RELEASING.md
61
61
  - Rakefile
62
+ - claude-sdk-docs.md
62
63
  - claude-swarm.yml
63
64
  - example/claude-swarm.yml
64
65
  - example/microservices-team.yml
@@ -72,8 +73,6 @@ files:
72
73
  - lib/claude_swarm/configuration.rb
73
74
  - lib/claude_swarm/mcp_generator.rb
74
75
  - lib/claude_swarm/orchestrator.rb
75
- - lib/claude_swarm/permission_mcp_server.rb
76
- - lib/claude_swarm/permission_tool.rb
77
76
  - lib/claude_swarm/process_tracker.rb
78
77
  - lib/claude_swarm/reset_session_tool.rb
79
78
  - lib/claude_swarm/session_info_tool.rb
@@ -81,7 +80,6 @@ files:
81
80
  - lib/claude_swarm/task_tool.rb
82
81
  - lib/claude_swarm/version.rb
83
82
  - llms.txt
84
- - sdk-docs.md
85
83
  homepage: https://github.com/parruda/claude-swarm
86
84
  licenses: []
87
85
  metadata:
@@ -1,189 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "fast_mcp_annotations"
5
- require "logger"
6
- require "fileutils"
7
- require_relative "permission_tool"
8
- require_relative "session_path"
9
- require_relative "process_tracker"
10
-
11
- module ClaudeSwarm
12
- class PermissionMcpServer
13
- # Server configuration
14
- SERVER_NAME = "claude-swarm-permissions"
15
- SERVER_VERSION = "1.0.0"
16
-
17
- # Tool categories
18
- FILE_TOOLS = %w[Read Write Edit].freeze
19
- BASH_TOOL = "Bash"
20
-
21
- # Pattern matching
22
- TOOL_PATTERN_REGEX = /^([^()]+)\(([^)]+)\)$/
23
- PARAM_PATTERN_REGEX = /^(\w+)\s*:\s*(.+)$/
24
-
25
- def initialize(allowed_tools: nil, disallowed_tools: nil)
26
- @allowed_tools = allowed_tools
27
- @disallowed_tools = disallowed_tools
28
- setup_logging
29
- end
30
-
31
- def start
32
- configure_permission_tool
33
- create_and_start_server
34
- end
35
-
36
- private
37
-
38
- def configure_permission_tool
39
- allowed_patterns = parse_tool_patterns(@allowed_tools)
40
- disallowed_patterns = parse_tool_patterns(@disallowed_tools)
41
-
42
- log_configuration(allowed_patterns, disallowed_patterns)
43
-
44
- PermissionTool.allowed_patterns = allowed_patterns
45
- PermissionTool.disallowed_patterns = disallowed_patterns
46
- PermissionTool.logger = @logger
47
- end
48
-
49
- def create_and_start_server
50
- # Track this process
51
- session_path = SessionPath.from_env
52
- if session_path && File.exist?(session_path)
53
- tracker = ProcessTracker.new(session_path)
54
- tracker.track_pid(Process.pid, "mcp_permissions")
55
- end
56
-
57
- server = FastMcp::Server.new(
58
- name: SERVER_NAME,
59
- version: SERVER_VERSION
60
- )
61
-
62
- server.register_tool(PermissionTool)
63
- @logger.info("Permission MCP server started successfully")
64
- server.start
65
- end
66
-
67
- def setup_logging
68
- session_path = SessionPath.from_env
69
- SessionPath.ensure_directory(session_path)
70
- @logger = create_logger(session_path)
71
- @logger.info("Permission MCP server logging initialized")
72
- end
73
-
74
- def create_logger(session_path)
75
- log_path = File.join(session_path, "permissions.log")
76
- logger = Logger.new(log_path)
77
- logger.level = Logger::DEBUG
78
- logger.formatter = log_formatter
79
- logger
80
- end
81
-
82
- def log_formatter
83
- proc do |severity, datetime, _progname, msg|
84
- "[#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")}] [#{severity}] #{msg}\n"
85
- end
86
- end
87
-
88
- def log_configuration(allowed_patterns, disallowed_patterns)
89
- @logger.info("Starting permission MCP server with allowed patterns: #{allowed_patterns.inspect}, " \
90
- "disallowed patterns: #{disallowed_patterns.inspect}")
91
- end
92
-
93
- def parse_tool_patterns(tools)
94
- return [] if tools.nil? || tools.empty?
95
-
96
- normalize_tool_list(tools).filter_map do |tool|
97
- parse_single_tool_pattern(tool.strip)
98
- end
99
- end
100
-
101
- def normalize_tool_list(tools)
102
- tools.is_a?(Array) ? tools : tools.split(/[,\s]+/)
103
- end
104
-
105
- def parse_single_tool_pattern(tool)
106
- return nil if tool.empty?
107
-
108
- if (match = tool.match(TOOL_PATTERN_REGEX))
109
- parse_tool_with_pattern(match[1], match[2])
110
- elsif tool.include?("*")
111
- create_wildcard_tool_pattern(tool)
112
- else
113
- create_exact_tool_pattern(tool)
114
- end
115
- end
116
-
117
- def parse_tool_with_pattern(tool_name, pattern)
118
- case tool_name
119
- when *FILE_TOOLS
120
- create_file_tool_pattern(tool_name, pattern)
121
- when BASH_TOOL
122
- create_bash_tool_pattern(tool_name, pattern)
123
- else
124
- create_custom_tool_pattern(tool_name, pattern)
125
- end
126
- end
127
-
128
- def create_file_tool_pattern(tool_name, pattern)
129
- {
130
- tool_name: tool_name,
131
- pattern: File.expand_path(pattern),
132
- type: :glob
133
- }
134
- end
135
-
136
- def create_bash_tool_pattern(tool_name, pattern)
137
- {
138
- tool_name: tool_name,
139
- pattern: process_bash_pattern(pattern),
140
- type: :regex
141
- }
142
- end
143
-
144
- def process_bash_pattern(pattern)
145
- if pattern.include?(":")
146
- # Colon syntax: convert parts and join with spaces
147
- pattern.split(":")
148
- .map { |part| part.gsub("*", ".*") }
149
- .join(" ")
150
- else
151
- # Literal pattern: escape asterisks
152
- pattern.gsub("*", "\\*")
153
- end
154
- end
155
-
156
- def create_custom_tool_pattern(tool_name, pattern)
157
- {
158
- tool_name: tool_name,
159
- pattern: parse_parameter_patterns(pattern),
160
- type: :params
161
- }
162
- end
163
-
164
- def parse_parameter_patterns(pattern)
165
- pattern.split(",").each_with_object({}) do |param_pair, params|
166
- param_pair = param_pair.strip
167
- if (match = param_pair.match(PARAM_PATTERN_REGEX))
168
- params[match[1]] = match[2]
169
- end
170
- end
171
- end
172
-
173
- def create_wildcard_tool_pattern(tool)
174
- {
175
- tool_name: tool.gsub("*", ".*"),
176
- pattern: nil,
177
- type: :regex
178
- }
179
- end
180
-
181
- def create_exact_tool_pattern(tool)
182
- {
183
- tool_name: tool,
184
- pattern: nil,
185
- type: :exact
186
- }
187
- end
188
- end
189
- end
@@ -1,201 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "fast_mcp_annotations"
5
-
6
- module ClaudeSwarm
7
- class PermissionTool < FastMcp::Tool
8
- # Class variables to store allowed/disallowed patterns and logger
9
- class << self
10
- attr_accessor :allowed_patterns, :disallowed_patterns, :logger
11
- end
12
-
13
- # Tool categories
14
- FILE_TOOLS = %w[Read Write Edit].freeze
15
- BASH_TOOL = "Bash"
16
-
17
- # Response behaviors
18
- BEHAVIOR_ALLOW = "allow"
19
- BEHAVIOR_DENY = "deny"
20
-
21
- # File matching flags
22
- FILE_MATCH_FLAGS = File::FNM_DOTMATCH | File::FNM_PATHNAME | File::FNM_EXTGLOB
23
-
24
- tool_name "check_permission"
25
- description "Check if a tool is allowed to be used based on configured patterns"
26
-
27
- arguments do
28
- required(:tool_name).filled(:string).description("The tool requesting permission")
29
- required(:input).value(:hash).description("The input for the tool")
30
- end
31
-
32
- def call(tool_name:, input:)
33
- @current_tool_name = tool_name
34
- log_request(tool_name, input)
35
-
36
- result = evaluate_permission(tool_name, input)
37
- response = JSON.generate(result)
38
-
39
- log_response(response)
40
- response
41
- end
42
-
43
- private
44
-
45
- def evaluate_permission(tool_name, input)
46
- if explicitly_disallowed?(tool_name, input)
47
- deny_response(tool_name, "explicitly disallowed")
48
- elsif implicitly_allowed?(tool_name, input)
49
- allow_response(input)
50
- else
51
- deny_response(tool_name, "not allowed by configured patterns")
52
- end
53
- end
54
-
55
- def explicitly_disallowed?(tool_name, input)
56
- check_patterns(disallowed_patterns, tool_name, input, "Disallowed")
57
- end
58
-
59
- def implicitly_allowed?(tool_name, input)
60
- allowed_patterns.empty? || check_patterns(allowed_patterns, tool_name, input, "Allowed")
61
- end
62
-
63
- def check_patterns(patterns, tool_name, input, pattern_type)
64
- patterns.any? do |pattern_hash|
65
- match = matches_pattern?(tool_name, input, pattern_hash)
66
- log_pattern_check(pattern_type, pattern_hash, tool_name, input, match)
67
- match
68
- end
69
- end
70
-
71
- def matches_pattern?(tool_name, input, pattern_hash)
72
- return false unless tool_name_matches?(tool_name, pattern_hash)
73
- return true if pattern_hash[:pattern].nil?
74
-
75
- match_tool_specific_pattern(tool_name, input, pattern_hash)
76
- end
77
-
78
- def tool_name_matches?(tool_name, pattern_hash)
79
- case pattern_hash[:type]
80
- when :regex
81
- tool_name.match?(/^#{pattern_hash[:tool_name]}$/)
82
- else
83
- tool_name == pattern_hash[:tool_name]
84
- end
85
- end
86
-
87
- def match_tool_specific_pattern(_tool_name, input, pattern_hash)
88
- case pattern_hash[:tool_name]
89
- when BASH_TOOL
90
- match_bash_pattern(input, pattern_hash)
91
- when *FILE_TOOLS
92
- match_file_pattern(input, pattern_hash[:pattern])
93
- else
94
- match_custom_tool_pattern(input, pattern_hash)
95
- end
96
- end
97
-
98
- def match_bash_pattern(input, pattern_hash)
99
- command = extract_field_value(input, "command")
100
- return false unless command
101
-
102
- if pattern_hash[:type] == :regex
103
- command.match?(/^#{pattern_hash[:pattern]}$/)
104
- else
105
- command == pattern_hash[:pattern]
106
- end
107
- end
108
-
109
- def match_file_pattern(input, pattern)
110
- file_path = extract_field_value(input, "file_path")
111
- unless file_path
112
- log_missing_field("file_path", input)
113
- return false
114
- end
115
-
116
- File.fnmatch(pattern, File.expand_path(file_path), FILE_MATCH_FLAGS)
117
- end
118
-
119
- def match_custom_tool_pattern(input, pattern_hash)
120
- return false unless pattern_hash[:type] == :params && pattern_hash[:pattern].is_a?(Hash)
121
- return false if pattern_hash[:pattern].empty?
122
-
123
- match_parameter_patterns(input, pattern_hash[:pattern])
124
- end
125
-
126
- def match_parameter_patterns(input, param_patterns)
127
- param_patterns.all? do |param_name, param_pattern|
128
- value = extract_field_value(input, param_name.to_s)
129
- return false unless value
130
-
131
- regex_pattern = glob_to_regex(param_pattern)
132
- value.to_s.match?(/^#{regex_pattern}$/)
133
- end
134
- end
135
-
136
- def extract_field_value(input, field_name)
137
- input[field_name] || input[field_name.to_sym]
138
- end
139
-
140
- def glob_to_regex(pattern)
141
- Regexp.escape(pattern)
142
- .gsub('\*', ".*")
143
- .gsub('\?', ".")
144
- end
145
-
146
- # Response builders
147
- def allow_response(input)
148
- log_decision("ALLOWED", "matches configured patterns")
149
- {
150
- "behavior" => BEHAVIOR_ALLOW,
151
- "updatedInput" => input
152
- }
153
- end
154
-
155
- def deny_response(tool_name, reason)
156
- log_decision("DENIED", "is #{reason}")
157
- {
158
- "behavior" => BEHAVIOR_DENY,
159
- "message" => "Tool '#{tool_name}' is #{reason}"
160
- }
161
- end
162
-
163
- # Logging helpers
164
- def log_request(tool_name, input)
165
- logger&.info("Permission check requested for tool: #{tool_name}")
166
- logger&.info("Tool input: #{input.inspect}")
167
- logger&.info("Checking against allowed patterns: #{allowed_patterns.inspect}")
168
- logger&.info("Checking against disallowed patterns: #{disallowed_patterns.inspect}")
169
- end
170
-
171
- def log_response(response)
172
- logger&.info("Returning response: #{response}")
173
- end
174
-
175
- def log_pattern_check(pattern_type, pattern_hash, tool_name, input, match)
176
- logger&.info("#{pattern_type} pattern '#{pattern_hash.inspect}' vs '#{tool_name}' " \
177
- "with input '#{input.inspect}': #{match}")
178
- end
179
-
180
- def log_decision(status, reason)
181
- logger&.info("#{status}: Tool '#{@current_tool_name}' #{reason}")
182
- end
183
-
184
- def log_missing_field(field_name, input)
185
- logger&.info("#{field_name} not found in input: #{input.inspect}")
186
- end
187
-
188
- # Convenience accessors
189
- def logger
190
- self.class.logger
191
- end
192
-
193
- def allowed_patterns
194
- self.class.allowed_patterns || []
195
- end
196
-
197
- def disallowed_patterns
198
- self.class.disallowed_patterns || []
199
- end
200
- end
201
- end
File without changes