fast-agent-mcp 0.1.12__py3-none-any.whl → 0.1.13__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.
Files changed (126) hide show
  1. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/METADATA +1 -1
  2. fast_agent_mcp-0.1.13.dist-info/RECORD +164 -0
  3. mcp_agent/agents/agent.py +37 -79
  4. mcp_agent/app.py +16 -22
  5. mcp_agent/cli/commands/bootstrap.py +22 -52
  6. mcp_agent/cli/commands/config.py +4 -4
  7. mcp_agent/cli/commands/setup.py +11 -26
  8. mcp_agent/cli/main.py +6 -9
  9. mcp_agent/cli/terminal.py +2 -2
  10. mcp_agent/config.py +1 -5
  11. mcp_agent/context.py +13 -24
  12. mcp_agent/context_dependent.py +3 -7
  13. mcp_agent/core/agent_app.py +45 -121
  14. mcp_agent/core/agent_utils.py +3 -5
  15. mcp_agent/core/decorators.py +5 -12
  16. mcp_agent/core/enhanced_prompt.py +25 -52
  17. mcp_agent/core/exceptions.py +8 -8
  18. mcp_agent/core/factory.py +29 -70
  19. mcp_agent/core/fastagent.py +48 -88
  20. mcp_agent/core/mcp_content.py +8 -16
  21. mcp_agent/core/prompt.py +8 -15
  22. mcp_agent/core/proxies.py +34 -25
  23. mcp_agent/core/request_params.py +6 -3
  24. mcp_agent/core/types.py +4 -6
  25. mcp_agent/core/validation.py +4 -3
  26. mcp_agent/executor/decorator_registry.py +11 -23
  27. mcp_agent/executor/executor.py +8 -17
  28. mcp_agent/executor/task_registry.py +2 -4
  29. mcp_agent/executor/temporal.py +28 -74
  30. mcp_agent/executor/workflow.py +3 -5
  31. mcp_agent/executor/workflow_signal.py +17 -29
  32. mcp_agent/human_input/handler.py +4 -9
  33. mcp_agent/human_input/types.py +2 -3
  34. mcp_agent/logging/events.py +1 -5
  35. mcp_agent/logging/json_serializer.py +7 -6
  36. mcp_agent/logging/listeners.py +20 -23
  37. mcp_agent/logging/logger.py +15 -17
  38. mcp_agent/logging/rich_progress.py +10 -8
  39. mcp_agent/logging/tracing.py +4 -6
  40. mcp_agent/logging/transport.py +22 -22
  41. mcp_agent/mcp/gen_client.py +4 -12
  42. mcp_agent/mcp/interfaces.py +71 -86
  43. mcp_agent/mcp/mcp_agent_client_session.py +11 -19
  44. mcp_agent/mcp/mcp_agent_server.py +8 -10
  45. mcp_agent/mcp/mcp_aggregator.py +45 -117
  46. mcp_agent/mcp/mcp_connection_manager.py +16 -37
  47. mcp_agent/mcp/prompt_message_multipart.py +12 -18
  48. mcp_agent/mcp/prompt_serialization.py +13 -38
  49. mcp_agent/mcp/prompts/prompt_load.py +99 -0
  50. mcp_agent/mcp/prompts/prompt_server.py +21 -128
  51. mcp_agent/mcp/prompts/prompt_template.py +20 -42
  52. mcp_agent/mcp/resource_utils.py +8 -17
  53. mcp_agent/mcp/sampling.py +5 -14
  54. mcp_agent/mcp/stdio.py +11 -8
  55. mcp_agent/mcp_server/agent_server.py +10 -17
  56. mcp_agent/mcp_server_registry.py +13 -35
  57. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
  58. mcp_agent/resources/examples/data-analysis/analysis.py +1 -1
  59. mcp_agent/resources/examples/data-analysis/slides.py +110 -0
  60. mcp_agent/resources/examples/internal/agent.py +2 -1
  61. mcp_agent/resources/examples/internal/job.py +2 -1
  62. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  63. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  64. mcp_agent/resources/examples/internal/sizer.py +2 -1
  65. mcp_agent/resources/examples/internal/social.py +2 -1
  66. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +1 -1
  67. mcp_agent/resources/examples/prompting/agent.py +2 -1
  68. mcp_agent/resources/examples/prompting/image_server.py +5 -11
  69. mcp_agent/resources/examples/researcher/researcher-eval.py +1 -1
  70. mcp_agent/resources/examples/researcher/researcher-imp.py +3 -4
  71. mcp_agent/resources/examples/researcher/researcher.py +2 -1
  72. mcp_agent/resources/examples/workflows/agent_build.py +2 -1
  73. mcp_agent/resources/examples/workflows/chaining.py +2 -1
  74. mcp_agent/resources/examples/workflows/evaluator.py +2 -1
  75. mcp_agent/resources/examples/workflows/human_input.py +2 -1
  76. mcp_agent/resources/examples/workflows/orchestrator.py +2 -1
  77. mcp_agent/resources/examples/workflows/parallel.py +2 -1
  78. mcp_agent/resources/examples/workflows/router.py +2 -1
  79. mcp_agent/resources/examples/workflows/sse.py +1 -1
  80. mcp_agent/telemetry/usage_tracking.py +2 -1
  81. mcp_agent/ui/console_display.py +15 -39
  82. mcp_agent/workflows/embedding/embedding_base.py +1 -4
  83. mcp_agent/workflows/embedding/embedding_cohere.py +2 -2
  84. mcp_agent/workflows/embedding/embedding_openai.py +4 -13
  85. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +23 -57
  86. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +5 -8
  87. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +7 -11
  88. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +4 -8
  89. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +4 -8
  90. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +11 -22
  91. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +3 -3
  92. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +4 -6
  93. mcp_agent/workflows/llm/anthropic_utils.py +8 -29
  94. mcp_agent/workflows/llm/augmented_llm.py +69 -247
  95. mcp_agent/workflows/llm/augmented_llm_anthropic.py +39 -73
  96. mcp_agent/workflows/llm/augmented_llm_openai.py +42 -97
  97. mcp_agent/workflows/llm/augmented_llm_passthrough.py +13 -20
  98. mcp_agent/workflows/llm/augmented_llm_playback.py +8 -6
  99. mcp_agent/workflows/llm/memory.py +103 -0
  100. mcp_agent/workflows/llm/model_factory.py +8 -20
  101. mcp_agent/workflows/llm/openai_utils.py +1 -1
  102. mcp_agent/workflows/llm/prompt_utils.py +1 -3
  103. mcp_agent/workflows/llm/providers/multipart_converter_anthropic.py +47 -89
  104. mcp_agent/workflows/llm/providers/multipart_converter_openai.py +20 -55
  105. mcp_agent/workflows/llm/providers/openai_multipart.py +19 -61
  106. mcp_agent/workflows/llm/providers/sampling_converter_anthropic.py +10 -12
  107. mcp_agent/workflows/llm/providers/sampling_converter_openai.py +7 -11
  108. mcp_agent/workflows/llm/sampling_converter.py +4 -11
  109. mcp_agent/workflows/llm/sampling_format_converter.py +12 -12
  110. mcp_agent/workflows/orchestrator/orchestrator.py +24 -67
  111. mcp_agent/workflows/orchestrator/orchestrator_models.py +14 -40
  112. mcp_agent/workflows/parallel/fan_in.py +17 -47
  113. mcp_agent/workflows/parallel/fan_out.py +6 -12
  114. mcp_agent/workflows/parallel/parallel_llm.py +9 -26
  115. mcp_agent/workflows/router/router_base.py +19 -49
  116. mcp_agent/workflows/router/router_embedding.py +11 -25
  117. mcp_agent/workflows/router/router_embedding_cohere.py +2 -2
  118. mcp_agent/workflows/router/router_embedding_openai.py +2 -2
  119. mcp_agent/workflows/router/router_llm.py +12 -28
  120. mcp_agent/workflows/swarm/swarm.py +20 -48
  121. mcp_agent/workflows/swarm/swarm_anthropic.py +2 -2
  122. mcp_agent/workflows/swarm/swarm_openai.py +2 -2
  123. fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
  124. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/WHEEL +0 -0
  125. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/entry_points.txt +0 -0
  126. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.1.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,15 @@
