fast-agent-mcp 0.1.12__py3-none-any.whl → 0.2.0__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 (169) hide show
  1. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
  2. fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
  3. mcp_agent/__init__.py +75 -0
  4. mcp_agent/agents/agent.py +61 -415
  5. mcp_agent/agents/base_agent.py +522 -0
  6. mcp_agent/agents/workflow/__init__.py +1 -0
  7. mcp_agent/agents/workflow/chain_agent.py +173 -0
  8. mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
  9. mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
  10. mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
  11. mcp_agent/agents/workflow/parallel_agent.py +182 -0
  12. mcp_agent/agents/workflow/router_agent.py +307 -0
  13. mcp_agent/app.py +15 -19
  14. mcp_agent/cli/commands/bootstrap.py +19 -38
  15. mcp_agent/cli/commands/config.py +4 -4
  16. mcp_agent/cli/commands/setup.py +7 -14
  17. mcp_agent/cli/main.py +7 -10
  18. mcp_agent/cli/terminal.py +3 -3
  19. mcp_agent/config.py +25 -40
  20. mcp_agent/context.py +12 -21
  21. mcp_agent/context_dependent.py +3 -5
  22. mcp_agent/core/agent_types.py +10 -7
  23. mcp_agent/core/direct_agent_app.py +179 -0
  24. mcp_agent/core/direct_decorators.py +443 -0
  25. mcp_agent/core/direct_factory.py +476 -0
  26. mcp_agent/core/enhanced_prompt.py +23 -55
  27. mcp_agent/core/exceptions.py +8 -8
  28. mcp_agent/core/fastagent.py +145 -371
  29. mcp_agent/core/interactive_prompt.py +424 -0
  30. mcp_agent/core/mcp_content.py +17 -17
  31. mcp_agent/core/prompt.py +6 -9
  32. mcp_agent/core/request_params.py +6 -3
  33. mcp_agent/core/validation.py +92 -18
  34. mcp_agent/executor/decorator_registry.py +9 -17
  35. mcp_agent/executor/executor.py +8 -17
  36. mcp_agent/executor/task_registry.py +2 -4
  37. mcp_agent/executor/temporal.py +19 -41
  38. mcp_agent/executor/workflow.py +3 -5
  39. mcp_agent/executor/workflow_signal.py +15 -21
  40. mcp_agent/human_input/handler.py +4 -7
  41. mcp_agent/human_input/types.py +2 -3
  42. mcp_agent/llm/__init__.py +2 -0
  43. mcp_agent/llm/augmented_llm.py +450 -0
  44. mcp_agent/llm/augmented_llm_passthrough.py +162 -0
  45. mcp_agent/llm/augmented_llm_playback.py +83 -0
  46. mcp_agent/llm/memory.py +103 -0
  47. mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
  48. mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
  49. mcp_agent/llm/providers/__init__.py +8 -0
  50. mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
  51. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
  52. mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
  53. mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
  54. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
  55. mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
  56. mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
  57. mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
  58. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
  59. mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
  60. mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
  61. mcp_agent/llm/sampling_format_converter.py +37 -0
  62. mcp_agent/logging/events.py +1 -5
  63. mcp_agent/logging/json_serializer.py +7 -6
  64. mcp_agent/logging/listeners.py +20 -23
  65. mcp_agent/logging/logger.py +17 -19
  66. mcp_agent/logging/rich_progress.py +10 -8
  67. mcp_agent/logging/tracing.py +4 -6
  68. mcp_agent/logging/transport.py +22 -22
  69. mcp_agent/mcp/gen_client.py +1 -3
  70. mcp_agent/mcp/interfaces.py +117 -110
  71. mcp_agent/mcp/logger_textio.py +97 -0
  72. mcp_agent/mcp/mcp_agent_client_session.py +7 -7
  73. mcp_agent/mcp/mcp_agent_server.py +8 -8
  74. mcp_agent/mcp/mcp_aggregator.py +102 -143
  75. mcp_agent/mcp/mcp_connection_manager.py +20 -27
  76. mcp_agent/mcp/prompt_message_multipart.py +68 -16
  77. mcp_agent/mcp/prompt_render.py +77 -0
  78. mcp_agent/mcp/prompt_serialization.py +30 -48
  79. mcp_agent/mcp/prompts/prompt_constants.py +18 -0
  80. mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
  81. mcp_agent/mcp/prompts/prompt_load.py +109 -0
  82. mcp_agent/mcp/prompts/prompt_server.py +155 -195
  83. mcp_agent/mcp/prompts/prompt_template.py +35 -66
  84. mcp_agent/mcp/resource_utils.py +7 -14
  85. mcp_agent/mcp/sampling.py +17 -17
  86. mcp_agent/mcp_server/agent_server.py +13 -17
  87. mcp_agent/mcp_server_registry.py +13 -22
  88. mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
  89. mcp_agent/resources/examples/in_dev/slides.py +110 -0
  90. mcp_agent/resources/examples/internal/agent.py +6 -3
  91. mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
  92. mcp_agent/resources/examples/internal/job.py +2 -1
  93. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  94. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  95. mcp_agent/resources/examples/internal/sizer.py +2 -1
  96. mcp_agent/resources/examples/internal/social.py +2 -1
  97. mcp_agent/resources/examples/prompting/agent.py +2 -1
  98. mcp_agent/resources/examples/prompting/image_server.py +4 -8
  99. mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
  100. mcp_agent/ui/console_display.py +16 -20
  101. fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
  102. mcp_agent/core/agent_app.py +0 -646
  103. mcp_agent/core/agent_utils.py +0 -71
  104. mcp_agent/core/decorators.py +0 -455
  105. mcp_agent/core/factory.py +0 -463
  106. mcp_agent/core/proxies.py +0 -269
  107. mcp_agent/core/types.py +0 -24
  108. mcp_agent/eval/__init__.py +0 -0
  109. mcp_agent/mcp/stdio.py +0 -111
  110. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
  111. mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
  112. mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
  113. mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
  114. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
  115. mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
  116. mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
  117. mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
  118. mcp_agent/resources/examples/researcher/researcher.py +0 -38
  119. mcp_agent/resources/examples/workflows/chaining.py +0 -44
  120. mcp_agent/resources/examples/workflows/evaluator.py +0 -78
  121. mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
  122. mcp_agent/resources/examples/workflows/human_input.py +0 -25
  123. mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
  124. mcp_agent/resources/examples/workflows/parallel.py +0 -78
  125. mcp_agent/resources/examples/workflows/router.py +0 -53
  126. mcp_agent/resources/examples/workflows/sse.py +0 -23
  127. mcp_agent/telemetry/__init__.py +0 -0
  128. mcp_agent/telemetry/usage_tracking.py +0 -18
  129. mcp_agent/workflows/__init__.py +0 -0
  130. mcp_agent/workflows/embedding/__init__.py +0 -0
  131. mcp_agent/workflows/embedding/embedding_base.py +0 -61
  132. mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
  133. mcp_agent/workflows/embedding/embedding_openai.py +0 -46
  134. mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
  135. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
  136. mcp_agent/workflows/intent_classifier/__init__.py +0 -0
  137. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
  138. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
  139. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
  140. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
  141. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
  142. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
  143. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
  144. mcp_agent/workflows/llm/__init__.py +0 -0
  145. mcp_agent/workflows/llm/augmented_llm.py +0 -753
  146. mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
  147. mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
  148. mcp_agent/workflows/llm/providers/__init__.py +0 -8
  149. mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
  150. mcp_agent/workflows/orchestrator/__init__.py +0 -0
  151. mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
  152. mcp_agent/workflows/parallel/__init__.py +0 -0
  153. mcp_agent/workflows/parallel/fan_in.py +0 -350
  154. mcp_agent/workflows/parallel/fan_out.py +0 -187
  155. mcp_agent/workflows/parallel/parallel_llm.py +0 -166
  156. mcp_agent/workflows/router/__init__.py +0 -0
  157. mcp_agent/workflows/router/router_base.py +0 -368
  158. mcp_agent/workflows/router/router_embedding.py +0 -240
  159. mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
  160. mcp_agent/workflows/router/router_embedding_openai.py +0 -59
  161. mcp_agent/workflows/router/router_llm.py +0 -320
  162. mcp_agent/workflows/swarm/__init__.py +0 -0
  163. mcp_agent/workflows/swarm/swarm.py +0 -320
  164. mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
  165. mcp_agent/workflows/swarm/swarm_openai.py +0 -41
  166. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
  167. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
  168. {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
  169. /mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_prompts.py +0 -0
@@ -7,15 +7,25 @@ Provides clean, testable classes for managing template substitution.
7
7
 
8
8
  import re
9
9
  from pathlib import Path
10
- from typing import Dict, List, Set, Any, Optional, Literal
10
+ from typing import Any, Dict, List, Literal, Optional, Set
11
11
 
12
+ from mcp.types import (
13
+ EmbeddedResource,
14
+ TextContent,
15
+ TextResourceContents,
16
+ )
12
17
  from pydantic import BaseModel, field_validator
13
18
 
14
- from mcp.types import TextContent, EmbeddedResource, TextResourceContents
15
19
  from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
16
20
  from mcp_agent.mcp.prompt_serialization import (
17
21
  multipart_messages_to_delimited_format,
18
22
  )
23
+ from mcp_agent.mcp.prompts.prompt_constants import (
24
+ ASSISTANT_DELIMITER,
25
+ DEFAULT_DELIMITER_MAP,
26
+ RESOURCE_DELIMITER,
27
+ USER_DELIMITER,
28
+ )
19
29
 
20
30
 
21
31
  class PromptMetadata(BaseModel):
@@ -67,9 +77,7 @@ class PromptContent(BaseModel):
67
77
  substituted = substituted.replace(make_placeholder(key), str(value))
68
78
  substituted_resources.append(substituted)
69
79
 
70
- return PromptContent(
71
- text=result, role=self.role, resources=substituted_resources
72
- )
80
+ return PromptContent(text=result, role=self.role, resources=substituted_resources)
73
81
 
74
82
 
75
83
  class PromptTemplate:
@@ -82,7 +90,7 @@ class PromptTemplate:
82
90
  template_text: str,
83
91
  delimiter_map: Optional[Dict[str, str]] = None,
84
92
  template_file_path: Optional[Path] = None,
85
- ):
93
+ ) -> None:
86
94
  """
87
95
  Initialize a prompt template.
88
96
 
@@ -93,11 +101,7 @@ class PromptTemplate:
93
101
  """
