claude_swarm 0.1.5 → 0.1.6

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: a6fa670f8d967ad8976c05b96fe3d2103125ec17e31e3c343bb38a3002dc5182
4
- data.tar.gz: e4cb03e288ea20ac810414bd71e5a84c6d1bb9b4c77ee041a29deebb8270d7ee
3
+ metadata.gz: 32206b2e4ec4228e8d9c2450447f0508a2c58378d37c80ce24d327a54653ee05
4
+ data.tar.gz: c11bf2d8042b73de10ee2f40c01e977d8bae0a8bd74e23b74fc29216735396c3
5
5
  SHA512:
6
- metadata.gz: 06d9f249a5f0c52d13a6dc9d34b1b49d572d9432b0617a616cd9c3d35058b17320b23d63b3db641c55d5a812b810c6ea8fab4ae742555da483f28064360f3326
7
- data.tar.gz: '082a16da97c25bffa67fcdf831a1b5c612b02f1ba3884a0a952eb77bf78f9cf22d045b728dad351b0cce8b201943dd972df4fd4301396ace0322fb1de0dd711a'
6
+ metadata.gz: 5e639606be661128f4c23411f6e9c4164cf9b212ce37e6f99178ba33e4d67a5eb0995b25c1dbe885fff0c20230ea12e347c7b04c7b3bef8b4313353bce6cb948
7
+ data.tar.gz: 8d51962bb83f7268510b44a1aff1b13c5f5260c008b8bb26b41d971d0fbdd816c76a9221c275c028851c7ccc825c2aeee7b0d7d0e311f50485119693b3b5791a
data/.rubocop.yml CHANGED
@@ -59,4 +59,7 @@ Metrics/ModuleLength:
59
59
  Enabled: false
60
60
 
61
61
  Minitest/MultipleAssertions:
62
+ Enabled: false
63
+
64
+ Metrics/ParameterLists:
62
65
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## [0.1.6]
2
+ - Refactor: move tools out of the ClaudeMcpServer class
3
+ - Move logging into code executor and save instance interaction streams to session.log
4
+ - Human readable logs with thoughts and tool calls
5
+
1
6
  ## [0.1.5]
2
7
 
3
8
  ### Changed
data/claude-swarm.yml CHANGED
@@ -6,7 +6,7 @@ swarm:
6
6
  lead_developer:
7
7
  description: "Lead developer coordinating the team and making architectural decisions"
8
8
  directory: .
9
- model: sonnet
9
+ model: opus
10
10
  prompt: "You are the lead developer coordinating the team"
11
11
  tools: [Read, Edit, Bash, Write]
12
12
  connections: [frontend_dev]
@@ -16,6 +16,6 @@ swarm:
16
16
  frontend_dev:
17
17
  description: "Frontend developer specializing in React and modern web technologies"
18
18
  directory: .
19
- model: sonnet
19
+ model: opus
20
20
  prompt: "You specialize in frontend development with React, TypeScript, and modern web technologies"
21
21
  tools: [Read, Edit, Write, "Bash(npm:*)", "Bash(yarn:*)", "Bash(pnpm:*)"]
@@ -2,43 +2,85 @@
2
2
 
3
3
  require "json"
4
4
  require "open3"
5
+ require "logger"
6
+ require "fileutils"
5
7
 
6
8
  module ClaudeSwarm
7
9
  class ClaudeCodeExecutor
8
- attr_reader :session_id, :last_response, :working_directory
10
+ SWARM_DIR = ".claude-swarm"
11
+ SESSIONS_DIR = "sessions"
9
12
 
10
- def initialize(working_directory: Dir.pwd, model: nil, mcp_config: nil, vibe: false)
13
+ attr_reader :session_id, :last_response, :working_directory, :logger, :session_timestamp
14
+
15
+ def initialize(working_directory: Dir.pwd, model: nil, mcp_config: nil, vibe: false, instance_name: nil, calling_instance: nil)
11
16
  @working_directory = working_directory
12
17
  @model = model
13
18
  @mcp_config = mcp_config
14
19
  @vibe = vibe
15
20
  @session_id = nil
16
21
  @last_response = nil
22
+ @instance_name = instance_name
23
+ @calling_instance = calling_instance
24
+
25
+ # Setup logging
26
+ setup_logging
17
27
  end
18
28
 
19
29
  def execute(prompt, options = {})
20
- cmd_array = build_command_array(prompt, options)
21
-
22
- stdout, stderr, status = Open3.capture3(*cmd_array, chdir: @working_directory)
23
-
24
- raise ExecutionError, "Claude Code execution failed: #{stderr}" unless status.success?
30
+ # Log the request
31
+ log_request(prompt)
25
32
 
26
- begin
27
- response = JSON.parse(stdout)
28
- @last_response = response
29
-
30
- # Extract and store session ID from the response
31
- @session_id = response["session_id"]
33
+ cmd_array = build_command_array(prompt, options)
32
34
 