1
1
  import base64
2
2
  from pathlib import Path
3
3
  from typing import List, Optional, Tuple
4
+
4
5
  from mcp.types import (
5
- EmbeddedResource,
6
- TextResourceContents,
7
6
  BlobResourceContents,
7
+ EmbeddedResource,
8
8
  ImageContent,
9
+ TextResourceContents,
9
10
  )
10
11
  from pydantic import AnyUrl
12
+
11
13
  import mcp_agent.mcp.mime_utils as mime_utils
12
14
 
13
15
  HTTP_TIMEOUT = 10 # Default timeout for HTTP requests
@@ -25,9 +27,7 @@ def find_resource_file(resource_path: str, prompt_files: List[Path]) -> Optional
25
27
  return None
26
28
 
27
29
 
28
- def load_resource_content(
29
- resource_path: str, prompt_files: List[Path]
30
- ) -> ResourceContent:
30
+ def load_resource_content(resource_path: str, prompt_files: List[Path]) -> ResourceContent:
31
31
  """
32
32
  Load a resource's content and determine its mime type
33
33
 
@@ -71,9 +71,6 @@ def create_resource_uri(path: str) -> str:
71
71
  return f"resource://fast-agent/{Path(path).name}"
72
72
 
73
73
 
74
- # Add this to your resource_utils.py module
75
-
76
-
77
74
  def create_resource_reference(uri: str, mime_type: str) -> "EmbeddedResource":
78
75
  """