94
102
  self.template_text = template_text
95
103
  self.template_file_path = template_file_path
96
- self.delimiter_map = delimiter_map or {
97
- "---USER": "user",
98
- "---ASSISTANT": "assistant",
99
- "---RESOURCE": "resource",
100
- }
104
+ self.delimiter_map = delimiter_map or DEFAULT_DELIMITER_MAP
101
105
  self._template_variables = self._extract_template_variables(template_text)
102
106
  self._parsed_content = self._parse_template()
103
107
 
@@ -118,21 +122,17 @@ class PromptTemplate:
118
122
  A new PromptTemplate object
119
123
  """
120
124
  # Use default delimiter map if none provided
121
- delimiter_map = delimiter_map or {
122
- "---USER": "user",
123
- "---ASSISTANT": "assistant",
124
- "---RESOURCE": "resource",
125
- }
125
+ delimiter_map = delimiter_map or DEFAULT_DELIMITER_MAP
126
126
 
127
127
  # Convert to delimited format
128
128
  delimited_content = multipart_messages_to_delimited_format(
129
129
  messages,
130
130
  user_delimiter=next(
131
- (k for k, v in delimiter_map.items() if v == "user"), "---USER"
131
+ (k for k, v in delimiter_map.items() if v == "user"), USER_DELIMITER
132
132
  ),
133
133
  assistant_delimiter=next(
134
134
  (k for k, v in delimiter_map.items() if v == "assistant"),
135
- "---ASSISTANT",
135
+ ASSISTANT_DELIMITER,
136
136
  ),
137
137
  )
138
138
 
@@ -162,10 +162,8 @@ class PromptTemplate:
162
162
  Returns:
163
163
  List of PromptContent with substitutions applied
164
164
  """
