fast-agent-mcp 0.2.4__py3-none-any.whl → 0.2.6__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.
- {fast_agent_mcp-0.2.4.dist-info → fast_agent_mcp-0.2.6.dist-info}/METADATA +2 -2
- {fast_agent_mcp-0.2.4.dist-info → fast_agent_mcp-0.2.6.dist-info}/RECORD +32 -33
- mcp_agent/agents/agent.py +1 -1
- mcp_agent/agents/base_agent.py +25 -13
- mcp_agent/agents/workflow/chain_agent.py +7 -1
- mcp_agent/agents/workflow/evaluator_optimizer.py +6 -0
- mcp_agent/agents/workflow/orchestrator_agent.py +6 -1
- mcp_agent/agents/workflow/parallel_agent.py +7 -1
- mcp_agent/agents/workflow/router_agent.py +7 -2
- mcp_agent/cli/commands/setup.py +2 -2
- mcp_agent/cli/main.py +11 -0
- mcp_agent/config.py +29 -7
- mcp_agent/context.py +2 -0
- mcp_agent/core/agent_app.py +8 -19
- mcp_agent/core/agent_types.py +1 -0
- mcp_agent/core/direct_decorators.py +2 -1
- mcp_agent/core/direct_factory.py +6 -15
- mcp_agent/core/enhanced_prompt.py +3 -3
- mcp_agent/core/fastagent.py +202 -46
- mcp_agent/core/interactive_prompt.py +1 -1
- mcp_agent/llm/augmented_llm.py +8 -10
- mcp_agent/llm/augmented_llm_passthrough.py +3 -1
- mcp_agent/llm/model_factory.py +5 -7
- mcp_agent/mcp/interfaces.py +5 -0
- mcp_agent/mcp/prompt_serialization.py +42 -0
- mcp_agent/mcp/prompts/prompt_load.py +51 -3
- mcp_agent/mcp_server/agent_server.py +61 -12
- mcp_agent/resources/examples/internal/agent.py +2 -2
- mcp_agent/resources/examples/internal/fastagent.config.yaml +5 -0
- mcp_agent/mcp/mcp_agent_server.py +0 -56
- {fast_agent_mcp-0.2.4.dist-info → fast_agent_mcp-0.2.6.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.4.dist-info → fast_agent_mcp-0.2.6.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.2.4.dist-info → fast_agent_mcp-0.2.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,9 +1,13 @@
|
|
1
1
|
# src/mcp_agent/mcp_server/agent_server.py
|
2
2
|
|
3
|
+
import asyncio
|
4
|
+
|
3
5
|
from mcp.server.fastmcp import Context as MCPContext
|
4
6
|
from mcp.server.fastmcp import FastMCP
|
5
7
|
|
6
|
-
|
8
|
+
import mcp_agent
|
9
|
+
import mcp_agent.core
|
10
|
+
import mcp_agent.core.prompt
|
7
11
|
from mcp_agent.core.agent_app import AgentApp
|
8
12
|
|
9
13
|
|
@@ -14,7 +18,7 @@ class AgentMCPServer:
|
|
14
18
|
self,
|
15
19
|
agent_app: AgentApp,
|
16
20
|
server_name: str = "FastAgent-MCP-Server",
|
17
|
-
server_description: str = None,
|
21
|
+
server_description: str | None = None,
|
18
22
|
) -> None:
|
19
23
|
self.agent_app = agent_app
|
20
24
|
self.mcp_server = FastMCP(
|
@@ -26,28 +30,26 @@ class AgentMCPServer:
|
|
26
30
|
|
27
31
|
def setup_tools(self) -> None:
|
28
32
|
"""Register all agents as MCP tools."""
|
29
|
-
for agent_name,
|
30
|
-
self.register_agent_tools(agent_name,
|
33
|
+
for agent_name, agent in self.agent_app._agents.items():
|
34
|
+
self.register_agent_tools(agent_name, agent)
|
31
35
|
|
32
|
-
def register_agent_tools(self, agent_name: str,
|
36
|
+
def register_agent_tools(self, agent_name: str, agent) -> None:
|
33
37
|
"""Register tools for a specific agent."""
|
34
38
|
|
35
39
|
# Basic send message tool
|
36
40
|
@self.mcp_server.tool(
|
37
|
-
name=f"{agent_name}
|
41
|
+
name=f"{agent_name}_send",
|
38
42
|
description=f"Send a message to the {agent_name} agent",
|
39
43
|
)
|
40
44
|
async def send_message(message: str, ctx: MCPContext) -> str:
|
41
45
|
"""Send a message to the agent and return its response."""
|
42
46
|
|
43
47
|
# Get the agent's context
|
44
|
-
agent_context = None
|
45
|
-
if hasattr(agent_proxy, "_agent") and hasattr(agent_proxy._agent, "context"):
|
46
|
-
agent_context = agent_proxy._agent.context
|
48
|
+
agent_context = getattr(agent, "context", None)
|
47
49
|
|
48
50
|
# Define the function to execute
|
49
51
|
async def execute_send():
|
50
|
-
return await
|
52
|
+
return await agent.send(message)
|
51
53
|
|
52
54
|
# Execute with bridged context
|
53
55
|
if agent_context and ctx:
|
@@ -55,6 +57,25 @@ class AgentMCPServer:
|
|
55
57
|
else:
|
56
58
|
return await execute_send()
|
57
59
|
|
60
|
+
# Register a history prompt for this agent
|
61
|
+
@self.mcp_server.prompt(
|
62
|
+
name=f"{agent_name}_history",
|
63
|
+
description=f"Conversation history for the {agent_name} agent",
|
64
|
+
)
|
65
|
+
async def get_history_prompt() -> list:
|
66
|
+
"""Return the conversation history as MCP messages."""
|
67
|
+
# Get the conversation history from the agent's LLM
|
68
|
+
if not hasattr(agent, "_llm") or agent._llm is None:
|
69
|
+
return []
|
70
|
+
|
71
|
+
# Convert the multipart message history to standard PromptMessages
|
72
|
+
multipart_history = agent._llm.message_history
|
73
|
+
prompt_messages = mcp_agent.core.prompt.Prompt.from_multipart(multipart_history)
|
74
|
+
|
75
|
+
# In FastMCP, we need to return the raw list of messages
|
76
|
+
# that matches the structure that FastMCP expects (list of dicts with role/content)
|
77
|
+
return [{"role": msg.role, "content": msg.content} for msg in prompt_messages]
|
78
|
+
|
58
79
|
def run(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000) -> None:
|
59
80
|
"""Run the MCP server."""
|
60
81
|
if transport == "sse":
|
@@ -71,9 +92,19 @@ class AgentMCPServer:
|
|
71
92
|
if transport == "sse":
|
72
93
|
self.mcp_server.settings.host = host
|
73
94
|
self.mcp_server.settings.port = port
|
74
|
-
|
95
|
+
try:
|
96
|
+
await self.mcp_server.run_sse_async()
|
97
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
98
|
+
# Gracefully handle cancellation during shutdown
|
99
|
+
await self.shutdown()
|
100
|
+
pass
|
75
101
|
else: # stdio
|
76
|
-
|
102
|
+
try:
|
103
|
+
await self.mcp_server.run_stdio_async()
|
104
|
+
except (asyncio.CancelledError, KeyboardInterrupt):
|
105
|
+
# Gracefully handle cancellation during shutdown
|
106
|
+
await self.shutdown()
|
107
|
+
pass
|
77
108
|
|
78
109
|
async def with_bridged_context(self, agent_context, mcp_context, func, *args, **kwargs):
|
79
110
|
"""
|
@@ -115,3 +146,21 @@ class AgentMCPServer:
|
|
115
146
|
# Remove MCP context reference
|
116
147
|
if hasattr(agent_context, "mcp_context"):
|
117
148
|
delattr(agent_context, "mcp_context")
|
149
|
+
|
150
|
+
async def shutdown(self):
|
151
|
+
"""Gracefully shutdown the MCP server and its resources."""
|
152
|
+
# Your MCP server may have additional cleanup code here
|
153
|
+
try:
|
154
|
+
# If your MCP server has a shutdown method, call it
|
155
|
+
if hasattr(self.mcp_server, "shutdown"):
|
156
|
+
await self.mcp_server.shutdown()
|
157
|
+
|
158
|
+
# Clean up any other resources
|
159
|
+
import asyncio
|
160
|
+
|
161
|
+
# Allow any pending tasks to clean up
|
162
|
+
await asyncio.sleep(0.5)
|
163
|
+
except Exception as e:
|
164
|
+
# Just log exceptions during shutdown, don't raise
|
165
|
+
print(f"Error during MCP server shutdown: {e}")
|
166
|
+
pass
|
@@ -7,13 +7,13 @@ fast = FastAgent("FastAgent Example")
|
|
7
7
|
|
8
8
|
|
9
9
|
# Define the agent
|
10
|
-
@fast.agent(servers=["category", "mcp_hfspace"])
|
10
|
+
@fast.agent(servers=["category", "mcp_hfspace","mcp_webcam"])
|
11
11
|
#@fast.agent(name="test")
|
12
12
|
async def main() -> None:
|
13
13
|
# use the --model command line switch or agent arguments to change model
|
14
14
|
async with fast.run() as agent:
|
15
15
|
# await agent.prompt(agent_name="test")
|
16
|
-
await agent
|
16
|
+
await agent()
|
17
17
|
|
18
18
|
|
19
19
|
if __name__ == "__main__":
|
@@ -1,56 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
|
3
|
-
from mcp.server import NotificationOptions
|
4
|
-
from mcp.server.fastmcp import FastMCP
|
5
|
-
from mcp.server.stdio import stdio_server
|
6
|
-
|
7
|
-
from mcp_agent.executor.temporal import get_temporal_client
|
8
|
-
from mcp_agent.telemetry.tracing import setup_tracing
|
9
|
-
|
10
|
-
app = FastMCP("mcp-agent-server")
|
11
|
-
|
12
|
-
setup_tracing("mcp-agent-server")
|
13
|
-
|
14
|
-
|
15
|
-
async def run() -> None:
|
16
|
-
async with stdio_server() as (read_stream, write_stream):
|
17
|
-
await app._mcp_server.run(
|
18
|
-
read_stream,
|
19
|
-
write_stream,
|
20
|
-
app._mcp_server.create_initialization_options(
|
21
|
-
notification_options=NotificationOptions(tools_changed=True, resources_changed=True)
|
22
|
-
),
|
23
|
-
)
|
24
|
-
|
25
|
-
|
26
|
-
@app.tool
|
27
|
-
async def run_workflow(query: str) -> None:
|
28
|
-
"""Run the workflow given its name or id"""
|
29
|
-
pass
|
30
|
-
|
31
|
-
|
32
|
-
@app.tool
|
33
|
-
async def pause_workflow(workflow_id: str) -> None:
|
34
|
-
"""Pause a running workflow."""
|
35
|
-
temporal_client = await get_temporal_client()
|
36
|
-
handle = temporal_client.get_workflow_handle(workflow_id)
|
37
|
-
await handle.signal("pause")
|
38
|
-
|
39
|
-
|
40
|
-
@app.tool
|
41
|
-
async def resume_workflow(workflow_id: str) -> None:
|
42
|
-
"""Resume a paused workflow."""
|
43
|
-
temporal_client = await get_temporal_client()
|
44
|
-
handle = temporal_client.get_workflow_handle(workflow_id)
|
45
|
-
await handle.signal("resume")
|
46
|
-
|
47
|
-
|
48
|
-
async def provide_user_input(workflow_id: str, input_data: str) -> None:
|
49
|
-
"""Provide user/human input to a waiting workflow step."""
|
50
|
-
temporal_client = await get_temporal_client()
|
51
|
-
handle = temporal_client.get_workflow_handle(workflow_id)
|
52
|
-
await handle.signal("human_input", input_data)
|
53
|
-
|
54
|
-
|
55
|
-
if __name__ == "__main__":
|
56
|
-
asyncio.run(run())
|
File without changes
|
File without changes
|
File without changes
|