79
76
  Create a reference to a resource without embedding its content directly.
@@ -101,9 +98,7 @@ def create_resource_reference(uri: str, mime_type: str) -> "EmbeddedResource":
101
98
  return EmbeddedResource(type="resource", resource=resource_contents)
102
99
 
103
100
 
104
- def create_embedded_resource(
105
- resource_path: str, content: str, mime_type: str, is_binary: bool = False
106
- ) -> EmbeddedResource:
101
+ def create_embedded_resource(resource_path: str, content: str, mime_type: str, is_binary: bool = False) -> EmbeddedResource:
107
102
  """Create an embedded resource content object"""
108
103
  # Format a valid resource URI string
109
104
  resource_uri_str = create_resource_uri(resource_path)
@@ -141,9 +136,7 @@ def create_image_content(data: str, mime_type: str) -> ImageContent:
141
136
  )
142
137
 
143
138
 
144
- def create_blob_resource(
145
- resource_path: str, content: str, mime_type: str
146
- ) -> EmbeddedResource:
139
+ def create_blob_resource(resource_path: str, content: str, mime_type: str) -> EmbeddedResource:
147
140
  """Create an embedded resource for binary data"""
148
141
  return EmbeddedResource(
149
142
  type="resource",
@@ -155,9 +148,7 @@ def create_blob_resource(
155
148
  )
156
149
 
157
150
 
158
- def create_text_resource(
159
- resource_path: str, content: str, mime_type: str
160
- ) -> EmbeddedResource:
151
+ def create_text_resource(resource_path: str, content: str, mime_type: str) -> EmbeddedResource:
161
152
  """Create an embedded resource for text data"""
162
153
  return EmbeddedResource(
163
154
  type="resource",
mcp_agent/mcp/sampling.py CHANGED
@@ -11,15 +11,12 @@ from mcp.types import (
11
11
  from mcp_agent.core.agent_types import AgentConfig
12
12
  from mcp_agent.logging.logger import get_logger
13
13
  from mcp_agent.mcp.interfaces import AugmentedLLMProtocol
14
-
15
14
  from mcp_agent.workflows.llm.sampling_converter import SamplingConverter
16
15
 
17
16
  logger = get_logger(__name__)
18
17
 
19
18
 
20
- def create_sampling_llm(
21
- params: CreateMessageRequestParams, model_string: str
22
- ) -> AugmentedLLMProtocol:
19
+ def create_sampling_llm(params: CreateMessageRequestParams, model_string: str) -> AugmentedLLMProtocol:
23
20
  """
24
21
  Create an LLM instance for sampling without tools support.
25
22
  This utility function creates a minimal LLM instance based on the model string.
@@ -31,8 +28,8 @@ def create_sampling_llm(
31
28
  Returns:
32
29
  An initialized LLM instance ready to use
33
30
  """
34
- from mcp_agent.workflows.llm.model_factory import ModelFactory
35
31
  from mcp_agent.agents.agent import Agent
32
+ from mcp_agent.workflows.llm.model_factory import ModelFactory
36
33
 
37
34
  app_context = None
38
35
  try:
@@ -58,9 +55,7 @@ def create_sampling_llm(
58
55
  return llm
59
56
 
60
57
 
61
- async def sample(
62
- mcp_ctx: ClientSession, params: CreateMessageRequestParams
63
- ) -> CreateMessageResult:
58
+ async def sample(mcp_ctx: ClientSession, params: CreateMessageRequestParams) -> CreateMessageResult:
64
59
  """
65
60
  Handle sampling requests from the MCP protocol using SamplingConverter.
66
61
 
@@ -110,14 +105,10 @@ async def sample(
110
105
  logger.info(f"Complete sampling request : {llm_response[:50]}...")
111
106
 
112
107
  # Create result using our converter
113
- return SamplingConverter.create_message_result(
114
- response=llm_response, model=model
115
- )
108
+ return SamplingConverter.create_message_result(response=llm_response, model=model)
116
109
  except Exception as e:
117
110
  logger.error(f"Error in sampling: {str(e)}")
118
- return SamplingConverter.error_result(
119
- error_message=f"Error in sampling: {str(e)}", model=model
120
- )
111
+ return SamplingConverter.error_result(error_message=f"Error in sampling: {str(e)}", model=model)
121
112
 
122
113
 
123
114
  def sampling_agent_config(
mcp_agent/mcp/stdio.py CHANGED
@@ -2,14 +2,19 @@
2
2
  Custom implementation of stdio_client that handles stderr through rich console.
3
3
  """
4
4
 
5
- from contextlib import asynccontextmanager
6
5
  import subprocess
6
+ from contextlib import asynccontextmanager
7
+ from typing import TYPE_CHECKING
8
+
7
9
  import anyio
10
+ import mcp.types as types
8
11
  from anyio.streams.text import TextReceiveStream
9
12
  from mcp.client.stdio import StdioServerParameters, get_default_environment
10
- import mcp.types as types
13
+
11
14
  from mcp_agent.logging.logger import get_logger
12
- from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
15
+
16
+ if TYPE_CHECKING:
17
+ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
13
18
 
14
19
  logger = get_logger(__name__)
15
20
 
@@ -45,11 +50,9 @@ async def stdio_client_with_rich_stderr(server: StdioServerParameters):
45
50
 
46
51
  if process.returncode is not None:
47
52
  logger.debug(f"return code (early){process.returncode}")
48
- raise RuntimeError(
49
- f"Process terminated immediately with code {process.returncode}"
50
- )
53
+ raise RuntimeError(f"Process terminated immediately with code {process.returncode}")
51
54
 
52
- async def stdout_reader():
55
+ async def stdout_reader() -> None:
53
56
  assert process.stdout, "Opened process is missing stdout"
54
57
  try:
55
58
  async with read_stream_writer:
@@ -89,7 +92,7 @@ async def stdio_client_with_rich_stderr(server: StdioServerParameters):
89
92
  # except anyio.ClosedResourceError:
90
93
  # await anyio.lowlevel.checkpoint()
91
94
 
92
- async def stdin_writer():
95
+ async def stdin_writer() -> None:
93
96
  assert process.stdin, "Opened process is missing stdin"
94
97
  try:
95
98
  async with write_stream_reader:
@@ -1,10 +1,10 @@
1
1
  # src/mcp_agent/mcp_server/agent_server.py
2
2
 
3
+ from mcp.server.fastmcp import Context as MCPContext
3
4
  from mcp.server.fastmcp import FastMCP
4
5
 
5
6
  # Remove circular import
6
7
  from mcp_agent.core.agent_app import AgentApp
7
- from mcp.server.fastmcp import Context as MCPContext
8
8
 
9
9
 
10
10
  class AgentMCPServer:
@@ -15,21 +15,20 @@ class AgentMCPServer:
15
15
  agent_app: AgentApp,
16
16
  server_name: str = "FastAgent-MCP-Server",
17
17
  server_description: str = None,
18
- ):
18
+ ) -> None:
19
19
  self.agent_app = agent_app
20
20
  self.mcp_server = FastMCP(
21
21
  name=server_name,
22
- instructions=server_description
23
- or f"This server provides access to {len(agent_app.agents)} agents",
22
+ instructions=server_description or f"This server provides access to {len(agent_app.agents)} agents",
24
23
  )
25
24
  self.setup_tools()
26
25
 
27
- def setup_tools(self):
26
+ def setup_tools(self) -> None:
28
27
  """Register all agents as MCP tools."""
29
28
  for agent_name, agent_proxy in self.agent_app._agents.items():
30
29
  self.register_agent_tools(agent_name, agent_proxy)
31
30
 
32
- def register_agent_tools(self, agent_name: str, agent_proxy):
31
+ def register_agent_tools(self, agent_name: str, agent_proxy) -> None:
33
32
  """Register tools for a specific agent."""
34
33
 
35
34
  # Basic send message tool
@@ -42,9 +41,7 @@ class AgentMCPServer:
42
41
 
43
42
  # Get the agent's context
44
43
  agent_context = None
45
- if hasattr(agent_proxy, "_agent") and hasattr(
46
- agent_proxy._agent, "context"
47
- ):
44
+ if hasattr(agent_proxy, "_agent") and hasattr(agent_proxy._agent, "context"):
48
45
  agent_context = agent_proxy._agent.context
49
46
 
50
47
  # Define the function to execute
@@ -57,7 +54,7 @@ class AgentMCPServer:
57
54
  else:
58
55
  return await execute_send()
59
56
 
60
- def run(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000):
57
+ def run(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000) -> None:
61
58
  """Run the MCP server."""
62
59
  if transport == "sse":
63
60
  # For running as a web server
@@ -66,9 +63,7 @@ class AgentMCPServer:
66
63
 
67
64
  self.mcp_server.run(transport=transport)
68
65
 
69
- async def run_async(
70
- self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000
71
- ):
66
+ async def run_async(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000) -> None:
72
67
  """Run the MCP server asynchronously."""
73
68
  if transport == "sse":
74
69
  self.mcp_server.settings.host = host
@@ -77,9 +72,7 @@ class AgentMCPServer:
77
72
  else: # stdio
78
73
  await self.mcp_server.run_stdio_async()
79
74
 
80
- async def with_bridged_context(
81
- self, agent_context, mcp_context, func, *args, **kwargs
82
- ):
75
+ async def with_bridged_context(self, agent_context, mcp_context, func, *args, **kwargs):
83
76
  """
84
77
  Execute a function with bridged context between MCP and agent
85
78
 
@@ -98,7 +91,7 @@ class AgentMCPServer:
98
91
  agent_context.mcp_context = mcp_context
99
92
 
100
93
  # Create bridged progress reporter
101
- async def bridged_progress(progress, total=None):
94
+ async def bridged_progress(progress, total=None) -> None:
102
95
  if mcp_context:
103
96
  await mcp_context.report_progress(progress, total)
104
97
  if original_progress_reporter:
@@ -9,25 +9,25 @@ server initialization.
9
9
 
10
10
  from contextlib import asynccontextmanager
11
11
  from datetime import timedelta
12
- from typing import Callable, Dict, AsyncGenerator
12
+ from typing import AsyncGenerator, Callable, Dict
13
13
 
14
14
  from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
15
15
  from mcp import ClientSession
16
+ from mcp.client.sse import sse_client
16
17
  from mcp.client.stdio import (
17
18
  StdioServerParameters,
18
19
  get_default_environment,
19
20
  )
20
- from mcp_agent.mcp.stdio import stdio_client_with_rich_stderr
21
- from mcp.client.sse import sse_client
22
21
 
23
22
  from mcp_agent.config import (
24
- get_settings,
25
23
  MCPServerAuthSettings,
26
24
  MCPServerSettings,
27
25
  Settings,
26
+ get_settings,
28
27
  )
29
28
  from mcp_agent.logging.logger import get_logger
30
29
  from mcp_agent.mcp.mcp_connection_manager import MCPConnectionManager
30
+ from mcp_agent.mcp.stdio import stdio_client_with_rich_stderr
31
31
 
32
32
  logger = get_logger(__name__)
33
33
 
@@ -58,7 +58,7 @@ class ServerRegistry:
58
58
  init_hooks (Dict[str, InitHookCallable]): Registered initialization hooks.
59
59
  """
60
60
 
61
- def __init__(self, config: Settings | None = None, config_path: str | None = None):
61
+ def __init__(self, config: Settings | None = None, config_path: str | None = None) -> None:
62
62
  """
63
63
  Initialize the ServerRegistry with a configuration file.
64
64
 
@@ -66,17 +66,11 @@ class ServerRegistry:
66
66
  config (Settings): The Settings object containing the server configurations.
67
67
  config_path (str): Path to the YAML configuration file.
68
68
  """
69
- self.registry = (
70
- self.load_registry_from_file(config_path)
71
- if config is None
72
- else config.mcp.servers
73
- )
69
+ self.registry = self.load_registry_from_file(config_path) if config is None else config.mcp.servers
74
70
  self.init_hooks: Dict[str, InitHookCallable] = {}
75
71
  self.connection_manager = MCPConnectionManager(self)
76
72
 
77
- def load_registry_from_file(
78
- self, config_path: str | None = None
79
- ) -> Dict[str, MCPServerSettings]:
73
+ def load_registry_from_file(self, config_path: str | None = None) -> Dict[str, MCPServerSettings]:
80
74
  """
81
75
  Load the YAML configuration file and validate it.
82
76
 
@@ -116,17 +110,11 @@ class ServerRegistry:
116
110
 
117
111
  config = self.registry[server_name]
118
112
 
119
- read_timeout_seconds = (
120
- timedelta(config.read_timeout_seconds)
121
- if config.read_timeout_seconds
122
- else None
123
- )
113
+ read_timeout_seconds = timedelta(config.read_timeout_seconds) if config.read_timeout_seconds else None
124
114
 
125
115
  if config.transport == "stdio":
126
116
  if not config.command or not config.args:
127
- raise ValueError(
128
- f"Command and args are required for stdio transport: {server_name}"
129
- )
117
+ raise ValueError(f"Command and args are required for stdio transport: {server_name}")
130
118
 
131
119
  server_params = StdioServerParameters(
132
120
  command=config.command,
@@ -144,9 +132,7 @@ class ServerRegistry:
144
132
  read_timeout_seconds,
145
133
  )
146
134
  async with session:
147
- logger.info(
148
- f"{server_name}: Connected to server using stdio transport."
149
- )
135
+ logger.info(f"{server_name}: Connected to server using stdio transport.")
150
136
  try:
151
137
  yield session
152
138
  finally:
@@ -164,9 +150,7 @@ class ServerRegistry:
164
150
  read_timeout_seconds,
165
151
  )
166
152
  async with session:
167
- logger.info(
168
- f"{server_name}: Connected to server using SSE transport."
169
- )
153
+ logger.info(f"{server_name}: Connected to server using SSE transport.")
170
154
  try:
171
155
  yield session
172
156
  finally:
@@ -206,19 +190,13 @@ class ServerRegistry:
206
190
 
207
191
  config = self.registry[server_name]
208
192
 
209
- async with self.start_server(
210
- server_name, client_session_factory=client_session_factory
211
- ) as session:
193
+ async with self.start_server(server_name, client_session_factory=client_session_factory) as session:
212
194
  try:
213
195
  logger.info(f"{server_name}: Initializing server...")
214
196
  await session.initialize()
215
197
  logger.info(f"{server_name}: Initialized.")
216
198
 
217
- intialization_callback = (
218
- init_hook
219
- if init_hook is not None
220
- else self.init_hooks.get(server_name)
221
- )
199
+ intialization_callback = init_hook if init_hook is not None else self.init_hooks.get(server_name)
222
200
 
223
201
  if intialization_callback:
224
202
  logger.info(f"{server_name}: Executing init hook")
@@ -172,7 +172,7 @@ Extract key insights that would be compelling for a social media campaign.
172
172
  request_params=RequestParams(maxTokens=8192),
173
173
  plan_type="full",
174
174
  )
175
- async def main():
175
+ async def main() -> None:
176
176
  # Use the app's context manager
177
177
  print(
178
178
  "WARNING: This workflow will likely run for >10 minutes and consume a lot of tokens. Press Enter to accept the default prompt and proceed"
@@ -23,7 +23,7 @@ Visualisations should be saved as .png files in the current working directory.
23
23
  servers=["interpreter"],
24
24
  request_params=RequestParams(maxTokens=8192),
25
25
  )
26
- async def main():
26
+ async def main() -> None:
27
27
  # Use the app's context manager
28
28
  async with fast.run() as agent:
29
29
  await agent(
@@ -0,0 +1,110 @@
1
+ import asyncio
2
+ from pathlib import Path
3
+ from typing import TYPE_CHECKING
4
+
5
+ from mcp_agent.core.fastagent import FastAgent
6
+ from mcp_agent.mcp.prompts.prompt_load import load_prompt_multipart
7
+ from mcp_agent.workflows.llm.augmented_llm import RequestParams
8
+
9
+ if TYPE_CHECKING:
10
+ from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
11
+
12
+ # Create the application
13
+ fast = FastAgent("Data Analysis (Roots)")
14
+
15
+
16
+ # The sample data is under Database Contents License (DbCL) v1.0.
17
+
18
+ # Available here : https://www.kaggle.com/datasets/pavansubhasht/ibm-hr-analytics-attrition-dataset
19
+
20
+ # The CSS files are distributed under the MIT License from the excellent
21
+ # marpstyle project : https://github.com/cunhapaulo/marpstyle
22
+
23
+
24
+ @fast.agent(
25
+ name="slides",
26
+ servers=["filesystem"],
27
+ instruction="""
28
+ You produce compelling slide decks for impactful presentations. You usually try and keep the pack to between
29
+ 8-12 slides, with key insights at the start, backed up with data, diagrams and analysis as available. You
30
+ are able to help direct colour, style and and questions for enhancing the presentation. Produced charts and
31
+ visualisations will be in the ./mount-point/ directory. You output MARP markdown files.
32
+ """,
33
+ request_params=RequestParams(maxTokens=8192),
34
+ )
35
+ @fast.agent(
36
+ name="data_analysis",
37
+ instruction="""
38
+ You have access to a Python 3.12 interpreter and you can use this to analyse and process data.
39
+ Common analysis packages such as Pandas, Seaborn and Matplotlib are already installed.
40
+ You can add further packages if needed.
41
+ Data files are accessible from the /mnt/data/ directory (this is the current working directory).
42
+ Visualisations should be saved as .png files in the current working directory.
43
+ """,
44
+ servers=["interpreter"],
45
+ request_params=RequestParams(maxTokens=8192),
46
+ )
47
+ @fast.orchestrator(
48
+ name="orchestrator",
49
+ plan_type="iterative",
50
+ agents=["slides", "data_analysis"],
51
+ )
52
+ async def main() -> None:
53
+ # Use the app's context manager
54
+ async with fast.run() as agent:
55
+ prompts: list[PromptMessageMultipart] = load_prompt_multipart(Path("slides.md"))
56
+ await agent.slides.apply_prompt_messages(prompts)
57
+
58
+ await agent.orchestrator.send(
59
+ "Produce a compelling presentation for the CSV data file in the /mnt/data/ directory."
60
+ "The slides agent will produce a presentation, make sure to consult it first for "
61
+ "colour scheme and formatting guidance. Make sure that any 'call-outs' have a distinct"
62
+ "background to ensure they stand out."
63
+ "Make sure the presentation is impactful, concise and visualises key insights in to the data"
64
+ " in a compelling way."
65
+ "The presentation is by the 'llmindset team' and produced in 'march 2025'."
66
+ "Produce it step-by-step; long responses without checking in are likely to exceed"
67
+ "maximum output token limits."
68
+ )
69
+ # colours: str = await agent.slides.send("Tell the Data Analysis agent what colour schemes and chart sizes you prefer for the presentation")
70
+
71
+ # analysis: str = await agent.data_analysis.send(
72
+ # "Examine the CSV file in /mnt/data, produce a detailed analysis of the data,"
73
+ # "and any patterns it contains. Visualise some of the key points, saving .png files to"
74
+ # "your current workig folder (/mnt/data). Respond with a summary of your findings, and a list"
75
+ # "of visualiations and their filenames ready to incorporate in to a slide deck. The presentation agent has"
76
+ # f"specified the following style guidelines for generated charts:\n {colours}"
77
+ # )
78
+ # await agent.slides.send(
79
+ # "Produce a MARP Presentation for the this analysis. You will find the visualisations in "
80
+ # f"in the ./mount-point/ folder. The analysis follows....\n{analysis}"
81
+ # )
82
+
83
+ await agent()
84
+
85
+
86
+ if __name__ == "__main__":
87
+ asyncio.run(main())
88
+
89
+
90
+ ############################################################################################################
91
+ # Example of evaluator/optimizer flow
92
+ ############################################################################################################
93
+ # @fast.agent(
94
+ # "evaluator",
95
+ # """You are collaborating with a Data Analysis tool that has the capability to analyse data and produce visualisations.
96
+ # You must make sure that the tool has:
97
+ # - Considered the best way for a Human to interpret the data
98
+ # - Produced insightful visualasions.
99
+ # - Provided a high level summary report for the Human.
100
+ # - Has had its findings challenged, and justified
101
+ # """,
102
+ # request_params=RequestParams(maxTokens=8192),
103
+ # )
104
+ # @fast.evaluator_optimizer(
105
+ # "analysis_tool",
106
+ # generator="data_analysis",
107
+ # evaluator="evaluator",
108
+ # max_refinements=3,
109
+ # min_rating="EXCELLENT",
110
+ # )
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+
2
3
  from mcp_agent.core.fastagent import FastAgent
3
4
 
4
5
  # Create the application
@@ -7,7 +8,7 @@ fast = FastAgent("FastAgent Example")
7
8
 
8
9
  # Define the agent
9
10
  @fast.agent(servers=["fetch", "mcp_hfspace"])
10
- async def main():
11
+ async def main() -> None:
11
12
  # use the --model command line switch or agent arguments to change model
12
13
  async with fast.run() as agent:
13
14
  await agent.prompt()
@@ -5,6 +5,7 @@ PMO Job Description Generator Agent
5
5
  """
6
6
 
7
7
  import asyncio
8
+
8
9
  from mcp_agent.core.fastagent import FastAgent
9
10
 
10
11
  # Create the application
@@ -57,7 +58,7 @@ fast = FastAgent("PMO Job Description Generator")
57
58
  min_rating="EXCELLENT",
58
59
  max_refinements=2,
59
60
  )
60
- async def main():
61
+ async def main() -> None:
61
62
  async with fast.run() as agent:
62
63
  roles = [
63
64
  "PMO Director",
@@ -1,5 +1,5 @@
1
1
  from mcp.server.fastmcp import FastMCP
2
- from mcp.server.fastmcp.prompts.base import UserMessage, AssistantMessage
2
+ from mcp.server.fastmcp.prompts.base import AssistantMessage, UserMessage
3
3
 
4
4
  mcp = FastMCP("MCP Root Tester")
5
5
 
@@ -1,6 +1,6 @@
1
- from pydantic import Field
2
1
  from mcp.server.fastmcp import FastMCP
3
- from mcp.server.fastmcp.prompts.base import UserMessage, AssistantMessage
2
+ from mcp.server.fastmcp.prompts.base import AssistantMessage, UserMessage
3
+ from pydantic import Field
4
4
 
5
5
  mcp = FastMCP("MCP Prompt Tester")
6
6
 
@@ -23,9 +23,7 @@ def sizing_prompt():
23
23
  description="set up the sizing protocol with metric or imperial units",
24
24
  )
25
25
  def sizing_prompt_units(
26
- metric: bool = Field(
27
- description="Set to True for Metric, False for Imperial", default=True
28
- ),
26
+ metric: bool = Field(description="Set to True for Metric, False for Imperial", default=True),
29
27
  ):
30
28
  if metric:
31
29
  return [
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+
2
3
  from mcp_agent.core.fastagent import FastAgent
3
4
 
4
5
  fast = FastAgent("Sizer Prompt Test")
@@ -10,7 +11,7 @@ fast = FastAgent("Sizer Prompt Test")
10
11
  servers=["sizer", "category"],
11
12
  use_history=True,
12
13
  )
13
- async def main():
14
+ async def main() -> None:
14
15
  async with fast.run() as agent:
15
16
  await agent()
16
17
 
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+
2
3
  from mcp_agent.core.fastagent import FastAgent
3
4
 
4
5
  # Create the application
@@ -56,7 +57,7 @@ Social Media report ready to review with the Human.
56
57
  "human_review_and_post",
57
58
  ],
58
59
  )
59
- async def main():
60
+ async def main() -> None:
60
61
  async with fast.run() as agent:
61
62
  # using chain workflow
62
63
  await agent.post_writer.prompt()
@@ -41,7 +41,7 @@ Summarize your evaluation as a structured response with:
41
41
  min_rating="EXCELLENT",
42
42
  name="Researcher_Evaluator",
43
43
  )
44
- async def main():
44
+ async def main() -> None:
45
45
  async with agents.run() as agent:
46
46
  await agent.prompt("Researcher_Evaluator")
47
47
 
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+
2
3
  from mcp_agent.core.fastagent import FastAgent
3
4
 
4
5
  # Create the application
@@ -13,7 +14,7 @@ fast = FastAgent("FastAgent Example")
13
14
  # model="gpt-4o",
14
15
  # instruction="You are a helpful AI Agent", servers=["prompts","basic_memory"], model="haiku"
15
16
  )
16
- async def main():
17
+ async def main() -> None:
17
18
  # use the --model command line switch or agent arguments to change model
18
19
  async with fast.run() as agent:
19
20
  await agent()
@@ -6,8 +6,8 @@ Simple MCP server that responds to tool calls with text and image content.
6
6
  import logging
7
7
  from pathlib import Path
8
8
 
9
- from mcp.server.fastmcp import FastMCP, Context, Image
10
- from mcp.types import TextContent, ImageContent
9
+ from mcp.server.fastmcp import Context, FastMCP, Image
10
+ from mcp.types import ImageContent, TextContent
11
11
 
12
12
  # Configure logging
13
13
  logging.basicConfig(level=logging.INFO)
@@ -18,9 +18,7 @@ app = FastMCP(name="ImageToolServer", debug=True)
18
18
 
19
19
 
20
20
  @app.tool(name="get_image", description="Returns an image with a descriptive text")
21
- async def get_image(
22
- image_name: str = "default", ctx: Context = None
23
- ) -> list[TextContent | ImageContent]:
21
+ async def get_image(image_name: str = "default", ctx: Context = None) -> list[TextContent | ImageContent]:
24
22
  """
25
23
  Returns an image file along with a descriptive text.
26
24
 
@@ -45,12 +43,8 @@ async def get_image(
45
43
  if __name__ == "__main__":
46
44
  # Check if the default image exists
47
45
  if not Path("image.jpg").exists():
48
- logger.warning(
49
- "Default image file 'image.jpg' not found in the current directory"
50
- )
51
- logger.warning(
52
- "Please add an image file named 'image.jpg' to the current directory"
53
- )
46
+ logger.warning("Default image file 'image.jpg' not found in the current directory")
47
+ logger.warning("Please add an image file named 'image.jpg' to the current directory")
54
48
 
55
49
  # Run the server using stdio transport
56
50
  app.run(transport="stdio")
@@ -41,7 +41,7 @@ Summarize your evaluation as a structured response with:
41
41
  min_rating="EXCELLENT",
42
42
  name="Researcher_Evaluator",
43
43
  )
44
- async def main():
44
+ async def main() -> None:
45
45
  async with agents.run() as agent:
46
46
  await agent.prompt("Researcher_Evaluator")
47
47