165
- result = []
166
- for section in self._parsed_content:
167
- result.append(section.apply_substitutions(context))
168
- return result
165
+ # Create a new list with substitutions applied to each section
166
+ return [section.apply_substitutions(context) for section in self._parsed_content]
169
167
 
170
168
  def apply_substitutions_to_multipart(
171
169
  self, context: Dict[str, Any]
@@ -179,9 +177,11 @@ class PromptTemplate:
179
177
  Returns:
180
178
  List of PromptMessageMultipart objects with substitutions applied
181
179
  """
180
+ # First create a substituted template
182
181
  content_sections = self.apply_substitutions(context)
183
- multiparts = []
184
182
 
183
+ # Convert content sections to multipart messages
184
+ multiparts = []
185
185
  for section in content_sections:
186
186
  # Handle text content
187
187
  content_items = [TextContent(type="text", text=section.text)]
@@ -201,9 +201,7 @@ class PromptTemplate:
201
201
  )
202
202
  )
203
203
 
204
- multiparts.append(
205
- PromptMessageMultipart(role=section.role, content=content_items)
206
- )
204
+ multiparts.append(PromptMessageMultipart(role=section.role, content=content_items))
207
205
 
208
206
  return multiparts
209
207
 
@@ -241,9 +239,7 @@ class PromptTemplate:
241
239
  )
242
240
  )
243
241
 
244
- multiparts.append(
245
- PromptMessageMultipart(role=section.role, content=content_items)
246
- )
242
+ multiparts.append(PromptMessageMultipart(role=section.role, content=content_items))
247
243
 
248
244
  return multiparts
249
245
 
@@ -260,9 +256,7 @@ class PromptTemplate:
260
256
  first_non_empty_line = next((line for line in lines if line.strip()), "")
261
257
  delimiter_values = set(self.delimiter_map.keys())
262
258
 
263
- is_simple_mode = (
264
- first_non_empty_line and first_non_empty_line not in delimiter_values
265
- )
259
+ is_simple_mode = first_non_empty_line and first_non_empty_line not in delimiter_values
266
260
 
267
261
  if is_simple_mode:
268
262
  # Simple mode: treat the entire content as a single user message
@@ -330,18 +324,14 @@ class PromptTemplateLoader:
330
324
  Loads and processes prompt templates from files.
331
325
  """
332
326
 
333
- def __init__(self, delimiter_map: Optional[Dict[str, str]] = None):
327
+ def __init__(self, delimiter_map: Optional[Dict[str, str]] = None) -> None:
334
328
  """
335
329
  Initialize the loader with optional custom delimiters.
336
330
 
337
331
  Args:
338
332
  delimiter_map: Optional map of delimiters to roles
339
333
  """
340
- self.delimiter_map = delimiter_map or {
341
- "---USER": "user",
342
- "---ASSISTANT": "assistant",
343
- "---RESOURCE": "resource",
344
- }
334
+ self.delimiter_map = delimiter_map or DEFAULT_DELIMITER_MAP
345
335
 
346
336
  def load_from_file(self, file_path: Path) -> PromptTemplate:
347
337
  """
@@ -358,9 +348,7 @@ class PromptTemplateLoader:
358
348
 
359
349
  return PromptTemplate(content, self.delimiter_map, template_file_path=file_path)
360
350
 
361
- def load_from_multipart(
362
- self, messages: List[PromptMessageMultipart]
363
- ) -> PromptTemplate:
351
+ def load_from_multipart(self, messages: List[PromptMessageMultipart]) -> PromptTemplate:
364
352
  """
365
353
  Create a PromptTemplate from a list of PromptMessageMultipart objects.
366
354
 
@@ -370,19 +358,8 @@ class PromptTemplateLoader:
370
358
  Returns:
371
359
  A PromptTemplate object
372
360
  """
373
- delimited_content = multipart_messages_to_delimited_format(
374
- messages,
375
- user_delimiter=next(
376
- (k for k, v in self.delimiter_map.items() if v == "user"), "---USER"
377
- ),
378
- assistant_delimiter=next(
379
- (k for k, v in self.delimiter_map.items() if v == "assistant"),
380
- "---ASSISTANT",
381
- ),
382
- )
383
-
384
- content = "\n".join(delimited_content)
385
- return PromptTemplate(content, self.delimiter_map)
361
+ # Use the class method directly to avoid code duplication
362
+ return PromptTemplate.from_multipart_messages(messages, self.delimiter_map)
386
363
 
387
364
  def get_metadata(self, file_path: Path) -> PromptMetadata:
388
365
  """
@@ -401,18 +378,12 @@ class PromptTemplateLoader:
401
378
  first_non_empty_line = next((line for line in lines if line.strip()), "")
402
379
 
403
380
  # Check if we're in simple mode
404
- is_simple_mode = (
405
- first_non_empty_line and first_non_empty_line not in self.delimiter_map
406
- )
381
+ is_simple_mode = first_non_empty_line and first_non_empty_line not in self.delimiter_map
407
382
 
408
383
  if is_simple_mode:
409
384
  # In simple mode, use first line as description if it seems like one
410
385
  first_line = lines[0].strip() if lines else ""
411
- if (
412
- len(first_line) < 60
413
- and "{{" not in first_line
414
- and "}}" not in first_line
415
- ):
386
+ if len(first_line) < 60 and "{{" not in first_line and "}}" not in first_line:
416
387
  description = first_line
417
388
  else:
418
389
  description = f"Simple prompt: {file_path.stem}"
@@ -434,9 +405,7 @@ class PromptTemplateLoader:
434
405
  if first_role and first_content_index and first_content_index < len(lines):
435
406
  # Get up to 3 non-empty lines after the delimiter for a preview
436
407
  preview_lines = []
437
- for j in range(
438
- first_content_index, min(first_content_index + 10, len(lines))
439
- ):
408
+ for j in range(first_content_index, min(first_content_index + 10, len(lines))):
440
409
  stripped = lines[j].strip()
441
410
  if stripped and stripped not in self.delimiter_map:
442
411
  preview_lines.append(stripped)
@@ -453,7 +422,7 @@ class PromptTemplateLoader:
453
422
  # Extract resource paths from all sections that come after RESOURCE delimiters
454
423
  resource_paths = []
455
424
  resource_delimiter = next(
456
- (k for k, v in self.delimiter_map.items() if v == "resource"), "---RESOURCE"
425
+ (k for k, v in self.delimiter_map.items() if v == "resource"), RESOURCE_DELIMITER
457
426
  )
458
427
  for i, line in enumerate(lines):
459
428
  if line.strip() == resource_delimiter:
@@ -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.
@@ -141,9 +138,7 @@ def create_image_content(data: str, mime_type: str) -> ImageContent:
141
138
  )
