fast-agent-mcp 0.2.3__py3-none-any.whl → 0.2.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.
Files changed (33) hide show
  1. {fast_agent_mcp-0.2.3.dist-info → fast_agent_mcp-0.2.4.dist-info}/METADATA +12 -8
  2. {fast_agent_mcp-0.2.3.dist-info → fast_agent_mcp-0.2.4.dist-info}/RECORD +33 -30
  3. mcp_agent/__init__.py +2 -2
  4. mcp_agent/agents/agent.py +5 -0
  5. mcp_agent/agents/base_agent.py +158 -42
  6. mcp_agent/agents/workflow/chain_agent.py +9 -13
  7. mcp_agent/agents/workflow/evaluator_optimizer.py +3 -3
  8. mcp_agent/agents/workflow/orchestrator_agent.py +9 -7
  9. mcp_agent/agents/workflow/parallel_agent.py +2 -2
  10. mcp_agent/agents/workflow/router_agent.py +7 -5
  11. mcp_agent/core/{direct_agent_app.py → agent_app.py} +115 -15
  12. mcp_agent/core/direct_factory.py +12 -12
  13. mcp_agent/core/fastagent.py +17 -4
  14. mcp_agent/core/mcp_content.py +38 -5
  15. mcp_agent/core/prompt.py +70 -8
  16. mcp_agent/core/validation.py +1 -1
  17. mcp_agent/llm/augmented_llm.py +38 -8
  18. mcp_agent/llm/model_factory.py +17 -27
  19. mcp_agent/llm/providers/augmented_llm_openai.py +3 -3
  20. mcp_agent/llm/providers/multipart_converter_anthropic.py +8 -8
  21. mcp_agent/llm/providers/multipart_converter_openai.py +9 -9
  22. mcp_agent/mcp/helpers/__init__.py +3 -0
  23. mcp_agent/mcp/helpers/content_helpers.py +116 -0
  24. mcp_agent/mcp/interfaces.py +39 -16
  25. mcp_agent/mcp/mcp_aggregator.py +117 -13
  26. mcp_agent/mcp/prompt_message_multipart.py +29 -22
  27. mcp_agent/mcp/prompt_render.py +18 -15
  28. mcp_agent/mcp/prompts/prompt_helpers.py +22 -112
  29. mcp_agent/mcp_server/agent_server.py +2 -2
  30. mcp_agent/resources/examples/internal/history_transfer.py +35 -0
  31. {fast_agent_mcp-0.2.3.dist-info → fast_agent_mcp-0.2.4.dist-info}/WHEEL +0 -0
  32. {fast_agent_mcp-0.2.3.dist-info → fast_agent_mcp-0.2.4.dist-info}/entry_points.txt +0 -0
  33. {fast_agent_mcp-0.2.3.dist-info → fast_agent_mcp-0.2.4.dist-info}/licenses/LICENSE +0 -0
@@ -5,6 +5,7 @@ from typing import (
5
5
  Callable,
6
6
  Dict,
7
7
  List,
8
+ Mapping,
8
9
  Optional,
9
10
  TypeVar,
10
11
  )
@@ -373,7 +374,11 @@ class MCPAggregator(ContextDependent):
373
374
  f"Failed to {method_name} '{operation_name}' on server '{server_name}': {e}"
374
375
  )
375
376
  logger.error(error_msg)
376
- return error_factory(error_msg) if error_factory else None
377
+ if error_factory:
378
+ return error_factory(error_msg)
379
+ else:
380
+ # Re-raise the original exception to propagate it
381
+ raise e
377
382
 
378
383
  if self.connection_persistence:
379
384
  server_connection = await self._persistent_connection_manager.get_server(
@@ -476,7 +481,10 @@ class MCPAggregator(ContextDependent):
476
481
  )
477
482
 
478
483
  async def get_prompt(
479
- self, prompt_name: str | None, arguments: dict[str, str] | None
484
+ self,
485
+ prompt_name: str | None,
486
+ arguments: dict[str, str] | None = None,
487
+ server_name: str | None = None,
480
488
  ) -> GetPromptResult:
481
489
  """
482
490
  Get a prompt from a server.
@@ -485,6 +493,8 @@ class MCPAggregator(ContextDependent):
485
493
  using the format 'server_name-prompt_name'
486
494
  :param arguments: Optional dictionary of string arguments to pass to the prompt template
487
495
  for templating
496
+ :param server_name: Optional name of the server to get the prompt from. If not provided
497
+ and prompt_name is not namespaced, will search all servers.
488
498
  :return: GetPromptResult containing the prompt description and messages
489
499
  with a namespaced_name property for display purposes
490
500
  """
@@ -493,17 +503,17 @@ class MCPAggregator(ContextDependent):
493
503
 
494
504
  # Handle the case where prompt_name is None
495
505
  if not prompt_name:
496
- server_name = self.server_names[0] if self.server_names else None
506
+ if server_name is None:
507
+ server_name = self.server_names[0] if self.server_names else None
497
508
  local_prompt_name = None
498
509
  namespaced_name = None
499
510
  # Handle namespaced prompt name
500
- elif SEP in prompt_name:
511
+ elif SEP in prompt_name and server_name is None:
501
512
  server_name, local_prompt_name = prompt_name.split(SEP, 1)
502
513
  namespaced_name = prompt_name # Already namespaced
503
- # Plain prompt name - will use cache to find the server
514
+ # Plain prompt name - use provided server or search
504
515
  else:
505
516
  local_prompt_name = prompt_name
506
- server_name = None
507
517
  namespaced_name = None # Will be set when server is found
508
518
 
509
519
  # If we have a specific server to check
@@ -700,7 +710,7 @@ class MCPAggregator(ContextDependent):
700
710
  messages=[],
701
711
  )
702
712
 
703
- async def list_prompts(self, server_name: str = None) -> Dict[str, List[Prompt]]:
713
+ async def list_prompts(self, server_name: str | None = None) -> Mapping[str, List[Prompt]]:
704
714
  """
705
715
  List available prompts from one or all servers.
706
716
 
@@ -795,13 +805,16 @@ class MCPAggregator(ContextDependent):
795
805
  logger.debug(f"Available prompts across servers: {results}")
796
806
  return results
797
807
 
798
- async def get_resource(self, server_name: str, resource_uri: str) -> ReadResourceResult:
808
+ async def get_resource(
809
+ self, resource_uri: str, server_name: str | None = None
810
+ ) -> ReadResourceResult:
799
811
  """
800
812
  Get a resource directly from an MCP server by URI.
813
+ If server_name is None, will search all available servers.
801
814
 
802
815
  Args:
803
- server_name: Name of the MCP server to retrieve the resource from
804
816
  resource_uri: URI of the resource to retrieve
817
+ server_name: Optional name of the MCP server to retrieve the resource from
805
818
 
806
819
  Returns:
807
820
  ReadResourceResult object containing the resource content
@@ -812,9 +825,45 @@ class MCPAggregator(ContextDependent):
812
825
  if not self.initialized:
813
826
  await self.load_servers()
814
827
 
