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 +4 -4
- data/CHANGELOG.md +25 -0
- data/CLAUDE.md +9 -5
- data/README.md +23 -28
- data/claude-swarm.yml +2 -2
- data/example/microservices-team.yml +17 -17
- data/example/test-generation.yml +5 -5
- data/lib/claude_swarm/claude_code_executor.rb +11 -6
- data/lib/claude_swarm/cli.rb +6 -20
- data/lib/claude_swarm/mcp_generator.rb +3 -22
- data/lib/claude_swarm/orchestrator.rb +11 -7
- data/lib/claude_swarm/task_tool.rb +5 -2
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm.rb +0 -2
- data/llms.txt +4 -5
- metadata +2 -4
- data/lib/claude_swarm/permission_mcp_server.rb +0 -189
- data/lib/claude_swarm/permission_tool.rb +0 -201
- /data/{sdk-docs.md → claude-sdk-docs.md} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b71b3ce3aebae7de091dd130cda9fc4c474f538f444be15f1ee5b07501210db6
|
4
|
+
data.tar.gz: 846ac3f979b5912ccbc70f1810b760f625e75b1c7e905d5d1199b893d0bc3400
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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,
|
89
|
+
tools: [Edit, Write, Bash]
|
89
90
|
```
|
90
91
|
|
91
92
|
## Testing
|
92
93
|
|
93
|
-
The gem includes
|
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
|
-
[](https://badge.fury.io/rb/claude_swarm)
|
4
4
|
[](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,
|
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,
|
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,
|
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,
|
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,
|
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
|
-
|
276
|
-
|
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
|
-
####
|
283
|
-
|
284
|
-
You can restrict tools with pattern-based filters:
|
282
|
+
#### Available Tools
|
285
283
|
|
286
284
|
```yaml
|
287
285
|
allowed_tools:
|
288
|
-
- Read
|
289
|
-
- Edit
|
290
|
-
-
|
291
|
-
-
|
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,
|
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
|
-
#
|
461
|
-
|
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
|
-
|
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
|
-
|
539
|
-
|
540
|
-
-
|
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,
|
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,
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
293
|
+
- Bash
|
data/example/test-generation.yml
CHANGED
@@ -70,7 +70,7 @@ swarm:
|
|
70
70
|
- Read
|
71
71
|
- Edit
|
72
72
|
- Write
|
73
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
-
-
|
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
|
283
|
-
|
284
|
-
cmd_array += ["--allowedTools",
|
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
|
data/lib/claude_swarm/cli.rb
CHANGED
@@ -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 :
|
82
|
-
|
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
|
-
|
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[:
|
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[:
|
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
|
213
|
-
tools_str =
|
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(
|
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[:
|
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
|
data/lib/claude_swarm/version.rb
CHANGED
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
|
-
**"
|
228
|
+
**"Tool not allowed"**
|
229
229
|
- Check `allowed_tools` includes the tool
|
230
230
|
- Check `disallowed_tools` doesn't block it
|
231
|
-
- Use
|
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
|
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
|
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.
|
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
|