142
139
 
143
140
 
144
- def create_blob_resource(
145
- resource_path: str, content: str, mime_type: str
146
- ) -> EmbeddedResource:
141
+ def create_blob_resource(resource_path: str, content: str, mime_type: str) -> EmbeddedResource:
147
142
  """Create an embedded resource for binary data"""
148
143
  return EmbeddedResource(
149
144
  type="resource",
@@ -155,9 +150,7 @@ def create_blob_resource(
155
150
  )
156
151
 
157
152
 
158
- def create_text_resource(
159
- resource_path: str, content: str, mime_type: str
160
- ) -> EmbeddedResource:
153
+ def create_text_resource(resource_path: str, content: str, mime_type: str) -> EmbeddedResource:
161
154
  """Create an embedded resource for text data"""
162
155
  return EmbeddedResource(
163
156
  type="resource",
mcp_agent/mcp/sampling.py CHANGED
@@ -2,17 +2,18 @@
2
2
  This simplified implementation directly converts between MCP types and PromptMessageMultipart.
3
3
  """
4
4
 
5
+ from typing import TYPE_CHECKING
6
+
5
7
  from mcp import ClientSession
6
- from mcp.types import (
7
- CreateMessageRequestParams,
8
- CreateMessageResult,
9
- )
8
+ from mcp.types import CreateMessageRequestParams, CreateMessageResult, TextContent
10
9
 
11
10
  from mcp_agent.core.agent_types import AgentConfig
11
+ from mcp_agent.llm.sampling_converter import SamplingConverter
12
12
  from mcp_agent.logging.logger import get_logger
13
13
  from mcp_agent.mcp.interfaces import AugmentedLLMProtocol
14
14
 
15
- from mcp_agent.workflows.llm.sampling_converter import SamplingConverter
15
+ if TYPE_CHECKING:
16
+ from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
16
17
 
17
18
  logger = get_logger(__name__)
18
19
 
@@ -31,8 +32,8 @@ def create_sampling_llm(
31
32
  Returns:
32
33
  An initialized LLM instance ready to use
33
34
  """
34
- from mcp_agent.workflows.llm.model_factory import ModelFactory
35
35
  from mcp_agent.agents.agent import Agent
36
+ from mcp_agent.llm.model_factory import ModelFactory
36
37
 
37
38
  app_context = None
38
39
  try:
@@ -58,9 +59,7 @@ def create_sampling_llm(
58
59
  return llm
59
60
 
60
61
 
61
- async def sample(
62
- mcp_ctx: ClientSession, params: CreateMessageRequestParams
63
- ) -> CreateMessageResult:
62
+ async def sample(mcp_ctx: ClientSession, params: CreateMessageRequestParams) -> CreateMessageResult:
64
63
  """
65
64
  Handle sampling requests from the MCP protocol using SamplingConverter.
66
65
 
@@ -105,13 +104,14 @@ async def sample(
105
104
  # Extract request parameters using our converter
106
105
  request_params = SamplingConverter.extract_request_params(params)
107
106
 
108
- # Use the new public apply_prompt method which is cleaner than calling the protected method
109
- llm_response = await llm.apply_prompt(conversation, request_params)
110
- logger.info(f"Complete sampling request : {llm_response[:50]}...")
107
+ llm_response: PromptMessageMultipart = await llm.generate(conversation, request_params)
108
+ logger.info(f"Complete sampling request : {llm_response.first_text()[:50]}...")
111
109
 
112
- # Create result using our converter
113
- return SamplingConverter.create_message_result(
114
- response=llm_response, model=model
110
+ return CreateMessageResult(
111
+ role=llm_response.role,
112
+ content=TextContent(type="text", text=llm_response.first_text()),
113
+ model=model,
114
+ stopReason="endTurn",
115
115
  )
116
116
  except Exception as e:
117
117
  logger.error(f"Error in sampling: {str(e)}")
@@ -121,7 +121,7 @@ async def sample(
121
121
 
122
122
 
123
123
  def sampling_agent_config(
124
- params: CreateMessageRequestParams = None,
124
+ params: CreateMessageRequestParams | None = None,
125
125
  ) -> AgentConfig:
126
126
  """
127
127
  Build a sampling AgentConfig based on request parameters.
@@ -134,7 +134,7 @@ def sampling_agent_config(
134
134
  """
135
135
  # Use systemPrompt from params if available, otherwise use default
136
136
  instruction = "You are a helpful AI Agent."
137
- if params and hasattr(params, "systemPrompt") and params.systemPrompt is not None:
137
+ if params and params.systemPrompt is not None:
138
138
  instruction = params.systemPrompt
139
139
 
140
140
  return AgentConfig(name="sampling_agent", instruction=instruction, servers=[])
@@ -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
- # Remove circular import
6
- from mcp_agent.core.agent_app import AgentApp
7
- from mcp.server.fastmcp import Context as MCPContext
6
+ # Import the DirectAgentApp instead of AgentApp
7
+ from mcp_agent.core.direct_agent_app import DirectAgentApp
8
8
 
9
9
 
10
10
  class AgentMCPServer:
@@ -12,24 +12,24 @@ class AgentMCPServer:
12
12
 
13
13
  def __init__(
14
14
  self,
15
- agent_app: AgentApp,
15
+ agent_app: DirectAgentApp,
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
22
  instructions=server_description
23
- or f"This server provides access to {len(agent_app.agents)} agents",
23
+ or f"This server provides access to {len(agent_app._agents)} agents",
24
24
  )
25
25
  self.setup_tools()
26
26
 
27
- def setup_tools(self):
27
+ def setup_tools(self) -> None:
28
28
  """Register all agents as MCP tools."""
29
29
  for agent_name, agent_proxy in self.agent_app._agents.items():
30
30
  self.register_agent_tools(agent_name, agent_proxy)
31
31
 
32
- def register_agent_tools(self, agent_name: str, agent_proxy):
32
+ def register_agent_tools(self, agent_name: str, agent_proxy) -> None:
33
33
  """Register tools for a specific agent."""
34
34
 
35
35
  # Basic send message tool
@@ -42,9 +42,7 @@ class AgentMCPServer:
42
42
 
43
43
  # Get the agent's context
44
44
  agent_context = None
45
- if hasattr(agent_proxy, "_agent") and hasattr(
46
- agent_proxy._agent, "context"
47
- ):
45
+ if hasattr(agent_proxy, "_agent") and hasattr(agent_proxy._agent, "context"):
48
46
  agent_context = agent_proxy._agent.context
49
47
 
50
48
  # Define the function to execute
@@ -57,7 +55,7 @@ class AgentMCPServer:
57
55
  else:
58
56
  return await execute_send()
59
57
 
60
- def run(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000):
58
+ def run(self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000) -> None:
61
59
  """Run the MCP server."""
62
60
  if transport == "sse":
63
61
  # For running as a web server
@@ -68,7 +66,7 @@ class AgentMCPServer:
68
66
 
69
67
  async def run_async(
70
68
  self, transport: str = "sse", host: str = "0.0.0.0", port: int = 8000
71
- ):
69
+ ) -> None:
72
70
  """Run the MCP server asynchronously."""
73
71
  if transport == "sse":
74
72
  self.mcp_server.settings.host = host
@@ -77,9 +75,7 @@ class AgentMCPServer:
77
75
  else: # stdio
78
76
  await self.mcp_server.run_stdio_async()
79
77
 
80
- async def with_bridged_context(
81
- self, agent_context, mcp_context, func, *args, **kwargs
82
- ):
78
+ async def with_bridged_context(self, agent_context, mcp_context, func, *args, **kwargs):
83
79
  """
84
80
  Execute a function with bridged context between MCP and agent
85
81
 
@@ -98,7 +94,7 @@ class AgentMCPServer:
98
94
  agent_context.mcp_context = mcp_context
99
95
 
100
96
  # Create bridged progress reporter
101
- async def bridged_progress(progress, total=None):
97
+ async def bridged_progress(progress, total=None) -> None:
102
98
  if mcp_context:
103
99
  await mcp_context.report_progress(progress, total)
104
100
  if original_progress_reporter:
@@ -9,24 +9,24 @@ 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
- from mcp import ClientSession
15
+ from mcp import ClientSession, stdio_client
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
29
+ from mcp_agent.mcp.logger_textio import get_stderr_handler
30
30
  from mcp_agent.mcp.mcp_connection_manager import MCPConnectionManager
31
31
 
32
32
  logger = get_logger(__name__)
@@ -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
 
@@ -67,9 +67,7 @@ class ServerRegistry:
67
67
  config_path (str): Path to the YAML configuration file.
68
68
  """
69
69
  self.registry = (
70
- self.load_registry_from_file(config_path)
71
- if config is None
72
- else config.mcp.servers
70
+ self.load_registry_from_file(config_path) if config is None else config.mcp.servers
73
71
  )
74
72
  self.init_hooks: Dict[str, InitHookCallable] = {}
75
73
  self.connection_manager = MCPConnectionManager(self)
@@ -117,9 +115,7 @@ class ServerRegistry:
117
115
  config = self.registry[server_name]
118
116
 
119
117
  read_timeout_seconds = (
120
- timedelta(config.read_timeout_seconds)
121
- if config.read_timeout_seconds
122
- else None
118
+ timedelta(config.read_timeout_seconds) if config.read_timeout_seconds else None
123
119
  )
124
120
 
125
121
  if config.transport == "stdio":
@@ -134,7 +130,8 @@ class ServerRegistry:
134
130
  env={**get_default_environment(), **(config.env or {})},
135
131
  )
136
132
 
137
- async with stdio_client_with_rich_stderr(server_params) as (
133
+ # Create a stderr handler that logs to our application logger
134
+ async with stdio_client(server_params, errlog=get_stderr_handler(server_name)) as (
138
135
  read_stream,
139
136
  write_stream,
140
137
  ):
@@ -144,9 +141,7 @@ class ServerRegistry:
144
141
  read_timeout_seconds,
145
142
  )
146
143
  async with session:
147
- logger.info(
148
- f"{server_name}: Connected to server using stdio transport."
149
- )
144
+ logger.info(f"{server_name}: Connected to server using stdio transport.")
150
145
  try:
151
146
  yield session
152
147
  finally:
@@ -164,9 +159,7 @@ class ServerRegistry:
164
159
  read_timeout_seconds,
165
160
  )
166
161
  async with session:
167
- logger.info(
168
- f"{server_name}: Connected to server using SSE transport."
169
- )
162
+ logger.info(f"{server_name}: Connected to server using SSE transport.")
170
163
  try:
171
164
  yield session
172
165
  finally:
@@ -215,9 +208,7 @@ class ServerRegistry:
215
208
  logger.info(f"{server_name}: Initialized.")
216
209
 
217
210
  intialization_callback = (
218
- init_hook
219
- if init_hook is not None
220
- else self.init_hooks.get(server_name)
211
+ init_hook if init_hook is not None else self.init_hooks.get(server_name)
221
212
  )
222
213
 
223
214
  if intialization_callback:
@@ -3,8 +3,9 @@ This demonstrates creating multiple agents and an orchestrator to coordinate the
3
3
  """
4
4
 
5
5
  import asyncio
6
+
6
7
  from mcp_agent.core.fastagent import FastAgent
7
- from mcp_agent.workflows.llm.augmented_llm import RequestParams
8
+ from mcp_agent.llm.augmented_llm import RequestParams
8
9
 
9
10
  # Create the application
10
11
  fast = FastAgent("Agent Builder")
@@ -62,7 +63,7 @@ if needed. Remind the Human of this.
62
63
  request_params=RequestParams(maxTokens=8192),
63
64
  max_iterations=5,
64
65
  )
65
- async def main():
66
+ async def main() -> None:
66
67
  async with fast.run() as agent:
67
68
  CODER_WARMUP = """
68
69
  - Read this paper: https://www.anthropic.com/research/building-effective-agents" to understand how