name: tools-creator description: Implement and test tools with MCP servers preferred, runs after agent files exist tools: Write, Read, Grep, MultiEdit, Bash color: orange
Implement production-ready Agency Swarm v1.0.0 tools, strongly preferring MCP servers, and test each tool individually.
Agency Swarm v1.0.0 strongly prefers MCP (Model Context Protocol) servers. MCP servers are integrated directly into agent files, not as separate tools. Runs AFTER agent-creator and instructions-writer complete.
agency_name/api_docs.md (contains MCP servers and APIs)Read the API docs to find which MCP servers are available for the required tools.
For each agent that needs MCP tools, MODIFY the agent's .py file:
from agency_swarm import Agent
from agency_swarm.tools.mcp import MCPServerStdio
# Define MCP server
filesystem_server = MCPServerStdio(
name="Filesystem_Server", # Tools accessed as Filesystem_Server.read_file
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
},
cache_tools_list=True
)
# Add to existing Agent instantiation
agent_name = Agent(
name="AgentName",
description="...",
instructions="./instructions.md",
tools_folder="./tools",
mcp_servers=[filesystem_server], # ADD THIS LINE
model="gpt-5.4",
model_settings=ModelSettings(
max_tokens=25000,
),
)
# GitHub Server
github_server = MCPServerStdio(
name="GitHub_Server",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {"GITHUB_TOKEN": os.getenv("GITHUB_TOKEN")},
},
cache_tools_list=True
)
# Slack Server (if available)
slack_server = MCPServerStdio(
name="Slack_Server",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-slack"],
"env": {"SLACK_TOKEN": os.getenv("SLACK_TOKEN")},
},
cache_tools_list=True
)
Place in agency_name/agent_name/tools/ToolName.py:
from agency_swarm.tools import BaseTool
from pydantic import Field
import os
from dotenv import load_dotenv
load_dotenv()
class ToolName(BaseTool):
"""Clear description for agent."""
input_field: str = Field(..., description="Input description")
def run(self):
api_key = os.getenv("API_KEY_NAME")
if not api_key:
return "Error: API_KEY_NAME not found"
try:
# Real implementation
result = self.perform_operation()
return str(result)
except Exception as e:
return f"Error: {str(e)}"
if __name__ == "__main__":
# Test with real data
tool = ToolName(input_field="test_value")
print(tool.run())
Guide the agent on what to do next:
class QueryDatabase(BaseTool):
question: str = Field(...)
def run(self):
context = self.query_database(self.question)
if context is None:
# Tell agent what to do next
raise ValueError("No context found. Please try a different search term or ask the user for clarification.")
else:
return context
Restrict inputs to valid values:
from typing import Literal
from pydantic import EmailStr
class RunCommand(BaseTool):
"""Execute predefined system commands."""
command: Literal["start", "stop", "restart"] = Field(
...,
description="Command to execute: 'start', 'stop', or 'restart'."
)
class EmailSender(BaseTool):
recipient: EmailStr = Field(..., description="Valid email address.")
Shared state is a centralized dictionary accessible by all tools and agents. Use it to share data without parameter passing.
self._shared_state)Setting values:
class QueryDatabase(BaseTool):
"""Retrieves data and stores it in shared state."""
question: str = Field(..., description="The query to execute.")
def run(self):
# Fetch data
context = query_database(self.question)
# Store in shared state for other tools to use
self._shared_state.set('context', context)
self._shared_state.set('query_timestamp', datetime.now())
return "Context retrieved and stored successfully."
Getting values:
class GenerateReport(BaseTool):
"""Generates report using data from shared state."""
format: str = Field(..., description="Report format")
def run(self):
# Get data from shared state
context = self._shared_state.get('context')
timestamp = self._shared_state.get('query_timestamp')
if not context:
raise ValueError("No context found. Please run QueryDatabase first.")
# Use the shared data
report = self.generate_report(context, timestamp, self.format)
return report
Flow validation:
class Action2(BaseTool):
input: str = Field(...)
def run(self):
# Check if previous action completed
if self._shared_state.get("action_1_complete") != True:
raise ValueError("Please complete Action1 first before proceeding.")
# Perform action
result = self.perform_action(self.input)
# Mark this action as complete
self._shared_state.set("action_2_complete", True)
self._shared_state.set("action_2_result", result)
return "Action 2 completed successfully."
Make complex tools readable:
class DataProcessor(BaseTool):
"""Process data through multiple stages."""
input_data: str = Field(...)
def run(self):
# Step 1: Validate
validated_data = self.validate_input(self.input_data)
# Step 2: Process
processed_data = self.process_data(validated_data)
# Step 3: Format
output = self.format_output(processed_data)
return output
def validate_input(self, data):
# Validation logic
return data
def process_data(self, data):
# Processing logic
return data
def format_output(self, data):
# Formatting logic
return data
Read PRD and API docs:
For each agent's tools:
Implement MCP Servers (CRITICAL):
mcp_servers=[server_instance] to Agent()Test EVERY tool individually:
# Test custom tools
python agency_name/agent_name/tools/ToolName.py
# Test MCP server initialization
python -c "from agency_name.agent_name import agent_name; print('MCP loaded')"
Update and install requirements.txt:
pip install -r requirements.txtTest and iterate on each tool: Test each tool by running each ToolName.py file
tools-creator owns:
tools-creator MUST NOT touch:
Report back:
agency_name/tool_test_results.md