33
- response
34
- rescue JSON::ParserError => e
35
- raise ParseError, "Failed to parse JSON response: #{e.message}\nOutput: #{stdout}"
35
+ # Variables to collect output
36
+ stderr_output = []
37
+ result_response = nil
38
+
39
+ # Execute command with streaming
40
+ Open3.popen3(*cmd_array, chdir: @working_directory) do |stdin, stdout, stderr, wait_thread|
41
+ stdin.close
42
+
43
+ # Read stderr in a separate thread
44
+ stderr_thread = Thread.new do
45
+ stderr.each_line { |line| stderr_output << line }
46
+ end
47
+
48
+ # Process stdout line by line
49
+ stdout.each_line do |line|
50
+ json_data = JSON.parse(line.strip)
51
+
52
+ # Log each JSON event
53
+ log_streaming_event(json_data)
54
+
55
+ # Capture session_id from system init
56
+ @session_id = json_data["session_id"] if json_data["type"] == "system" && json_data["subtype"] == "init"
57
+
58
+ # Capture the final result
59
+ result_response = json_data if json_data["type"] == "result"
60
+ rescue JSON::ParserError => e
61
+ @logger.warn("Failed to parse JSON line: #{line.strip} - #{e.message}")
62
+ end
63
+
64
+ # Wait for stderr thread to finish
65
+ stderr_thread.join
66
+
67
+ # Check exit status
68
+ exit_status = wait_thread.value
69
+ unless exit_status.success?
70
+ error_msg = stderr_output.join
71
+ @logger.error("Execution error for #{@instance_name}: #{error_msg}")
72
+ raise ExecutionError, "Claude Code execution failed: #{error_msg}"
73
+ end
36
74
  end
37
- end
38
75
 
39
- def execute_text(prompt, options = {})
40
- response = execute(prompt, options)
41
- response["result"] || ""
76
+ # Ensure we got a result
77
+ raise ParseError, "No result found in stream output" unless result_response
78
+
79
+ result_response
80
+ rescue StandardError => e
81
+ @logger.error("Unexpected error for #{@instance_name}: #{e.class} - #{e.message}")
82
+ @logger.error("Backtrace: #{e.backtrace.join("\n")}")
83
+ raise
42
84
  end
43
85
 
44
86
  def reset_session
@@ -52,12 +94,90 @@ module ClaudeSwarm
52
94
 
53
95
  private
54
96
 
