Integrating mcp.run Servlets with CrewAI
CrewAI is a framework that brings structure and organization to multi-agent AI systems. It lets you create teams of AI agents that work together in a coordinated way, similar to how human teams operate in organizations. Each agent has specific roles and responsibilities, working together through defined processes to accomplish complex tasks.
The framework is built on four key components:
- The Crew - Your management layer that oversees operations and ensures effective collaboration
- AI Agents - Specialized team members with defined roles, tools, and objectives
- Tasks - Well-defined assignments that can be chained together
- Process - The workflow manager that controls task execution and agent collaboration
In this tutorial, we'll build a system that manages zoo operations and social media presence using CrewAI and mcp.run tools. We'll create two agents: a zookeeper who writes engaging stories about the animals, and a social media manager who publishes these stories to WordPress.
Step 1: Pre-requisites
Before starting, ensure you have the following:
- Python, CrewAI, and uv installed on your system
- An OpenAI Account with API access
- An OpenAI API Key
- A GitHub Account for mcp.run authentication
- A WordPress blog. You can either use a free WordPress site hosted on WordPress.com, or you can run one locally using Docker.
Setting up mcp.run
You'll need an mcp.run account and session ID. Here's how to get started:
-
Run this command in your terminal:
npx --yes -p @dylibso/mcpx@latest gen-session
-
Your browser will open to complete authentication through GitHub
-
After authenticating, return to your terminal and save the provided session key
Keep your mcp.run session ID and OpenAI API key secure. Never commit these credentials to code repositories or expose them publicly.
Required Tools
Because our hypothetical Zoo uses WordPress for its website, you’ll need to install the WordPress servlet here: https://www.mcp.run/mhmd-azeez/wordpress
Install the servlet by:
- Visiting each URL
- Clicking the
Install
button - Verifying they appear in your install profile
Step 2: Create a new Crew
The CrewAI docs has a fantastic quick start page which guides you through the steps, but creating a new crew is as simple as running:
crewai create crew awesome_zoo_blogging
Step 3: Setting up mcp.run Integration
First, install the mcpx-py library that handles authentication and tool management:
uv add git+https://github.com/dylibso/mcpx-py
Step 4: Creating the MCPX Tool Wrapper
We need to create a wrapper that converts mcp.run tools into CrewAI-compatible tools. Here's our implementation:
Create a new file called src/tools/mcpx_tools.py
:
from typing import List, Type, Optional, Dict, Any
from pydantic import BaseModel, create_model
from crewai.tools import BaseTool
from mcpx import Client
import json
class MCPTool(BaseTool):
"""Wrapper for mcp.run tools to be used with CrewAI."""
name: str
description: str
_client: Client
_tool_name: str
def _run(self, text: Optional[str] = None, **kwargs) -> str:
"""Execute the mcp.run tool with the provided arguments."""
try:
if text:
try:
input_dict = json.loads(text)
except json.JSONDecodeError:
input_dict = {"text": text}
else:
input_dict = kwargs
# Call the mcp.run tool with the input arguments
results = self._client.call(self._tool_name, input=input_dict)
output = []
for content in results.content:
if content.type == "text":
output.append(content.text)
return "\n".join(output)
except Exception as e:
raise RuntimeError(f"MCPX tool execution failed: {str(e)}")
def get_mcprun_tools(session_id: Optional[str] = None) -> List[BaseTool]:
"""Create CrewAI tools from installed mcp.run tools."""
client = Client(session_id=session_id)
crew_tools = []
for tool_name, tool in client.tools.items():
# Create the Pydantic model from the schema
args_schema = _convert_json_schema_to_pydantic(
tool.input_schema,
f"{tool_name}Schema"
)
# Create the CrewAI tool with the converted schema
crew_tool = MCPTool(
name=tool_name,
description=tool.description,
args_schema=args_schema,
)
crew_tool._client = client
crew_tool._tool_name = tool_name
crew_tools.append(crew_tool)
return crew_tools
def _convert_json_schema_to_pydantic(schema: Dict[str, Any], model_name: str = "DynamicModel") -> Type[BaseModel]:
"""Convert a JSON schema dictionary into a Pydantic model."""
properties = schema.get("properties", {})
required = schema.get("required", [])
fields = {}
for field_name, field_schema in properties.items():
field_type = _get_field_type(field_schema)
# Handle default values correctly
default = field_schema.get("default", None)
if field_name in required:
# Required fields don't get a default
fields[field_name] = (field_type, ...)
else:
# Optional fields with or without default
fields[field_name] = (Optional[field_type], default)
return create_model(model_name, **fields)
def _get_field_type(field_schema: Dict[str, Any]) -> Type:
"""Convert JSON schema type to Python type."""
schema_type = field_schema.get("type", "string")
if schema_type == "array":
items = field_schema.get("items", {})
item_type = _get_field_type(items)
return List[item_type]
elif schema_type == "object":
# Handle nested objects by creating a new model
return _convert_json_schema_to_pydantic(field_schema, "NestedModel")
# Basic type mapping
type_mapping = {
"string": str,
"integer": int,
"number": float,
"boolean": bool,
}
return type_mapping.get(schema_type, str)
Step 5: Defining Our Agents
Create a config/agents.yaml
file:
zookeeper:
role: Zookeeper
goal: Manage the daily operations of the zoo
backstory: >
You are a seasoned zookeeper with a passion for wildlife conservation. You have
experience caring for a wide range of animals, from large mammals to exotic birds.
Your responsibilities include feeding, cleaning, and monitoring the health of the
animals under your care. You also interact with visitors, providing educational
information about the animals and their habitats.
social_media_manager:
role: Social Media Manager
goal: Increase engagement and brand awareness on social media platforms
backstory: >
You are a social media guru with a knack for creating engaging content that
resonates with your audience. You have experience managing social media accounts
for various brands and organizations, using data-driven strategies to optimize
reach and engagement. Your goal is to grow the zoo's online presence and foster
a sense of community among its followers. You also can publish directly to WordPress.
Step 6: Defining Tasks
Create a config/tasks.yaml
file:
write_interesting_stories_task:
description: >
Write an engaging zoo update that captures the week's most fascinating stories
and memorable moments. Share stand-out animal moments that show off their unique
personalities. Include visitor interactions, keeper stories, and facility updates.
Focus on interesting details that would surprise and delight readers. Keep the tone
friendly and conversational. Aim for 250 words.
expected_output: 5 engaging stories/updates that summarize the day.
agent: zookeeper
publish_blog_posts_task:
description: >
Publish the stories as one blog post to the zoo's WordPress site. Check recent
posts to avoid content duplication. Set status to "published" directly.
expected_output: A live blog post series showcasing the zoo's animals.
agent: social_media_manager
Step 7: Creating the Crew
Create your crew.py
:
from crewai import Agent, Crew, Process, Task
from crewai.project import CrewBase, agent, crew, task
from awesome_zoo_blogging.tools.mcpx_tools import get_mcprun_tools
mcpx_tools = get_mcprun_tools()
@CrewBase
class AwesomeZooBlogging():
"""Zoo management crew"""
agents_config = 'config/agents.yaml'
tasks_config = 'config/tasks.yaml'
@agent
def zookeeper(self) -> Agent:
return Agent(
config=self.agents_config['zookeeper'],
verbose=True
)
@agent
def social_media_manager(self) -> Agent:
return Agent(
config=self.agents_config['social_media_manager'],
verbose=True,
tools=mcpx_tools # Give access to mcp.run tools
)
@task
def write_interesting_stories_task(self) -> Task:
return Task(
config=self.tasks_config['write_interesting_stories_task']
)
@task
def publish_blog_posts_task(self) -> Task:
return Task(
config=self.tasks_config['publish_blog_posts_task']
)
@crew
def crew(self) -> Crew:
return Crew(
agents=self.agents,
tasks=self.tasks,
process=Process.sequential,
verbose=True
)
💡 Note: Currently,
get_mcprun_tools
returns all tools installed to your account's default profile. Future updates will support multiple profiles with different tool configurations.
You can find the complete code for this example at: mhmd-azeez/crewai-mcprun
Step 8: Run the crew
$ crewai run
This should run the two agents that each process their own tasks, in the end, you should have a new blog post in your WordPress website.
Support
If you get stuck and need some help, please reach out! Visit our support page to learn how best to get in touch.