agent-mcp 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl
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.
- agent_mcp/__init__.py +2 -2
- agent_mcp/camel_mcp_adapter.py +521 -0
- agent_mcp/cli.py +47 -0
- agent_mcp/heterogeneous_group_chat.py +412 -38
- agent_mcp/langchain_mcp_adapter.py +176 -43
- agent_mcp/mcp_agent.py +26 -0
- agent_mcp/mcp_transport.py +11 -5
- {agent_mcp-0.1.3.dist-info → agent_mcp-0.1.4.dist-info}/METADATA +6 -4
- agent_mcp-0.1.4.dist-info/RECORD +49 -0
- {agent_mcp-0.1.3.dist-info → agent_mcp-0.1.4.dist-info}/WHEEL +1 -1
- agent_mcp-0.1.4.dist-info/entry_points.txt +2 -0
- agent_mcp-0.1.4.dist-info/top_level.txt +3 -0
- demos/__init__.py +1 -0
- demos/basic/__init__.py +1 -0
- demos/basic/framework_examples.py +108 -0
- demos/basic/langchain_camel_demo.py +272 -0
- demos/basic/simple_chat.py +355 -0
- demos/basic/simple_integration_example.py +51 -0
- demos/collaboration/collaborative_task_example.py +437 -0
- demos/collaboration/group_chat_example.py +130 -0
- demos/collaboration/simplified_crewai_example.py +39 -0
- demos/langgraph/autonomous_langgraph_network.py +808 -0
- demos/langgraph/langgraph_agent_network.py +415 -0
- demos/langgraph/langgraph_collaborative_task.py +619 -0
- demos/langgraph/langgraph_example.py +227 -0
- demos/langgraph/run_langgraph_examples.py +213 -0
- demos/network/agent_network_example.py +381 -0
- demos/network/email_agent.py +130 -0
- demos/network/email_agent_demo.py +46 -0
- demos/network/heterogeneous_network_example.py +216 -0
- demos/network/multi_framework_example.py +199 -0
- demos/utils/check_imports.py +49 -0
- demos/workflows/autonomous_agent_workflow.py +248 -0
- demos/workflows/mcp_features_demo.py +353 -0
- demos/workflows/run_agent_collaboration_demo.py +63 -0
- demos/workflows/run_agent_collaboration_with_logs.py +396 -0
- demos/workflows/show_agent_interactions.py +107 -0
- demos/workflows/simplified_autonomous_demo.py +74 -0
- functions/main.py +144 -0
- functions/mcp_network_server.py +513 -0
- functions/utils.py +47 -0
- agent_mcp-0.1.3.dist-info/RECORD +0 -18
- agent_mcp-0.1.3.dist-info/entry_points.txt +0 -2
- agent_mcp-0.1.3.dist-info/top_level.txt +0 -1
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Network Example using MCPAgent.
|
|
3
|
+
|
|
4
|
+
This example demonstrates a network of agents that can communicate with each other
|
|
5
|
+
and share context, creating a simple agent social network. The user can interact
|
|
6
|
+
with any agent in the network, and agents can call other agents as tools.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
from typing import Dict, List, Any, Optional
|
|
12
|
+
import time
|
|
13
|
+
|
|
14
|
+
# Import AutoGen components and MCPAgent
|
|
15
|
+
from autogen import UserProxyAgent
|
|
16
|
+
from agent_mcp import MCPAgent
|
|
17
|
+
|
|
18
|
+
# Check for API key
|
|
19
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
20
|
+
if not api_key:
|
|
21
|
+
raise ValueError("Please set the OPENAI_API_KEY environment variable")
|
|
22
|
+
|
|
23
|
+
# LLM configuration - using GPT-3.5 for faster responses, but can be switched to GPT-4
|
|
24
|
+
config = {
|
|
25
|
+
"config_list": [{"model": "gpt-3.5-turbo", "api_key": api_key}],
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Define agent specialties and personalities
|
|
29
|
+
AGENT_PROFILES = {
|
|
30
|
+
"coordinator": {
|
|
31
|
+
"name": "Coordinator",
|
|
32
|
+
"system_message": """You are the Coordinator agent who manages the network.
|
|
33
|
+
You can connect agents, share information, and help route messages to the right specialist.
|
|
34
|
+
You maintain a global view of the agent network and its capabilities.
|
|
35
|
+
Always be helpful, concise, and informative.""",
|
|
36
|
+
"specialty": "coordination",
|
|
37
|
+
"connections": ["researcher", "analyst", "creative", "planner"]
|
|
38
|
+
},
|
|
39
|
+
"researcher": {
|
|
40
|
+
"name": "Researcher",
|
|
41
|
+
"system_message": """You are the Researcher agent who specializes in finding information.
|
|
42
|
+
You love discovering facts, searching for evidence, and sharing your knowledge.
|
|
43
|
+
You're methodical, detail-oriented, and cite sources when possible.
|
|
44
|
+
Always be informative, thorough, and accurate.""",
|
|
45
|
+
"specialty": "research",
|
|
46
|
+
"connections": ["coordinator", "analyst"]
|
|
47
|
+
},
|
|
48
|
+
"analyst": {
|
|
49
|
+
"name": "Analyst",
|
|
50
|
+
"system_message": """You are the Analyst agent who excels at interpreting data.
|
|
51
|
+
You can evaluate information, identify patterns, and provide insights.
|
|
52
|
+
You're logical, critical, and good at understanding implications.
|
|
53
|
+
Always be analytical, balanced, and data-driven.""",
|
|
54
|
+
"specialty": "analysis",
|
|
55
|
+
"connections": ["coordinator", "researcher", "planner"]
|
|
56
|
+
},
|
|
57
|
+
"creative": {
|
|
58
|
+
"name": "Creative",
|
|
59
|
+
"system_message": """You are the Creative agent who generates innovative ideas.
|
|
60
|
+
You can think outside the box, create content, and suggest novel approaches.
|
|
61
|
+
You're imaginative, artistic, and full of unique perspectives.
|
|
62
|
+
Always be original, expressive, and inspirational.""",
|
|
63
|
+
"specialty": "creativity",
|
|
64
|
+
"connections": ["coordinator", "planner"]
|
|
65
|
+
},
|
|
66
|
+
"planner": {
|
|
67
|
+
"name": "Planner",
|
|
68
|
+
"system_message": """You are the Planner agent who designs strategies and organizes tasks.
|
|
69
|
+
You can create roadmaps, set milestones, and optimize workflows.
|
|
70
|
+
You're structured, forward-thinking, and efficient.
|
|
71
|
+
Always be practical, organized, and goal-oriented.""",
|
|
72
|
+
"specialty": "planning",
|
|
73
|
+
"connections": ["coordinator", "analyst", "creative"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
class AgentNetwork:
|
|
78
|
+
"""A network of MCPAgents that can interact with each other and the user."""
|
|
79
|
+
|
|
80
|
+
def __init__(self):
|
|
81
|
+
self.agents = {}
|
|
82
|
+
self.user = None
|
|
83
|
+
self.current_topic = None
|
|
84
|
+
|
|
85
|
+
def create_network(self):
|
|
86
|
+
"""Create all agents in the network and connect them.
|
|
87
|
+
|
|
88
|
+
This method initializes all agents defined in AGENT_PROFILES, creates a user proxy agent,
|
|
89
|
+
and establishes connections between agents based on their defined relationships.
|
|
90
|
+
Each agent is registered as a tool for its connected agents.
|
|
91
|
+
"""
|
|
92
|
+
# First create all agents
|
|
93
|
+
for agent_id, profile in AGENT_PROFILES.items():
|
|
94
|
+
agent = MCPAgent(
|
|
95
|
+
name=profile["name"],
|
|
96
|
+
system_message=profile["system_message"],
|
|
97
|
+
llm_config=config,
|
|
98
|
+
human_input_mode="NEVER" # We'll handle human input separately
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Add agent-specific context
|
|
102
|
+
agent.update_context("profile", {
|
|
103
|
+
"specialty": profile["specialty"],
|
|
104
|
+
"connections": profile["connections"]
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
self.agents[agent_id] = agent
|
|
108
|
+
print(f"Created agent: {profile['name']} ({agent_id})")
|
|
109
|
+
|
|
110
|
+
# Create user proxy for human interaction
|
|
111
|
+
self.user = UserProxyAgent(
|
|
112
|
+
name="User",
|
|
113
|
+
human_input_mode="ALWAYS",
|
|
114
|
+
max_consecutive_auto_reply=0
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Now connect agents to each other
|
|
118
|
+
for agent_id, profile in AGENT_PROFILES.items():
|
|
119
|
+
agent = self.agents[agent_id]
|
|
120
|
+
|
|
121
|
+
# Register each connected agent as a tool
|
|
122
|
+
for connection_id in profile["connections"]:
|
|
123
|
+
if connection_id in self.agents:
|
|
124
|
+
connected_agent = self.agents[connection_id]
|
|
125
|
+
agent.register_agent_as_tool(connected_agent)
|
|
126
|
+
print(f"Connected {agent.name} to {connected_agent.name}")
|
|
127
|
+
|
|
128
|
+
print("\nAgent network created successfully!")
|
|
129
|
+
|
|
130
|
+
def set_topic(self, topic):
|
|
131
|
+
"""Set a topic for discussion in the network.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
topic: The topic to be discussed across the network.
|
|
135
|
+
|
|
136
|
+
This method updates the context of all agents with the new topic
|
|
137
|
+
and its timestamp for synchronized discussions.
|
|
138
|
+
"""
|
|
139
|
+
self.current_topic = topic
|
|
140
|
+
|
|
141
|
+
# Share the topic with all agents
|
|
142
|
+
for agent_id, agent in self.agents.items():
|
|
143
|
+
agent.update_context("current_topic", {
|
|
144
|
+
"title": topic,
|
|
145
|
+
"timestamp": time.time()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
print(f"\nTopic set: {topic}")
|
|
149
|
+
|
|
150
|
+
def interact_with_agent(self, agent_id):
|
|
151
|
+
"""Allow the user to interact with a specific agent.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
agent_id: The identifier of the agent to interact with.
|
|
155
|
+
|
|
156
|
+
This method enables direct conversation with a chosen agent,
|
|
157
|
+
supports topic-aware discussions, and allows switching between agents.
|
|
158
|
+
Type 'exit' to end conversation or 'switch:agent_id' to change agents.
|
|
159
|
+
"""
|
|
160
|
+
if agent_id not in self.agents:
|
|
161
|
+
print(f"Agent '{agent_id}' not found. Available agents: {', '.join(self.agents.keys())}")
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
agent = self.agents[agent_id]
|
|
165
|
+
print(f"\n--- Starting interaction with {agent.name} ({agent_id}) ---")
|
|
166
|
+
|
|
167
|
+
if self.current_topic:
|
|
168
|
+
print(f"Current topic: {self.current_topic}")
|
|
169
|
+
|
|
170
|
+
# Get initial message from user
|
|
171
|
+
initial_message = input(f"\nYour message to {agent.name}: ")
|
|
172
|
+
|
|
173
|
+
# Create a conversation chain that includes the agent's context
|
|
174
|
+
messages = [{"role": "user", "content": initial_message}]
|
|
175
|
+
|
|
176
|
+
# Get agent response
|
|
177
|
+
response = agent.generate_reply(messages=messages, sender=self.user)
|
|
178
|
+
print(f"\n{agent.name}: {response}")
|
|
179
|
+
|
|
180
|
+
# Continue the conversation until user exits
|
|
181
|
+
while True:
|
|
182
|
+
# Check if user wants to exit
|
|
183
|
+
next_message = input("\nYour response (or type 'exit' to end, 'switch:agent_id' to change agents): ")
|
|
184
|
+
|
|
185
|
+
if next_message.lower() == 'exit':
|
|
186
|
+
print(f"--- Ending interaction with {agent.name} ---")
|
|
187
|
+
break
|
|
188
|
+
|
|
189
|
+
if next_message.lower().startswith('switch:'):
|
|
190
|
+
new_agent_id = next_message.split(':', 1)[1].strip()
|
|
191
|
+
print(f"--- Switching from {agent.name} to {new_agent_id} ---")
|
|
192
|
+
self.interact_with_agent(new_agent_id)
|
|
193
|
+
break
|
|
194
|
+
|
|
195
|
+
# Add to messages and get response
|
|
196
|
+
messages.append({"role": "user", "content": next_message})
|
|
197
|
+
response = agent.generate_reply(messages=messages, sender=self.user)
|
|
198
|
+
print(f"\n{agent.name}: {response}")
|
|
199
|
+
|
|
200
|
+
# Add agent response to message history
|
|
201
|
+
messages.append({"role": "assistant", "content": response})
|
|
202
|
+
|
|
203
|
+
def share_knowledge(self, from_agent_id, to_agent_id, knowledge_key, knowledge_value):
|
|
204
|
+
"""Share specific knowledge from one agent to another.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
from_agent_id: The source agent's identifier
|
|
208
|
+
to_agent_id: The target agent's identifier
|
|
209
|
+
knowledge_key: The key under which to store the knowledge
|
|
210
|
+
knowledge_value: The knowledge content to share
|
|
211
|
+
|
|
212
|
+
This method enables direct knowledge transfer between agents
|
|
213
|
+
by updating the target agent's context with specified information.
|
|
214
|
+
"""
|
|
215
|
+
if from_agent_id not in self.agents or to_agent_id not in self.agents:
|
|
216
|
+
print("One or both agent IDs are invalid.")
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
# Get the agents
|
|
220
|
+
from_agent = self.agents[from_agent_id]
|
|
221
|
+
to_agent = self.agents[to_agent_id]
|
|
222
|
+
|
|
223
|
+
# Share the knowledge
|
|
224
|
+
to_agent.update_context(knowledge_key, knowledge_value)
|
|
225
|
+
|
|
226
|
+
print(f"Shared knowledge '{knowledge_key}' from {from_agent.name} to {to_agent.name}")
|
|
227
|
+
|
|
228
|
+
def broadcast_message(self, from_agent_id, message):
|
|
229
|
+
"""Broadcast a message from one agent to all connected agents.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
from_agent_id: The broadcasting agent's identifier
|
|
233
|
+
message: The message content to broadcast
|
|
234
|
+
|
|
235
|
+
This method sends a message to all agents connected to the source agent,
|
|
236
|
+
storing it in their contexts with timestamp information.
|
|
237
|
+
"""
|
|
238
|
+
if from_agent_id not in self.agents:
|
|
239
|
+
print(f"Agent '{from_agent_id}' not found.")
|
|
240
|
+
return
|
|
241
|
+
|
|
242
|
+
from_agent = self.agents[from_agent_id]
|
|
243
|
+
profile = AGENT_PROFILES[from_agent_id]
|
|
244
|
+
|
|
245
|
+
# Send to all connected agents
|
|
246
|
+
for connection_id in profile["connections"]:
|
|
247
|
+
if connection_id in self.agents:
|
|
248
|
+
to_agent = self.agents[connection_id]
|
|
249
|
+
|
|
250
|
+
# Create a message in the agent's context
|
|
251
|
+
message_key = f"message_from_{from_agent_id}_{int(time.time())}"
|
|
252
|
+
message_value = {
|
|
253
|
+
"from": from_agent.name,
|
|
254
|
+
"content": message,
|
|
255
|
+
"timestamp": time.time()
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
to_agent.update_context(message_key, message_value)
|
|
259
|
+
print(f"Broadcast message from {from_agent.name} to {to_agent.name}")
|
|
260
|
+
|
|
261
|
+
def list_agents(self):
|
|
262
|
+
"""List all agents in the network with their specialties.
|
|
263
|
+
|
|
264
|
+
This method prints a directory of all agents in the network,
|
|
265
|
+
showing their names, IDs, and specialties for easy reference.
|
|
266
|
+
"""
|
|
267
|
+
print("\n--- Agent Network Directory ---")
|
|
268
|
+
for agent_id, agent in self.agents.items():
|
|
269
|
+
profile = agent.get_context("profile")
|
|
270
|
+
specialty = profile.get("specialty") if profile else "unknown"
|
|
271
|
+
print(f"- {agent.name} ({agent_id}): {specialty}")
|
|
272
|
+
|
|
273
|
+
def get_context(self, agent_id, key):
|
|
274
|
+
"""Get a context value from an agent.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
agent_id: The identifier of the agent
|
|
278
|
+
key: The context key to retrieve
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
The context value if found, None otherwise
|
|
282
|
+
"""
|
|
283
|
+
if agent_id not in self.agents:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
agent = self.agents[agent_id]
|
|
287
|
+
return agent.get_context(key)
|
|
288
|
+
|
|
289
|
+
def interact_with_agent_programmatically(self, agent_id, message):
|
|
290
|
+
"""
|
|
291
|
+
Interact with an agent programmatically without requiring user input.
|
|
292
|
+
|
|
293
|
+
This method is similar to interact_with_agent but allows for automation
|
|
294
|
+
in scripts and demonstrations.
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
agent_id: The ID of the agent to interact with
|
|
298
|
+
message: The message to send to the agent
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
The agent's response as a string
|
|
302
|
+
"""
|
|
303
|
+
if agent_id not in self.agents:
|
|
304
|
+
print(f"Agent '{agent_id}' not found in the network.")
|
|
305
|
+
return "Error: Agent not found"
|
|
306
|
+
|
|
307
|
+
agent = self.agents[agent_id]
|
|
308
|
+
|
|
309
|
+
# Include current topic in the context if available
|
|
310
|
+
if self.current_topic:
|
|
311
|
+
# Check if agent has the current_topic in context
|
|
312
|
+
if not agent.has_context("current_topic"):
|
|
313
|
+
agent.update_context("current_topic", self.current_topic)
|
|
314
|
+
|
|
315
|
+
# Format the user message
|
|
316
|
+
print(f"\nMessage to {agent.name}: {message}")
|
|
317
|
+
|
|
318
|
+
# Send message to the agent
|
|
319
|
+
response = agent.generate_reply(
|
|
320
|
+
messages=[{"role": "user", "content": message}]
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
print(f"\n{agent.name}: {response}")
|
|
324
|
+
return response
|
|
325
|
+
|
|
326
|
+
def main():
|
|
327
|
+
"""Run the agent network example."""
|
|
328
|
+
print("=== Agent Network Example ===")
|
|
329
|
+
print("Creating a network of specialized agents that can communicate with each other.")
|
|
330
|
+
|
|
331
|
+
# Create the agent network
|
|
332
|
+
network = AgentNetwork()
|
|
333
|
+
network.create_network()
|
|
334
|
+
|
|
335
|
+
# Main interaction loop
|
|
336
|
+
while True:
|
|
337
|
+
print("\n=== Agent Network Menu ===")
|
|
338
|
+
print("1. List all agents")
|
|
339
|
+
print("2. Set a discussion topic")
|
|
340
|
+
print("3. Talk to an agent")
|
|
341
|
+
print("4. Share knowledge between agents")
|
|
342
|
+
print("5. Broadcast a message")
|
|
343
|
+
print("6. Exit")
|
|
344
|
+
|
|
345
|
+
choice = input("\nSelect an option (1-6): ")
|
|
346
|
+
|
|
347
|
+
if choice == "1":
|
|
348
|
+
network.list_agents()
|
|
349
|
+
|
|
350
|
+
elif choice == "2":
|
|
351
|
+
topic = input("Enter a topic for discussion: ")
|
|
352
|
+
network.set_topic(topic)
|
|
353
|
+
|
|
354
|
+
elif choice == "3":
|
|
355
|
+
network.list_agents()
|
|
356
|
+
agent_id = input("\nEnter the agent ID you want to talk to: ")
|
|
357
|
+
network.interact_with_agent(agent_id)
|
|
358
|
+
|
|
359
|
+
elif choice == "4":
|
|
360
|
+
network.list_agents()
|
|
361
|
+
from_agent = input("\nEnter the source agent ID: ")
|
|
362
|
+
to_agent = input("Enter the target agent ID: ")
|
|
363
|
+
key = input("Enter the knowledge key: ")
|
|
364
|
+
value = input("Enter the knowledge value: ")
|
|
365
|
+
network.share_knowledge(from_agent, to_agent, key, value)
|
|
366
|
+
|
|
367
|
+
elif choice == "5":
|
|
368
|
+
network.list_agents()
|
|
369
|
+
from_agent = input("\nEnter the broadcasting agent ID: ")
|
|
370
|
+
message = input("Enter the message to broadcast: ")
|
|
371
|
+
network.broadcast_message(from_agent, message)
|
|
372
|
+
|
|
373
|
+
elif choice == "6":
|
|
374
|
+
print("Exiting the Agent Network Example. Goodbye!")
|
|
375
|
+
break
|
|
376
|
+
|
|
377
|
+
else:
|
|
378
|
+
print("Invalid option. Please try again.")
|
|
379
|
+
|
|
380
|
+
if __name__ == "__main__":
|
|
381
|
+
main()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Simple Email Agent Example
|
|
2
|
+
|
|
3
|
+
This example shows how to create a simple email agent using just the @mcp_agent decorator.
|
|
4
|
+
The decorator automatically handles all the MCP communication functionality.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import smtplib
|
|
9
|
+
import logging
|
|
10
|
+
import json
|
|
11
|
+
from email.mime.text import MIMEText
|
|
12
|
+
from email.mime.multipart import MIMEMultipart
|
|
13
|
+
from typing import Dict, Any
|
|
14
|
+
from agent_mcp import mcp_agent
|
|
15
|
+
from agent_mcp.langgraph_mcp_adapter import LangGraphMCPAdapter
|
|
16
|
+
from langgraph.graph import StateGraph, START, END
|
|
17
|
+
from dotenv import load_dotenv
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
# Configure logging
|
|
21
|
+
logging.basicConfig(level=logging.INFO)
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
load_dotenv()
|
|
25
|
+
print("Email Agent is starting...")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@mcp_agent(mcp_id="EmailAgent")
|
|
29
|
+
class EmailAgent(LangGraphMCPAdapter):
|
|
30
|
+
"""An agent capable of sending emails through SMTP."""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
"""Initialize email agent with SMTP settings"""
|
|
34
|
+
# Email configuration
|
|
35
|
+
self.smtp_server = os.getenv("SMTP_SERVER", "smtp.gmail.com")
|
|
36
|
+
self.smtp_port = int(os.getenv("SMTP_PORT", "587"))
|
|
37
|
+
self.email_address = os.getenv("EMAIL_ADDRESS")
|
|
38
|
+
self.email_password = os.getenv("EMAIL_PASSWORD")
|
|
39
|
+
|
|
40
|
+
if not all([self.email_address, self.email_password]):
|
|
41
|
+
logger.warning("Email credentials not found in environment variables")
|
|
42
|
+
|
|
43
|
+
# Create workflow using dict for state
|
|
44
|
+
workflow = StateGraph(dict)
|
|
45
|
+
workflow.add_node("send_email", self.send_email)
|
|
46
|
+
workflow.add_edge(START, "send_email")
|
|
47
|
+
workflow.add_edge("send_email", END)
|
|
48
|
+
|
|
49
|
+
# Initialize adapter with workflow but preserve transport
|
|
50
|
+
transport = getattr(self, 'transport', None)
|
|
51
|
+
super().__init__(name="EmailAgent", workflow=workflow, state_type=dict)
|
|
52
|
+
if transport:
|
|
53
|
+
self.transport = transport
|
|
54
|
+
|
|
55
|
+
async def send_email(self, state_dict: dict):
|
|
56
|
+
"""Send an email using configured SMTP settings"""
|
|
57
|
+
try:
|
|
58
|
+
print("Email Agent is about to send email...")
|
|
59
|
+
print(f"State dict received: {state_dict}")
|
|
60
|
+
|
|
61
|
+
# Get the task message (could be in message or content field)
|
|
62
|
+
task = state_dict.get('message') or state_dict.get('content')
|
|
63
|
+
print(f"Task received: {task}")
|
|
64
|
+
|
|
65
|
+
result = None
|
|
66
|
+
if not all([self.email_address, self.email_password]):
|
|
67
|
+
result = "Error: Email credentials not configured"
|
|
68
|
+
return {"message": task, "result": result}
|
|
69
|
+
|
|
70
|
+
# Extract content from task
|
|
71
|
+
if isinstance(task, dict):
|
|
72
|
+
if 'text' in task:
|
|
73
|
+
try:
|
|
74
|
+
content = json.loads(task['text'])
|
|
75
|
+
except json.JSONDecodeError:
|
|
76
|
+
content = task
|
|
77
|
+
else:
|
|
78
|
+
content = task
|
|
79
|
+
else:
|
|
80
|
+
content = task
|
|
81
|
+
|
|
82
|
+
print(f"Content after parsing: {content}")
|
|
83
|
+
|
|
84
|
+
# Extract parameters
|
|
85
|
+
params = content.get('email_params', content)
|
|
86
|
+
to_address = params.get('to_address')
|
|
87
|
+
subject = params.get('subject')
|
|
88
|
+
body = params.get('body') or content.get('content')
|
|
89
|
+
body = re.sub(r'^Subject:.*?\n', '', body, flags=re.IGNORECASE).strip()
|
|
90
|
+
|
|
91
|
+
cc_address = params.get('cc_address')
|
|
92
|
+
|
|
93
|
+
print(f"Extracted params: to={to_address}, subject={subject}, body={body}, cc={cc_address}")
|
|
94
|
+
|
|
95
|
+
if not all([to_address, subject, body]):
|
|
96
|
+
result = "Error: Missing required email parameters"
|
|
97
|
+
logger.error(f"Missing email parameters. Got: {params}")
|
|
98
|
+
return {"message": task, "result": result}
|
|
99
|
+
|
|
100
|
+
# Create message
|
|
101
|
+
msg = MIMEMultipart()
|
|
102
|
+
msg['From'] = self.email_address
|
|
103
|
+
msg['To'] = to_address
|
|
104
|
+
msg['Subject'] = subject
|
|
105
|
+
|
|
106
|
+
if cc_address:
|
|
107
|
+
msg['Cc'] = cc_address
|
|
108
|
+
|
|
109
|
+
# Add body
|
|
110
|
+
msg.attach(MIMEText(body, 'plain'))
|
|
111
|
+
|
|
112
|
+
# Connect to SMTP server
|
|
113
|
+
with smtplib.SMTP(self.smtp_server, self.smtp_port) as server:
|
|
114
|
+
server.starttls()
|
|
115
|
+
server.login(self.email_address, self.email_password)
|
|
116
|
+
|
|
117
|
+
# Send email
|
|
118
|
+
recipients = [to_address]
|
|
119
|
+
if cc_address:
|
|
120
|
+
recipients.append(cc_address)
|
|
121
|
+
server.sendmail(self.email_address, recipients, msg.as_string())
|
|
122
|
+
|
|
123
|
+
logger.info(f"Email sent successfully to {to_address}")
|
|
124
|
+
result = "Email sent successfully"
|
|
125
|
+
return {"message": task, "result": result}
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
result = f"Error sending email: {str(e)}"
|
|
129
|
+
logger.error(f"Error sending email: {e}")
|
|
130
|
+
return {"message": task, "result": result}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Example demonstrating how to create a simple MCP-compatible email agent."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import asyncio
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
from email_agent import EmailAgent
|
|
7
|
+
|
|
8
|
+
# Load environment variables
|
|
9
|
+
load_dotenv()
|
|
10
|
+
|
|
11
|
+
async def demonstrate_email_agent():
|
|
12
|
+
# Create and initialize the email agent
|
|
13
|
+
email_agent = EmailAgent()
|
|
14
|
+
|
|
15
|
+
# Connect to the MCP network
|
|
16
|
+
await email_agent.connect()
|
|
17
|
+
print("Email agent is connected and ready to receive tasks")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Start message and task processors
|
|
21
|
+
email_agent.run()
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
# Wait for interrupt - the adapter handles message processing
|
|
25
|
+
while True:
|
|
26
|
+
await asyncio.sleep(1)
|
|
27
|
+
except KeyboardInterrupt:
|
|
28
|
+
print("\nShutting down email agent...")
|
|
29
|
+
await email_agent.disconnect()
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
# Check for required environment variables
|
|
33
|
+
required_vars = [
|
|
34
|
+
"EMAIL_ADDRESS",
|
|
35
|
+
"EMAIL_PASSWORD",
|
|
36
|
+
"SMTP_SERVER",
|
|
37
|
+
"SMTP_PORT"
|
|
38
|
+
]
|
|
39
|
+
missing_vars = [var for var in required_vars if not os.getenv(var)]
|
|
40
|
+
|
|
41
|
+
if missing_vars:
|
|
42
|
+
print(f"Missing required environment variables: {', '.join(missing_vars)}")
|
|
43
|
+
print("Please set them in your .env file")
|
|
44
|
+
else:
|
|
45
|
+
print("Starting email agent...")
|
|
46
|
+
asyncio.run(demonstrate_email_agent())
|