815
- if server_name not in self.server_names:
816
- raise ValueError(f"Server '{server_name}' not found")
828
+ # If specific server requested, use only that server
829
+ if server_name is not None:
830
+ if server_name not in self.server_names:
831
+ raise ValueError(f"Server '{server_name}' not found")
832
+
833
+ # Get the resource from the specified server
834
+ return await self._get_resource_from_server(server_name, resource_uri)
835
+
836
+ # If no server specified, search all servers
837
+ if not self.server_names:
838
+ raise ValueError("No servers available to get resource from")
839
+
840
+ # Try each server in order - simply attempt to get the resource
841
+ for s_name in self.server_names:
842
+ try:
843
+ return await self._get_resource_from_server(s_name, resource_uri)
844
+ except Exception:
845
+ # Continue to next server if not found
846
+ continue
847
+
848
+ # If we reach here, we couldn't find the resource on any server
849
+ raise ValueError(f"Resource '{resource_uri}' not found on any server")
850
+
851
+ async def _get_resource_from_server(
852
+ self, server_name: str, resource_uri: str
853
+ ) -> ReadResourceResult:
854
+ """
855
+ Internal helper method to get a resource from a specific server.
856
+
857
+ Args:
858
+ server_name: Name of the server to get the resource from
859
+ resource_uri: URI of the resource to retrieve
860
+
861
+ Returns:
862
+ ReadResourceResult containing the resource
817
863
 
864
+ Raises:
865
+ Exception: If the resource couldn't be found or other error occurs
866
+ """
818
867
  logger.info(
819
868
  "Requesting resource",
820
869
  data={
@@ -831,15 +880,70 @@ class MCPAggregator(ContextDependent):
831
880
  raise ValueError(f"Invalid resource URI: {resource_uri}. Error: {e}")
832
881
 
833
882
  # Use the _execute_on_server method to call read_resource on the server
834
- return await self._execute_on_server(
883
+ result = await self._execute_on_server(
835
884
  server_name=server_name,
836
885
  operation_type="resource",
837
886
  operation_name=resource_uri,
838
887
  method_name="read_resource",
839
888
  method_args={"uri": uri},
840
- error_factory=lambda msg: ValueError(f"Failed to retrieve resource: {msg}"),
889
+ # Don't create ValueError, just return None on error so we can catch it
890
+ # error_factory=lambda _: None,
841
891
  )
842
892
 
893
+ # If result is None, the resource was not found
894
+ if result is None:
895
+ raise ValueError(f"Resource '{resource_uri}' not found on server '{server_name}'")
896
+
897
+ return result
898
+
899
+ async def list_resources(self, server_name: str | None = None) -> Dict[str, List[str]]:
900
+ """
901
+ List available resources from one or all servers.
902
+
903
+ Args:
904
+ server_name: Optional server name to list resources from. If not provided,
905
+ lists resources from all servers.
906
+
907
+ Returns:
908
+ Dictionary mapping server names to lists of resource URIs
909
+ """
910
+ if not self.initialized:
911
+ await self.load_servers()
912
+
913
+ results: Dict[str, List[str]] = {}
914
+
915
+ # Get the list of servers to check
916
+ servers_to_check = [server_name] if server_name else self.server_names
917
+
918
+ # For each server, try to list its resources
919
+ for s_name in servers_to_check:
920
+ if s_name not in self.server_names:
921
+ logger.error(f"Server '{s_name}' not found")
922
+ continue
923
+
924
+ # Initialize empty list for this server
925
+ results[s_name] = []
926
+
927
+ try:
928
+ # Use the _execute_on_server method to call list_resources on the server
929
+ result = await self._execute_on_server(
930
+ server_name=s_name,
931
+ operation_type="resources-list",
932
+ operation_name="",
933
+ method_name="list_resources",
934
+ method_args={}, # Empty dictionary instead of None
935
+ # No error_factory to allow exceptions to propagate
936
+ )
937
+
938
+ # Get resources from result
939
+ resources = getattr(result, "resources", [])
940
+ results[s_name] = [str(r.uri) for r in resources]
941
+
942
+ except Exception as e:
943
+ logger.error(f"Error fetching resources from {s_name}: {e}")
944
+
945
+ return results
946
+
843
947
 
844
948
  class MCPCompoundServer(Server):
845
949
  """
@@ -1,4 +1,4 @@
1
- from typing import List, Union
1
+ from typing import List, Optional, Union
2
2
 
3
3
  from mcp.types import (
4
4
  EmbeddedResource,
@@ -7,29 +7,10 @@ from mcp.types import (
7
7
  PromptMessage,
8
8
  Role,
9
9
  TextContent,
10
- TextResourceContents,
11
10
  )
12
11
  from pydantic import BaseModel
13
12
 
14
-
15
- def get_text(content: Union[TextContent, ImageContent, EmbeddedResource]) -> str | None:
16
- """
17
- Extract text content from a content object if available.
18
-
19
- Args:
20
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
21
-
22
- Returns:
23
- The text content as a string or None if not a text content
24
- """
25
- if isinstance(content, TextContent):
26
- return content.text
27
-
28
- if isinstance(content, EmbeddedResource):
29
- if isinstance(content.resource, TextResourceContents):
30
- return content.resource.text
31
-
32
- return None
13
+ from mcp_agent.mcp.helpers.content_helpers import get_text
33
14
 
34
15
 
35
16
  class PromptMessageMultipart(BaseModel):
@@ -112,5 +93,31 @@ class PromptMessageMultipart(BaseModel):
112
93
 
113
94
  @classmethod
114
95
  def parse_get_prompt_result(cls, result: GetPromptResult) -> List["PromptMessageMultipart"]:
115
- """Parse a GetPromptResult into PromptMessageMultipart objects."""
96
+ """
97
+ Parse a GetPromptResult into PromptMessageMultipart objects.
98
+
99
+ Args:
100
+ result: GetPromptResult from MCP server
101
+
102
+ Returns:
103
+ List of PromptMessageMultipart objects
104
+ """
116
105
  return cls.to_multipart(result.messages)
106
+
107
+ @classmethod
108
+ def from_get_prompt_result(
109
+ cls, result: Optional[GetPromptResult]
110
+ ) -> List["PromptMessageMultipart"]:
111
+ """
112
+ Convert a GetPromptResult to PromptMessageMultipart objects with error handling.
113
+ This method safely handles None values and empty results.
114
+
115
+ Args:
116
+ result: GetPromptResult from MCP server or None
117
+
118
+ Returns:
119
+ List of PromptMessageMultipart objects or empty list if result is None/empty
120
+ """
121
+ if not result or not result.messages:
122
+ return []
123
+ return cls.to_multipart(result.messages)
@@ -6,14 +6,14 @@ from typing import List
6
6
 
7
7
  from mcp.types import BlobResourceContents, TextResourceContents
8
8
 
9
- from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
10
- from mcp_agent.mcp.prompts.prompt_helpers import (
9
+ from mcp_agent.mcp.helpers.content_helpers import (
11
10
  get_resource_uri,
12
11
  get_text,
13
12
  is_image_content,
14
13
  is_resource_content,
15
14
  is_text_content,
16
15
  )
16
+ from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
17
17
 
18
18
 
19
19
  def render_multipart_message(message: PromptMessageMultipart) -> str:
@@ -34,36 +34,39 @@ def render_multipart_message(message: PromptMessageMultipart) -> str:
34
34
  for content in message.content:
35
35
  if is_text_content(content):
36
36
  # Handle text content
37
- text_content = content # type: TextContent
38
- rendered_parts.append(text_content.text)
37
+ text = get_text(content)
38
+ if text:
39
+ rendered_parts.append(text)
39
40
 
40
41
  elif is_image_content(content):
41
42
  # Format details about the image
42
- image_content = content # type: ImageContent
43
- data_size = len(image_content.data) if image_content.data else 0
44
- image_info = f"[IMAGE: {image_content.mimeType}, {data_size} bytes]"
43
+ image_data = getattr(content, "data", "")
44
+ data_size = len(image_data) if image_data else 0
45
+ mime_type = getattr(content, "mimeType", "unknown")
46
+ image_info = f"[IMAGE: {mime_type}, {data_size} bytes]"
45
47
  rendered_parts.append(image_info)
46
48
 
47
49
  elif is_resource_content(content):
48
50
  # Handle embedded resources
49
- resource = content # type: EmbeddedResource
50
- uri = get_resource_uri(resource)
51
+ uri = get_resource_uri(content)
52
+ resource = getattr(content, "resource", None)
51
53
 
52
- if isinstance(resource.resource, TextResourceContents):
54
+ if resource and isinstance(resource, TextResourceContents):
53
55
  # Handle text resources
54
- text = resource.resource.text
56
+ text = resource.text
55
57
  text_length = len(text)
56
- mime_type = resource.resource.mimeType
58
+ mime_type = getattr(resource, "mimeType", "text/plain")
57
59
 
58
60
  # Preview with truncation for long content
59
61
  preview = text[:300] + ("..." if text_length > 300 else "")
60
62
  resource_info = f"[EMBEDDED TEXT RESOURCE: {mime_type}, {uri}, {text_length} chars]\n{preview}"
61
63
  rendered_parts.append(resource_info)
62
64
 
63
- elif isinstance(resource.resource, BlobResourceContents):
65
+ elif resource and isinstance(resource, BlobResourceContents):
64
66
  # Handle blob resources (binary data)
65
- blob_length = len(resource.resource.blob) if resource.resource.blob else 0
66
- mime_type = resource.resource.mimeType
67
+ blob = getattr(resource, "blob", "")
68
+ blob_length = len(blob) if blob else 0
69
+ mime_type = getattr(resource, "mimeType", "application/octet-stream")
67
70
 
68
71
  resource_info = f"[EMBEDDED BLOB RESOURCE: {mime_type}, {uri}, {blob_length} bytes]"
69
72
  rendered_parts.append(resource_info)
@@ -8,112 +8,22 @@ without repetitive type checking.
8
8
  from typing import List, Optional, Union, cast
9
9
 
10
10
  from mcp.types import (
11
- BlobResourceContents,
12
11
  EmbeddedResource,
13
- ImageContent,
14
12
  PromptMessage,
15
13
  TextContent,
16
- TextResourceContents,
17
14
  )
18
15
 
19
- from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
16
+ from mcp_agent.mcp.helpers.content_helpers import get_image_data, get_text
20
17
 
21
-
22
- def get_text(content: Union[TextContent, ImageContent, EmbeddedResource]) -> Optional[str]:
23
- """
24
- Extract text content from a content object if available.
25
-
26
- Args:
27
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
28
-
29
- Returns:
30
- The text content as a string or None if not a text content
31
- """
32
- if isinstance(content, TextContent):
33
- return content.text
34
-
35
- if isinstance(content, EmbeddedResource):
36
- if isinstance(content.resource, TextResourceContents):
37
- return content.resource.text
38
-
39
- return None
40
-
41
-
42
- def get_image_data(content: Union[TextContent, ImageContent, EmbeddedResource]) -> Optional[str]:
43
- """
44
- Extract image data from a content object if available.
45
-
46
- Args:
47
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
48
-
49
- Returns:
50
- The image data as a base64 string or None if not an image content
51
- """
52
- if isinstance(content, ImageContent):
53
- return content.data
54
-
55
- if isinstance(content, EmbeddedResource):
56
- if isinstance(content.resource, BlobResourceContents):
57
- # This assumes the blob might be an image, which isn't always true
58
- # Consider checking the mimeType if needed
59
- return content.resource.blob
60
-
61
- return None
62
-
63
-
64
- def get_resource_uri(content: Union[TextContent, ImageContent, EmbeddedResource]) -> Optional[str]:
65
- """
66
- Extract resource URI from an EmbeddedResource if available.
67
-
68
- Args:
69
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
70
-
71
- Returns:
72
- The resource URI as a string or None if not an embedded resource
73
- """
74
- if isinstance(content, EmbeddedResource):
75
- return str(content.resource.uri)
76
-
77
- return None
78
-
79
-
80
- def is_text_content(content: Union[TextContent, ImageContent, EmbeddedResource]) -> bool:
81
- """
82
- Check if the content is text content.
83
-
84
- Args:
85
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
86
-
87
- Returns:
88
- True if the content is TextContent, False otherwise
89
- """
90
- return isinstance(content, TextContent)
91
-
92
-
93
- def is_image_content(content: Union[TextContent, ImageContent, EmbeddedResource]) -> bool:
94
- """
95
- Check if the content is image content.
96
-
97
- Args:
98
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
99
-
100
- Returns:
101
- True if the content is ImageContent, False otherwise
102
- """
103
- return isinstance(content, ImageContent)
104
-
105
-
106
- def is_resource_content(content: Union[TextContent, ImageContent, EmbeddedResource]) -> bool:
107
- """
108
- Check if the content is an embedded resource.
109
-
110
- Args:
111
- content: A content object (TextContent, ImageContent, or EmbeddedResource)
112
-
113
- Returns:
114
- True if the content is EmbeddedResource, False otherwise
115
- """
116
- return isinstance(content, EmbeddedResource)
18
+ # Forward reference for PromptMessageMultipart, actual import happens at runtime
19
+ PromptMessageMultipartType = Union[object] # Will be replaced with actual type
20
+ try:
21
+ from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
22
+ PromptMessageMultipartType = PromptMessageMultipart
23
+ except ImportError:
24
+ # During initialization, there might be a circular import.
25
+ # We'll handle this gracefully.
26
+ pass
117
27
 
118
28
 
119
29
  class MessageContent:
@@ -123,7 +33,7 @@ class MessageContent:
123
33
  """
124
34
 
125
35
  @staticmethod
126
- def get_all_text(message: Union[PromptMessage, PromptMessageMultipart]) -> List[str]:
36
+ def get_all_text(message: Union[PromptMessage, "PromptMessageMultipart"]) -> List[str]:
127
37
  """
128
38
  Extract all text content from a message.
129
39
 
@@ -147,7 +57,7 @@ class MessageContent:
147
57
 
148
58
  @staticmethod
149
59
  def join_text(
150
- message: Union[PromptMessage, PromptMessageMultipart], separator: str = "\n\n"
60
+ message: Union[PromptMessage, "PromptMessageMultipart"], separator: str = "\n\n"
151
61
  ) -> str:
152
62
  """
153
63
  Join all text content in a message with a separator.
@@ -162,7 +72,7 @@ class MessageContent:
162
72
  return separator.join(MessageContent.get_all_text(message))
163
73
 
164
74
  @staticmethod
165
- def get_first_text(message: Union[PromptMessage, PromptMessageMultipart]) -> Optional[str]:
75
+ def get_first_text(message: Union[PromptMessage, "PromptMessageMultipart"]) -> Optional[str]:
166
76
  """
167
77
  Get the first available text content from a message.
168
78
 
@@ -183,7 +93,7 @@ class MessageContent:
183
93
  return None
184
94
 
185
95
  @staticmethod
186
- def has_text_at_first_position(message: Union[PromptMessage, PromptMessageMultipart]) -> bool:
96
+ def has_text_at_first_position(message: Union[PromptMessage, "PromptMessageMultipart"]) -> bool:
187
97
  """
188
98
  Check if a message has a TextContent at the first position.
189
99
  This is a common case when dealing with messages that start with text.
@@ -202,7 +112,7 @@ class MessageContent:
202
112
 
203
113
  @staticmethod
204
114
  def get_text_at_first_position(
205
- message: Union[PromptMessage, PromptMessageMultipart],
115
+ message: Union[PromptMessage, "PromptMessageMultipart"],
206
116
  ) -> Optional[str]:
207
117
  """
208
118
  Get the text from the first position of a message if it's TextContent.
@@ -224,7 +134,7 @@ class MessageContent:
224
134
  return cast("TextContent", message.content[0]).text
225
135
 
226
136
  @staticmethod
227
- def get_all_images(message: Union[PromptMessage, PromptMessageMultipart]) -> List[str]:
137
+ def get_all_images(message: Union[PromptMessage, "PromptMessageMultipart"]) -> List[str]:
228
138
  """
229
139
  Extract all image data from a message.
230
140
 
@@ -247,7 +157,7 @@ class MessageContent:
247
157
  return result
248
158
 
249
159
  @staticmethod
250
- def get_first_image(message: Union[PromptMessage, PromptMessageMultipart]) -> Optional[str]:
160
+ def get_first_image(message: Union[PromptMessage, "PromptMessageMultipart"]) -> Optional[str]:
251
161
  """
252
162
  Get the first available image data from a message.
253
163
 
@@ -269,7 +179,7 @@ class MessageContent:
269
179
 
270
180
  @staticmethod
271
181
  def get_all_resources(
272
- message: Union[PromptMessage, PromptMessageMultipart],
182
+ message: Union[PromptMessage, "PromptMessageMultipart"],
273
183
  ) -> List[EmbeddedResource]:
274
184
  """
275
185
  Extract all embedded resources from a message.
@@ -288,7 +198,7 @@ class MessageContent:
288
198
  return [content for content in message.content if isinstance(content, EmbeddedResource)]
289
199
 
290
200
  @staticmethod
291
- def has_text(message: Union[PromptMessage, PromptMessageMultipart]) -> bool:
201
+ def has_text(message: Union[PromptMessage, "PromptMessageMultipart"]) -> bool:
292
202
  """
293
203
  Check if the message has any text content.
294
204
 
@@ -301,7 +211,7 @@ class MessageContent:
301
211
  return len(MessageContent.get_all_text(message)) > 0
302
212
 
303
213
  @staticmethod
304
- def has_images(message: Union[PromptMessage, PromptMessageMultipart]) -> bool:
214
+ def has_images(message: Union[PromptMessage, "PromptMessageMultipart"]) -> bool:
305
215
  """
306
216
  Check if the message has any image content.
307
217
 
@@ -314,7 +224,7 @@ class MessageContent:
314
224
  return len(MessageContent.get_all_images(message)) > 0
315
225
 
316
226
  @staticmethod
317
- def has_resources(message: Union[PromptMessage, PromptMessageMultipart]) -> bool:
227
+ def has_resources(message: Union[PromptMessage, "PromptMessageMultipart"]) -> bool:
318
228
  """
319
229
  Check if the message has any embedded resources.
320
230
 
@@ -324,4 +234,4 @@ class MessageContent:
324
234
  Returns:
325
235
  True if the message has embedded resources, False otherwise
326
236
  """
327
- return len(MessageContent.get_all_resources(message)) > 0
237
+ return len(MessageContent.get_all_resources(message)) > 0
@@ -4,7 +4,7 @@ from mcp.server.fastmcp import Context as MCPContext
4
4
  from mcp.server.fastmcp import FastMCP
5
5
 
6
6
  # Import the DirectAgentApp instead of AgentApp
7
- from mcp_agent.core.direct_agent_app import DirectAgentApp
7
+ from mcp_agent.core.agent_app import AgentApp
8
8
 
9
9
 
10
10
  class AgentMCPServer:
@@ -12,7 +12,7 @@ class AgentMCPServer:
12
12
 
13
13
  def __init__(
14
14
  self,
15
- agent_app: DirectAgentApp,
15
+ agent_app: AgentApp,
16
16
  server_name: str = "FastAgent-MCP-Server",
17
17
  server_description: str = None,
18
18
  ) -> None:
@@ -0,0 +1,35 @@
1
+ import asyncio
2
+
3
+ from mcp_agent.core.fastagent import FastAgent
4
+
5
+ # Create the application
6
+ fast = FastAgent("FastAgent Example")
7
+
8
+
9
+ # Define the agent
10
+ @fast.agent(name="haiku", model="haiku")
11
+ @fast.agent(name="openai", model="o3-mini.medium")
12
+
13
+ # @fast.agent(name="test")
14
+ async def main() -> None:
15
+ async with fast.run() as agent:
16
+ # Start an interactive session with "haiku"
17
+ await agent.prompt(agent_name="haiku")
18
+ # Transfer the message history top "openai"
19
+ await agent.openai.generate(agent.haiku.message_history)
20
+ # Continue the conversation
21
+ await agent.prompt(agent_name="openai") # Interactive shell
22
+
23
+ # result: str = await agent.send("foo")
24
+ # mcp_prompt: PromptMessage = PromptMessage(
25
+ # role="user", content=TextContent(type="text", text="How are you?")
26
+ # )
27
+ # result: str = agent.send(mcp_prompt)
28
+ # resource: ReadResourceResult = agent.openai.get_resource(
29
+ # "server_name", "resource://images/cat.png"
30
+ # )
31
+ # response: str = Prompt.user("What is in this image?", resource)
32
+
33
+
34
+ if __name__ == "__main__":
35
+ asyncio.run(main())