97
+ def setup_logging
98
+ # Use environment variable for session timestamp if available (set by orchestrator)
99
+ # Otherwise create a new timestamp
100
+ @session_timestamp = ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] || Time.now.strftime("%Y%m%d_%H%M%S")
101
+
102
+ # Ensure the session directory exists
103
+ session_dir = File.join(Dir.pwd, SWARM_DIR, SESSIONS_DIR, @session_timestamp)
104
+ FileUtils.mkdir_p(session_dir)
105
+
106
+ # Create logger with session.log filename
107
+ log_filename = "session.log"
108
+ log_path = File.join(session_dir, log_filename)
109
+ @logger = Logger.new(log_path)
110
+ @logger.level = Logger::INFO
111
+
112
+ # Custom formatter for better readability
113
+ @logger.formatter = proc do |severity, datetime, _progname, msg|
114
+ "[#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")}] [#{severity}] #{msg}\n"
115
+ end
116
+
117
+ @logger.info("Started Claude Code executor for instance: #{@instance_name}") if @instance_name
118
+ end
119
+
120
+ def log_request(prompt)
121
+ @logger.info("#{@calling_instance} -> #{@instance_name}: \n---\n#{prompt}\n---")
122
+ end
123
+
124
+ def log_response(response)
125
+ @logger.info(
126
+ "($#{response["total_cost"]} - #{response["duration_ms"]}ms) #{@instance_name} -> #{@calling_instance}: \n---\n#{response["result"]}\n---"
127
+ )
128
+ end
129
+
130
+ def log_streaming_event(event)
131
+ return log_system_message(event) if event["type"] == "system"
132
+
133
+ # Add specific details based on event type
134
+ case event["type"]
135
+ when "assistant"
136
+ log_assistant_message(event["message"])
137
+ when "user"
138
+ log_user_message(event["message"]["content"])
139
+ when "result"
140
+ log_response(event)
141
+ end
142
+ end
143
+
144
+ def log_system_message(event)
145
+ @logger.debug("SYSTEM: #{JSON.pretty_generate(event)}")
146
+ end
147
+
148
+ def log_assistant_message(msg)
149
+ return if msg["stop_reason"] == "end_turn" # that means it is not a thought but the final answer
150
+
151
+ content = msg["content"]
152
+ @logger.debug("ASSISTANT: #{JSON.pretty_generate(content)}")
153
+ tool_calls = content.select { |c| c["type"] == "tool_use" }
154
+ tool_calls.each do |tool_call|
155
+ arguments = tool_call["input"].to_json
156
+ arguments = "#{arguments[0..300]} ...}" if arguments.length > 300
157
+
158
+ @logger.info(
159
+ "Tool call from #{@instance_name} -> Tool: #{tool_call["name"]}, ID: #{tool_call["id"]}, Arguments: #{arguments}"
160
+ )
161
+ end
162
+
163
+ text = content.select { |c| c["type"] == "text" }
164
+ text.each do |t|
165
+ @logger.info("#{@instance_name} is thinking:\n---\n#{t["text"]}\n---")
166
+ end
167
+ end
168
+
169
+ def log_user_message(content)
170
+ @logger.debug("USER: #{JSON.pretty_generate(content)}")
171
+ end
172
+
55
173
  def build_command_array(prompt, options)
56
174
  cmd_array = ["claude"]
57
175
 
58
176
  # Add model if specified
59
177
  cmd_array += ["--model", @model]
60
178
 
179
+ cmd_array << "--verbose"
180
+
61
181
  # Add MCP config if specified
62
182
  cmd_array += ["--mcp-config", @mcp_config] if @mcp_config
63
183
 
@@ -65,7 +185,7 @@ module ClaudeSwarm
65
185
  cmd_array += ["--resume", @session_id] if @session_id && !options[:new_session]
66
186
 
67
187
  # Always use JSON output format for structured responses
68
- cmd_array += ["--output-format", "json"]
188
+ cmd_array += ["--output-format", "stream-json"]
69
189
 
70
190
  # Add non-interactive mode with prompt
71
191
  cmd_array += ["--print", "-p", prompt]
@@ -2,15 +2,13 @@
2
2
 
3
3
  require "fast_mcp"
4
4
  require "json"
5
- require "fileutils"
6
- require "logger"
7
5
  require_relative "claude_code_executor"
6
+ require_relative "task_tool"
7
+ require_relative "session_info_tool"
8
+ require_relative "reset_session_tool"
8
9
 
9
10
  module ClaudeSwarm
10
11
  class ClaudeMcpServer
11
- SWARM_DIR = ".claude-swarm"
12
- SESSIONS_DIR = "sessions"
13
-
14
12
  # Class variables to share state with tool classes
15
13
  class << self
16
14
  attr_accessor :executor, :instance_config, :logger, :session_timestamp, :calling_instance
@@ -23,46 +21,19 @@ module ClaudeSwarm
23
21
  working_directory: instance_config[:directory],
24
22
  model: instance_config[:model],
25
23
  mcp_config: instance_config[:mcp_config_path],
26
- vibe: instance_config[:vibe]
24
+ vibe: instance_config[:vibe],
25
+ instance_name: instance_config[:name],
26
+ calling_instance: calling_instance
27
27
  )
28
28
 
29
- # Setup logging
30
- setup_logging
31
-
32
29
  # Set class variables so tools can access them
33
30
  self.class.executor = @executor
34
31
  self.class.instance_config = @instance_config
35
- self.class.logger = @logger
32
+ self.class.logger = @executor.logger
33
+ self.class.session_timestamp = @executor.session_timestamp
36
34
  self.class.calling_instance = @calling_instance
37
35
  end
38
36
 
39
- private
40
-
41
- def setup_logging
42
- # Use environment variable for session timestamp if available (set by orchestrator)
43
- # Otherwise create a new timestamp
44
- self.class.session_timestamp ||= ENV["CLAUDE_SWARM_SESSION_TIMESTAMP"] || Time.now.strftime("%Y%m%d_%H%M%S")
45
-
46
- # Ensure the session directory exists
47
- session_dir = File.join(Dir.pwd, SWARM_DIR, SESSIONS_DIR, self.class.session_timestamp)
48
- FileUtils.mkdir_p(session_dir)
49
-
50
- # Create logger with session.log filename
51
- log_filename = "session.log"
52
- log_path = File.join(session_dir, log_filename)
53
- @logger = Logger.new(log_path)
54
- @logger.level = Logger::INFO
55
-
56
- # Custom formatter for better readability
57
- @logger.formatter = proc do |severity, datetime, _progname, msg|
58
- "[#{datetime.strftime("%Y-%m-%d %H:%M:%S.%L")}] [#{severity}] #{msg}\n"
59
- end
60
-
61
- @logger.info("Started MCP server for instance: #{@instance_config[:name]}")
62
- end
63
-
64
- public
65
-
66
37
  def start
