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.
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/METADATA +3 -4
- fast_agent_mcp-0.2.0.dist-info/RECORD +123 -0
- mcp_agent/__init__.py +75 -0
- mcp_agent/agents/agent.py +61 -415
- mcp_agent/agents/base_agent.py +522 -0
- mcp_agent/agents/workflow/__init__.py +1 -0
- mcp_agent/agents/workflow/chain_agent.py +173 -0
- mcp_agent/agents/workflow/evaluator_optimizer.py +362 -0
- mcp_agent/agents/workflow/orchestrator_agent.py +591 -0
- mcp_agent/{workflows/orchestrator → agents/workflow}/orchestrator_models.py +11 -21
- mcp_agent/agents/workflow/parallel_agent.py +182 -0
- mcp_agent/agents/workflow/router_agent.py +307 -0
- mcp_agent/app.py +15 -19
- mcp_agent/cli/commands/bootstrap.py +19 -38
- mcp_agent/cli/commands/config.py +4 -4
- mcp_agent/cli/commands/setup.py +7 -14
- mcp_agent/cli/main.py +7 -10
- mcp_agent/cli/terminal.py +3 -3
- mcp_agent/config.py +25 -40
- mcp_agent/context.py +12 -21
- mcp_agent/context_dependent.py +3 -5
- mcp_agent/core/agent_types.py +10 -7
- mcp_agent/core/direct_agent_app.py +179 -0
- mcp_agent/core/direct_decorators.py +443 -0
- mcp_agent/core/direct_factory.py +476 -0
- mcp_agent/core/enhanced_prompt.py +23 -55
- mcp_agent/core/exceptions.py +8 -8
- mcp_agent/core/fastagent.py +145 -371
- mcp_agent/core/interactive_prompt.py +424 -0
- mcp_agent/core/mcp_content.py +17 -17
- mcp_agent/core/prompt.py +6 -9
- mcp_agent/core/request_params.py +6 -3
- mcp_agent/core/validation.py +92 -18
- mcp_agent/executor/decorator_registry.py +9 -17
- mcp_agent/executor/executor.py +8 -17
- mcp_agent/executor/task_registry.py +2 -4
- mcp_agent/executor/temporal.py +19 -41
- mcp_agent/executor/workflow.py +3 -5
- mcp_agent/executor/workflow_signal.py +15 -21
- mcp_agent/human_input/handler.py +4 -7
- mcp_agent/human_input/types.py +2 -3
- mcp_agent/llm/__init__.py +2 -0
- mcp_agent/llm/augmented_llm.py +450 -0
- mcp_agent/llm/augmented_llm_passthrough.py +162 -0
- mcp_agent/llm/augmented_llm_playback.py +83 -0
- mcp_agent/llm/memory.py +103 -0
- mcp_agent/{workflows/llm → llm}/model_factory.py +22 -16
- mcp_agent/{workflows/llm → llm}/prompt_utils.py +1 -3
- mcp_agent/llm/providers/__init__.py +8 -0
- mcp_agent/{workflows/llm → llm/providers}/anthropic_utils.py +8 -25
- mcp_agent/{workflows/llm → llm/providers}/augmented_llm_anthropic.py +56 -194
- mcp_agent/llm/providers/augmented_llm_deepseek.py +53 -0
- mcp_agent/{workflows/llm → llm/providers}/augmented_llm_openai.py +99 -190
- mcp_agent/{workflows/llm → llm}/providers/multipart_converter_anthropic.py +72 -71
- mcp_agent/{workflows/llm → llm}/providers/multipart_converter_openai.py +65 -71
- mcp_agent/{workflows/llm → llm}/providers/openai_multipart.py +16 -44
- mcp_agent/{workflows/llm → llm/providers}/openai_utils.py +4 -4
- mcp_agent/{workflows/llm → llm}/providers/sampling_converter_anthropic.py +9 -11
- mcp_agent/{workflows/llm → llm}/providers/sampling_converter_openai.py +8 -12
- mcp_agent/{workflows/llm → llm}/sampling_converter.py +3 -31
- mcp_agent/llm/sampling_format_converter.py +37 -0
- mcp_agent/logging/events.py +1 -5
- mcp_agent/logging/json_serializer.py +7 -6
- mcp_agent/logging/listeners.py +20 -23
- mcp_agent/logging/logger.py +17 -19
- mcp_agent/logging/rich_progress.py +10 -8
- mcp_agent/logging/tracing.py +4 -6
- mcp_agent/logging/transport.py +22 -22
- mcp_agent/mcp/gen_client.py +1 -3
- mcp_agent/mcp/interfaces.py +117 -110
- mcp_agent/mcp/logger_textio.py +97 -0
- mcp_agent/mcp/mcp_agent_client_session.py +7 -7
- mcp_agent/mcp/mcp_agent_server.py +8 -8
- mcp_agent/mcp/mcp_aggregator.py +102 -143
- mcp_agent/mcp/mcp_connection_manager.py +20 -27
- mcp_agent/mcp/prompt_message_multipart.py +68 -16
- mcp_agent/mcp/prompt_render.py +77 -0
- mcp_agent/mcp/prompt_serialization.py +30 -48
- mcp_agent/mcp/prompts/prompt_constants.py +18 -0
- mcp_agent/mcp/prompts/prompt_helpers.py +327 -0
- mcp_agent/mcp/prompts/prompt_load.py +109 -0
- mcp_agent/mcp/prompts/prompt_server.py +155 -195
- mcp_agent/mcp/prompts/prompt_template.py +35 -66
- mcp_agent/mcp/resource_utils.py +7 -14
- mcp_agent/mcp/sampling.py +17 -17
- mcp_agent/mcp_server/agent_server.py +13 -17
- mcp_agent/mcp_server_registry.py +13 -22
- mcp_agent/resources/examples/{workflows → in_dev}/agent_build.py +3 -2
- mcp_agent/resources/examples/in_dev/slides.py +110 -0
- mcp_agent/resources/examples/internal/agent.py +6 -3
- mcp_agent/resources/examples/internal/fastagent.config.yaml +8 -2
- mcp_agent/resources/examples/internal/job.py +2 -1
- mcp_agent/resources/examples/internal/prompt_category.py +1 -1
- mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
- mcp_agent/resources/examples/internal/sizer.py +2 -1
- mcp_agent/resources/examples/internal/social.py +2 -1
- mcp_agent/resources/examples/prompting/agent.py +2 -1
- mcp_agent/resources/examples/prompting/image_server.py +4 -8
- mcp_agent/resources/examples/prompting/work_with_image.py +19 -0
- mcp_agent/ui/console_display.py +16 -20
- fast_agent_mcp-0.1.12.dist-info/RECORD +0 -161
- mcp_agent/core/agent_app.py +0 -646
- mcp_agent/core/agent_utils.py +0 -71
- mcp_agent/core/decorators.py +0 -455
- mcp_agent/core/factory.py +0 -463
- mcp_agent/core/proxies.py +0 -269
- mcp_agent/core/types.py +0 -24
- mcp_agent/eval/__init__.py +0 -0
- mcp_agent/mcp/stdio.py +0 -111
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -188
- mcp_agent/resources/examples/data-analysis/analysis.py +0 -65
- mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -41
- mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -1471
- mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +0 -53
- mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -66
- mcp_agent/resources/examples/researcher/researcher-eval.py +0 -53
- mcp_agent/resources/examples/researcher/researcher-imp.py +0 -190
- mcp_agent/resources/examples/researcher/researcher.py +0 -38
- mcp_agent/resources/examples/workflows/chaining.py +0 -44
- mcp_agent/resources/examples/workflows/evaluator.py +0 -78
- mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -24
- mcp_agent/resources/examples/workflows/human_input.py +0 -25
- mcp_agent/resources/examples/workflows/orchestrator.py +0 -73
- mcp_agent/resources/examples/workflows/parallel.py +0 -78
- mcp_agent/resources/examples/workflows/router.py +0 -53
- mcp_agent/resources/examples/workflows/sse.py +0 -23
- mcp_agent/telemetry/__init__.py +0 -0
- mcp_agent/telemetry/usage_tracking.py +0 -18
- mcp_agent/workflows/__init__.py +0 -0
- mcp_agent/workflows/embedding/__init__.py +0 -0
- mcp_agent/workflows/embedding/embedding_base.py +0 -61
- mcp_agent/workflows/embedding/embedding_cohere.py +0 -49
- mcp_agent/workflows/embedding/embedding_openai.py +0 -46
- mcp_agent/workflows/evaluator_optimizer/__init__.py +0 -0
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +0 -481
- mcp_agent/workflows/intent_classifier/__init__.py +0 -0
- mcp_agent/workflows/intent_classifier/intent_classifier_base.py +0 -120
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +0 -134
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +0 -45
- mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +0 -45
- mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +0 -161
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +0 -60
- mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +0 -60
- mcp_agent/workflows/llm/__init__.py +0 -0
- mcp_agent/workflows/llm/augmented_llm.py +0 -753
- mcp_agent/workflows/llm/augmented_llm_passthrough.py +0 -241
- mcp_agent/workflows/llm/augmented_llm_playback.py +0 -109
- mcp_agent/workflows/llm/providers/__init__.py +0 -8
- mcp_agent/workflows/llm/sampling_format_converter.py +0 -22
- mcp_agent/workflows/orchestrator/__init__.py +0 -0
- mcp_agent/workflows/orchestrator/orchestrator.py +0 -578
- mcp_agent/workflows/parallel/__init__.py +0 -0
- mcp_agent/workflows/parallel/fan_in.py +0 -350
- mcp_agent/workflows/parallel/fan_out.py +0 -187
- mcp_agent/workflows/parallel/parallel_llm.py +0 -166
- mcp_agent/workflows/router/__init__.py +0 -0
- mcp_agent/workflows/router/router_base.py +0 -368
- mcp_agent/workflows/router/router_embedding.py +0 -240
- mcp_agent/workflows/router/router_embedding_cohere.py +0 -59
- mcp_agent/workflows/router/router_embedding_openai.py +0 -59
- mcp_agent/workflows/router/router_llm.py +0 -320
- mcp_agent/workflows/swarm/__init__.py +0 -0
- mcp_agent/workflows/swarm/swarm.py +0 -320
- mcp_agent/workflows/swarm/swarm_anthropic.py +0 -42
- mcp_agent/workflows/swarm/swarm_openai.py +0 -41
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.1.12.dist-info → fast_agent_mcp-0.2.0.dist-info}/licenses/LICENSE +0 -0
- /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,
|
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"),
|
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
|
-
|
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
|
-
|
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
|
-
|
374
|
-
|
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"),
|
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:
|
mcp_agent/mcp/resource_utils.py
CHANGED
@@ -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
|
-
|
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
|
-
|
109
|
-
|
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
|
-
|
113
|
-
|
114
|
-
|
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
|
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
|
-
#
|
6
|
-
from mcp_agent.core.
|
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:
|
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.
|
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:
|
mcp_agent/mcp_server_registry.py
CHANGED
@@ -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
|
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
|
-
|
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.
|
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
|