67
38
  server = FastMcp::Server.new(
68
39
  name: @instance_config[:name],
@@ -84,120 +55,5 @@ module ClaudeSwarm
84
55
  # Start the stdio server
85
56
  server.start
86
57
  end
87
-
88
- class TaskTool < FastMcp::Tool
89
- tool_name "task"
90
- description "Execute a task using Claude Code"
91
-
92
- arguments do
93
- required(:prompt).filled(:string).description("The task or question for the agent")
94
- optional(:new_session).filled(:bool).description("Start a new session (default: false)")
95
- optional(:system_prompt).filled(:string).description("Override the system prompt for this request")
96
- end
97
-
98
- def call(prompt:, new_session: false, system_prompt: nil)
99
- executor = ClaudeMcpServer.executor
100
- instance_config = ClaudeMcpServer.instance_config
101
- logger = ClaudeMcpServer.logger
102
-
103
- options = {
104
- new_session: new_session,
105
- system_prompt: system_prompt || instance_config[:prompt]
106
- }
107
-
108
- # Add allowed tools from instance config
109
- options[:allowed_tools] = instance_config[:tools] if instance_config[:tools]&.any?
110
-
111
- begin
112
- # Log the request
113
- log_entry = {
114
- timestamp: Time.now.utc.iso8601,
115
- from_instance: ClaudeMcpServer.calling_instance, # The instance making the request
116
- to_instance: instance_config[:name], # This instance is receiving the request
117
- model: instance_config[:model],
118
- working_directory: instance_config[:directory],
119
- session_id: executor.session_id,
120
- request: {
121
- prompt: prompt,
122
- new_session: new_session,
123
- system_prompt: options[:system_prompt],
124
- allowed_tools: options[:allowed_tools]
125
- }
126
- }
127
-
128
- logger.info("REQUEST: #{JSON.pretty_generate(log_entry)}")
129
-
130
- response = executor.execute(prompt, options)
131
-
132
- # Log the response
133
- response_entry = {
134
- timestamp: Time.now.utc.iso8601,
135
- from_instance: instance_config[:name], # This instance is sending the response
136
- to_instance: ClaudeMcpServer.calling_instance, # The instance that made the request receives the response
137
- session_id: executor.session_id, # Update with new session ID if changed
138
- response: {
139
- result: response["result"],
140
- cost_usd: response["cost_usd"],
141
- duration_ms: response["duration_ms"],
142
- is_error: response["is_error"],
143
- total_cost: response["total_cost"]
144
- }
145
- }
146
-
147
- logger.info("RESPONSE: #{JSON.pretty_generate(response_entry)}")
148
-
149
- # Return just the result text as expected by MCP
150
- response["result"]
151
- rescue ClaudeCodeExecutor::ExecutionError => e
152
- logger.error("Execution error for #{instance_config[:name]}: #{e.message}")
153
- raise StandardError, "Execution failed: #{e.message}"
154
- rescue ClaudeCodeExecutor::ParseError => e
155
- logger.error("Parse error for #{instance_config[:name]}: #{e.message}")
156
- raise StandardError, "Parse error: #{e.message}"
157
- rescue StandardError => e
158
- logger.error("Unexpected error for #{instance_config[:name]}: #{e.class} - #{e.message}")
159
- logger.error("Backtrace: #{e.backtrace.join("\n")}")
160
- raise StandardError, "Unexpected error: #{e.message}"
161
- end
162
- end
163
- end
164
-
165
- class SessionInfoTool < FastMcp::Tool
166
- tool_name "session_info"
167
- description "Get information about the current Claude session for this agent"
168
-
169
- arguments do
170
- # No arguments needed
171
- end
172
-
173
- def call
174
- executor = ClaudeMcpServer.executor
175
-
176
- {
177
- has_session: executor.has_session?,
178
- session_id: executor.session_id,
179
- working_directory: executor.working_directory
180
- }
181
- end
182
- end
183
-
184
- class ResetSessionTool < FastMcp::Tool
185
- tool_name "reset_session"
186
- description "Reset the Claude session for this agent, starting fresh on the next task"
187
-
188
- arguments do
189
- # No arguments needed
190
- end
191
-
192
- def call
193
- executor = ClaudeMcpServer.executor
194
- executor.reset_session
195
-
196
- {
197
- success: true,
198
- message: "Session has been reset"
199
- }
200
- end
201
- end
202
58
  end
203
59
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeSwarm
4
+ class ResetSessionTool < FastMcp::Tool
5
+ tool_name "reset_session"
6
+ description "Reset the Claude session for this agent, starting fresh on the next task"
7
+
8
+ arguments do
9
+ # No arguments needed
10
+ end
11
+
12
+ def call
13
+ executor = ClaudeMcpServer.executor
14
+ executor.reset_session
15
+
16
+ {
17
+ success: true,
18
+ message: "Session has been reset"
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeSwarm
4
+ class SessionInfoTool < FastMcp::Tool
5
+ tool_name "session_info"
6
+ description "Get information about the current Claude session for this agent"
7
+
8
+ arguments do
9
+ # No arguments needed
10
+ end
11
+
12
+ def call
13
+ executor = ClaudeMcpServer.executor
14
+
15
+ {
16
+ has_session: executor.has_session?,
17
+ session_id: executor.session_id,
18
+ working_directory: executor.working_directory
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeSwarm
4
+ class TaskTool < FastMcp::Tool
5
+ tool_name "task"
6
+ description "Execute a task using Claude Code"
7
+
8
+ arguments do
9
+ required(:prompt).filled(:string).description("The task or question for the agent")
10
+ optional(:new_session).filled(:bool).description("Start a new session (default: false)")
11
+ optional(:system_prompt).filled(:string).description("Override the system prompt for this request")
12
+ end
13
+
14
+ def call(prompt:, new_session: false, system_prompt: nil)
15
+ executor = ClaudeMcpServer.executor
16
+ instance_config = ClaudeMcpServer.instance_config
17
+
18
+ options = {
19
+ new_session: new_session,
20
+ system_prompt: system_prompt || instance_config[:prompt]
21
+ }
22
+
23
+ # Add allowed tools from instance config
24
+ options[:allowed_tools] = instance_config[:tools] if instance_config[:tools]&.any?
25
+
26
+ response = executor.execute(prompt, options)
27
+
28
+ # Return just the result text as expected by MCP
29
+ response["result"]
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeSwarm
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.6"
5
5
  end
data/sdk-docs.md ADDED
@@ -0,0 +1,426 @@
1
+ # SDK
2
+
3
+ > Programmatically integrate Claude Code into your applications using the SDK.
4
+
5
+ The Claude Code SDK allows developers to programmatically integrate Claude Code into their applications. It enables running Claude Code as a subprocess, providing a way to build AI-powered coding assistants and tools that leverage Claude's capabilities.
6
+
7
+ The SDK currently support command line usage. TypeScript and Python SDKs are coming soon.
8
+
9
+ ## Authentication
10
+
11
+ To use the Claude Code SDK, we recommend creating a dedicated API key:
12
+
13
+ 1. Create an Anthropic API key in the [Anthropic Console](https://console.anthropic.com/)
14
+ 2. Then, set the `ANTHROPIC_API_KEY` environment variable. We recommend storing this key securely (eg. using a Github [secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions))
15
+
16
+ ## Basic SDK usage
17
+
18
+ The Claude Code SDK allows you to use Claude Code in non-interactive mode from your applications. Here's a basic example:
19
+
20
+ ```bash
21
+ # Run a single prompt and exit (print mode)
22
+ $ claude -p "Write a function to calculate Fibonacci numbers"
23
+
24
+ # Using a pipe to provide stdin
25
+ $ echo "Explain this code" | claude -p
26
+
27
+ # Output in JSON format with metadata
28
+ $ claude -p "Generate a hello world function" --output-format json
29
+
30
+ # Stream JSON output as it arrives
31
+ $ claude -p "Build a React component" --output-format stream-json
32
+ ```
33
+
34
+ ## Advanced usage
35
+
36
+ ### Multi-turn conversations
37
+
38
+ For multi-turn conversations, you can resume conversations or continue from the most recent session:
39
+
40
+ ```bash
41
+ # Continue the most recent conversation
42
+ $ claude --continue
43
+
44
+ # Continue and provide a new prompt
45
+ $ claude --continue "Now refactor this for better performance"
46
+
47
+ # Resume a specific conversation by session ID
48
+ $ claude --resume 550e8400-e29b-41d4-a716-446655440000
49
+
50
+ # Resume in print mode (non-interactive)
51
+ $ claude -p --resume 550e8400-e29b-41d4-a716-446655440000 "Update the tests"
52
+
53
+ # Continue in print mode (non-interactive)
54
+ $ claude -p --continue "Add error handling"
55
+ ```
56
+
57
+ ### Custom system prompts
58
+
59
+ You can provide custom system prompts to guide Claude's behavior:
60
+
61
+ ```bash
62
+ # Override system prompt (only works with --print)
63
+ $ claude -p "Build a REST API" --system-prompt "You are a senior backend engineer. Focus on security, performance, and maintainability."
64
+
65
+ # System prompt with specific requirements
66
+ $ claude -p "Create a database schema" --system-prompt "You are a database architect. Use PostgreSQL best practices and include proper indexing."
67
+ ```
68
+
69
+ You can also append instructions to the default system prompt:
70
+
71
+ ```bash
72
+ # Append system prompt (only works with --print)
73
+ $ claude -p "Build a REST API" --append-system-prompt "After writing code, be sure to code review yourself."
74
+ ```
75
+
76
+ ### MCP Configuration
77
+
78
+ The Model Context Protocol (MCP) allows you to extend Claude Code with additional tools and resources from external servers. Using the `--mcp-config` flag, you can load MCP servers that provide specialized capabilities like database access, API integrations, or custom tooling.
79
+
80
+ Create a JSON configuration file with your MCP servers:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "filesystem": {
86
+ "command": "npx",
87
+ "args": [
88
+ "-y",
89
+ "@modelcontextprotocol/server-filesystem",
90
+ "/path/to/allowed/files"
91
+ ]
92
+ },
93
+ "github": {
94
+ "command": "npx",
95
+ "args": ["-y", "@modelcontextprotocol/server-github"],
96
+ "env": {
97
+ "GITHUB_TOKEN": "your-github-token"
98
+ }
99
+ }
100
+ }
101
+ }
102
+ ```
103
+
104
+ Then use it with Claude Code:
105
+
106
+ ```bash
107
+ # Load MCP servers from configuration
108
+ $ claude -p "List all files in the project" --mcp-config mcp-servers.json
109
+
110
+ # Important: MCP tools must be explicitly allowed using --allowedTools
111
+ # MCP tools follow the format: mcp__$serverName__$toolName
112
+ $ claude -p "Search for TODO comments" \
113
+ --mcp-config mcp-servers.json \
114
+ --allowedTools "mcp__filesystem__read_file,mcp__filesystem__list_directory"
115
+
116
+ # Use an MCP tool for handling permission prompts in non-interactive mode
117
+ $ claude -p "Deploy the application" \
118
+ --mcp-config mcp-servers.json \
119
+ --allowedTools "mcp__permissions__approve" \
120
+ --permission-prompt-tool mcp__permissions__approve
121
+ ```
122
+
123
+ Note: When using MCP tools, you must explicitly allow them using the `--allowedTools` flag. MCP tool names follow the pattern `mcp__<serverName>__<toolName>` where:
124
+
125
+ * `serverName` is the key from your MCP configuration file
126
+ * `toolName` is the specific tool provided by that server
127
+
128
+ This security measure ensures that MCP tools are only used when explicitly permitted.
129
+
130
+ ### Custom permission prompt tool
131
+
132
+ Optionally, use `--permission-prompt-tool` to pass in an MCP tool that we will use to check whether or not the user grants the model permissions to invoke a given tool. When the model invokes a tool the following happens:
133
+
134
+ 1. We first check permission settings: all [settings.json files](/en/docs/claude-code/settings), as well as `--allowedTools` and `--disallowedTools` passed into the SDK; if one of these allows or denies the tool call, we proceed with the tool call
135
+ 2. Otherwise, we invoke the MCP tool you provided in `--permission-prompt-tool`
136
+
137
+ The `--permission-prompt-tool` MCP tool is passed the tool name and input, and must return a JSON-stringified payload with the result. The payload must be one of:
138
+
139
+ ```ts
140
+ // tool call is allowed
141
+ {
142
+ "behavior": "allow",
143
+ "updatedInput": {...}, // updated input, or just return back the original input
144
+ }
145
+
146
+ // tool call is denied
147
+ {
148
+ "behavior": "deny",
149
+ "message": "..." // human-readable string explaining why the permission was denied
150
+ }
151
+ ```
152
+
153
+ For example, a TypeScript MCP permission prompt tool implementation might look like this:
154
+
155
+ ```ts
156
+ const server = new McpServer({
157
+ name: "Test permission prompt MCP Server",
158
+ version: "0.0.1",
159
+ });
160
+
161
+ server.tool(
162
+ "approval_prompt",
163
+ 'Simulate a permission check - approve if the input contains "allow", otherwise deny',
164
+ {
165
+ tool_name: z.string().describe("The tool requesting permission"),
166
+ input: z.object({}).passthrough().describe("The input for the tool"),
167
+ },
168
+ async ({ tool_name, input }) => {
169
+ return {
170
+ content: [
171
+ {
172
+ type: "text",
173
+ text: JSON.stringify(
174
+ JSON.stringify(input).includes("allow")
175
+ ? {
176
+ behavior: "allow",
177
+ updatedInput: input,
178
+ }
179
+ : {
180
+ behavior: "deny",
181
+ message: "Permission denied by test approval_prompt tool",
182
+ }
183
+ ),
184
+ },
185
+ ],
186
+ };
187
+ }
188
+ );
189
+ ```
190
+
191
+ To use this tool, add your MCP server (eg. with `--mcp-config`), then invoke the SDK like so:
192
+
193
+ ```sh
194
+ claude -p "..." \
195
+ --permission-prompt-tool mcp__test-server__approval_prompt \
196
+ --mcp-config my-config.json
197
+ ```
198
+
199
+ Usage notes:
200
+
201
+ * Use `updatedInput` to tell the model that the permission prompt mutated its input; otherwise, set `updatedInput` to the original input, as in the example above. For example, if the tool shows a file edit diff to the user and lets them edit the diff manually, the permission prompt tool should return that updated edit.
202
+ * The payload must be JSON-stringified
203
+
204
+ ## Available CLI options
205
+
206
+ The SDK leverages all the CLI options available in Claude Code. Here are the key ones for SDK usage:
207
+
208
+ | Flag | Description | Example |
209
+ | :------------------------- | :--------------------------------------------------------------- | :------------------------------------------------------------- |
210
+ | `--print`, `-p` | Run in non-interactive mode | `claude -p "query"` |
211
+ | `--output-format` | Specify output format (`text`, `json`, `stream-json`) | `claude -p --output-format json` |
212
+ | `--resume`, `-r` | Resume a conversation by session ID | `claude --resume abc123` |
213
+ | `--continue`, `-c` | Continue the most recent conversation | `claude --continue` |
214
+ | `--verbose` | Enable verbose logging | `claude --verbose` |
215
+ | `--max-turns` | Limit agentic turns in non-interactive mode | `claude --max-turns 3` |
216
+ | `--system-prompt` | Override system prompt (only with `--print`) | `claude --system-prompt "Custom instruction"` |
217
+ | `--append-system-prompt` | Append to system prompt (only with `--print`) | `claude --append-system-prompt "Custom instruction"` |
218
+ | `--allowedTools` | Comma/space-separated list of allowed tools (includes MCP tools) | `claude --allowedTools "Bash(npm install),mcp__filesystem__*"` |
219
+ | `--disallowedTools` | Comma/space-separated list of denied tools | `claude --disallowedTools "Bash(git commit),mcp__github__*"` |
220
+ | `--mcp-config` | Load MCP servers from a JSON file | `claude --mcp-config servers.json` |
221
+ | `--permission-prompt-tool` | MCP tool for handling permission prompts (only with `--print`) | `claude --permission-prompt-tool mcp__auth__prompt` |
222
+
223
+ For a complete list of CLI options and features, see the [CLI usage](/en/docs/claude-code/cli-usage) documentation.
224
+
225
+ ## Output formats
226
+
227
+ The SDK supports multiple output formats:
228
+
229
+ ### Text output (default)
230
+
231
+ Returns just the response text:
232
+
233
+ ```bash
234
+ $ claude -p "Explain file src/components/Header.tsx"
235
+ # Output: This is a React component showing...
236
+ ```
237
+
238
+ ### JSON output
239
+
240
+ Returns structured data including metadata:
241
+
242
+ ```bash
243
+ $ claude -p "How does the data layer work?" --output-format json
244
+ ```
245
+
246
+ Response format:
247
+
248
+ ```json
249
+ {
250
+ "type": "result",
251
+ "subtype": "success",
252
+ "cost_usd": 0.003,
253
+ "is_error": false,
254
+ "duration_ms": 1234,
255
+ "duration_api_ms": 800,
256
+ "num_turns": 6,
257
+ "result": "The response text here...",
258
+ "session_id": "abc123"
259
+ }
260
+ ```
261
+
262
+ ### Streaming JSON output
263
+
264
+ Streams each message as it is received:
265
+
266
+ ```bash
267
+ $ claude -p "Build an application" --output-format stream-json
268
+ ```
269
+
270
+ Each conversation begins with an initial `init` system message, followed by a list of user and assistant messages, followed by a final `result` system message with stats. Each message is emitted as a separate JSON object.
271
+
272
+ ## Message schema
273
+
274
+ Messages returned from the JSON API are strictly typed according to the following schema:
275
+
276
+ ```ts
277
+ type SDKMessage =
278
+ // An assistant message
279
+ | {
280
+ type: "assistant";
281
+ message: Message; // from Anthropic SDK
282
+ session_id: string;
283
+ }
284
+
285
+ // A user message
286
+ | {
287
+ type: "user";
288
+ message: MessageParam; // from Anthropic SDK
289
+ session_id: string;
290
+ }
291
+
292
+ // Emitted as the last message
293
+ | {
294
+ type: "result";
295
+ subtype: "success";
296
+ cost_usd: float;
297
+ duration_ms: float;
298
+ duration_api_ms: float;
299
+ is_error: boolean;
300
+ num_turns: int;
301
+ result: string;
302
+ session_id: string;
303
+ }
304
+
305
+ // Emitted as the last message, when we've reached the maximum number of turns
306
+ | {
307
+ type: "result";
308
+ subtype: "error_max_turns";
309
+ cost_usd: float;
310
+ duration_ms: float;
311
+ duration_api_ms: float;
312
+ is_error: boolean;
313
+ num_turns: int;
314
+ session_id: string;
315
+ }
316
+
317
+ // Emitted as the first message at the start of a conversation
318
+ | {
319
+ type: "system";
320
+ subtype: "init";
321
+ session_id: string;
322
+ tools: string[];
323
+ mcp_servers: {
324
+ name: string;
325
+ status: string;
326
+ }[];
327
+ };
328
+ ```
329
+
330
+ We will soon publish these types in a JSONSchema-compatible format. We use semantic versioning for the main Claude Code package to communicate breaking changes to this format.
331
+
332
+ `Message` and `MessageParam` types are available in Anthropic SDKs. For example, see the Anthropic [TypeScript](https://github.com/anthropics/anthropic-sdk-typescript) and [Python](https://github.com/anthropics/anthropic-sdk-python/) SDKs.
333
+
334
+ ## Examples
335
+
336
+ ### Simple script integration
337
+
338
+ ```bash
339
+ #!/bin/bash
340
+
341
+ # Simple function to run Claude and check exit code
342
+ run_claude() {
343
+ local prompt="$1"
344
+ local output_format="${2:-text}"
345
+
346
+ if claude -p "$prompt" --output-format "$output_format"; then
347
+ echo "Success!"
348
+ else
349
+ echo "Error: Claude failed with exit code $?" >&2
350
+ return 1
351
+ fi
352
+ }
353
+
354
+ # Usage examples
355
+ run_claude "Write a Python function to read CSV files"
356
+ run_claude "Optimize this database query" "json"
357
+ ```
358
+
359
+ ### Processing files with Claude
360
+
361
+ ```bash
362
+ # Process a file through Claude
363
+ $ cat mycode.py | claude -p "Review this code for bugs"
364
+
365
+ # Process multiple files
366
+ $ for file in *.js; do
367
+ echo "Processing $file..."
368
+ claude -p "Add JSDoc comments to this file:" < "$file" > "${file}.documented"
369
+ done
370
+
371
+ # Use Claude in a pipeline
372
+ $ grep -l "TODO" *.py | while read file; do
373
+ claude -p "Fix all TODO items in this file" < "$file"
374
+ done
375
+ ```
376
+
377
+ ### Session management
378
+
379
+ ```bash
380
+ # Start a session and capture the session ID
381
+ $ claude -p "Initialize a new project" --output-format json | jq -r '.session_id' > session.txt
382
+
383
+ # Continue with the same session
384
+ $ claude -p --resume "$(cat session.txt)" "Add unit tests"
385
+ ```
386
+
387
+ ## Best practices
388
+
389
+ 1. **Use JSON output format** for programmatic parsing of responses:
390
+
391
+ ```bash
392
+ # Parse JSON response with jq
393
+ result=$(claude -p "Generate code" --output-format json)
394
+ code=$(echo "$result" | jq -r '.result')
395
+ cost=$(echo "$result" | jq -r '.cost_usd')
396
+ ```
397
+
398
+ 2. **Handle errors gracefully** - check exit codes and stderr:
399
+
400
+ ```bash
401
+ if ! claude -p "$prompt" 2>error.log; then
402
+ echo "Error occurred:" >&2
403
+ cat error.log >&2
404
+ exit 1
405
+ fi
406
+ ```
407
+
408
+ 3. **Use session management** for maintaining context in multi-turn conversations
409
+
410
+ 4. **Consider timeouts** for long-running operations:
411
+
412
+ ```bash
413
+ timeout 300 claude -p "$complex_prompt" || echo "Timed out after 5 minutes"
414
+ ```
415
+
416
+ 5. **Respect rate limits** when making multiple requests by adding delays between calls
417
+
418
+ ## Real-world applications
419
+
420
+ The Claude Code SDK enables powerful integrations with your development workflow. One notable example is the [Claude Code GitHub Actions](/en/docs/claude-code/github-actions), which uses the SDK to provide automated code review, PR creation, and issue triage capabilities directly in your GitHub workflow.
421
+
422
+ ## Related resources
423
+
424
+ * [CLI usage and controls](/en/docs/claude-code/cli-usage) - Complete CLI documentation
425
+ * [GitHub Actions integration](/en/docs/claude-code/github-actions) - Automate your GitHub workflow with Claude
426
+ * [Tutorials](/en/docs/claude-code/tutorials) - Step-by-step guides for common use cases
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: claude_swarm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Arruda
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-05-30 00:00:00.000000000 Z
10
+ date: 2025-06-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: thor
@@ -67,7 +67,11 @@ files:
67
67
  - lib/claude_swarm/configuration.rb
68
68
  - lib/claude_swarm/mcp_generator.rb
69
69
  - lib/claude_swarm/orchestrator.rb
70
+ - lib/claude_swarm/reset_session_tool.rb
71
+ - lib/claude_swarm/session_info_tool.rb
72
+ - lib/claude_swarm/task_tool.rb
70
73
  - lib/claude_swarm/version.rb
74
+ - sdk-docs.md
71
75
  homepage: https://github.com/parruda/claude-swarm
72
76
  licenses: []
73
77
  metadata: