fast-agent-mcp 0.2.57__py3-none-any.whl → 0.3.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.
Potentially problematic release.
This version of fast-agent-mcp might be problematic. Click here for more details.
- fast_agent/__init__.py +127 -0
- fast_agent/agents/__init__.py +36 -0
- {mcp_agent/core → fast_agent/agents}/agent_types.py +2 -1
- fast_agent/agents/llm_agent.py +217 -0
- fast_agent/agents/llm_decorator.py +486 -0
- mcp_agent/agents/base_agent.py → fast_agent/agents/mcp_agent.py +377 -385
- fast_agent/agents/tool_agent.py +168 -0
- {mcp_agent → fast_agent}/agents/workflow/chain_agent.py +43 -33
- {mcp_agent → fast_agent}/agents/workflow/evaluator_optimizer.py +31 -35
- {mcp_agent → fast_agent}/agents/workflow/iterative_planner.py +56 -47
- {mcp_agent → fast_agent}/agents/workflow/orchestrator_models.py +4 -4
- {mcp_agent → fast_agent}/agents/workflow/parallel_agent.py +34 -41
- {mcp_agent → fast_agent}/agents/workflow/router_agent.py +54 -39
- {mcp_agent → fast_agent}/cli/__main__.py +5 -3
- {mcp_agent → fast_agent}/cli/commands/check_config.py +95 -66
- {mcp_agent → fast_agent}/cli/commands/go.py +20 -11
- {mcp_agent → fast_agent}/cli/commands/quickstart.py +4 -4
- {mcp_agent → fast_agent}/cli/commands/server_helpers.py +1 -1
- {mcp_agent → fast_agent}/cli/commands/setup.py +64 -134
- {mcp_agent → fast_agent}/cli/commands/url_parser.py +9 -8
- {mcp_agent → fast_agent}/cli/main.py +36 -16
- {mcp_agent → fast_agent}/cli/terminal.py +2 -2
- {mcp_agent → fast_agent}/config.py +13 -2
- fast_agent/constants.py +8 -0
- {mcp_agent → fast_agent}/context.py +24 -19
- {mcp_agent → fast_agent}/context_dependent.py +9 -5
- fast_agent/core/__init__.py +17 -0
- {mcp_agent → fast_agent}/core/agent_app.py +39 -36
- fast_agent/core/core_app.py +135 -0
- {mcp_agent → fast_agent}/core/direct_decorators.py +12 -26
- {mcp_agent → fast_agent}/core/direct_factory.py +95 -73
- {mcp_agent → fast_agent/core}/executor/executor.py +4 -5
- {mcp_agent → fast_agent}/core/fastagent.py +32 -32
- fast_agent/core/logging/__init__.py +5 -0
- {mcp_agent → fast_agent/core}/logging/events.py +3 -3
- {mcp_agent → fast_agent/core}/logging/json_serializer.py +1 -1
- {mcp_agent → fast_agent/core}/logging/listeners.py +85 -7
- {mcp_agent → fast_agent/core}/logging/logger.py +7 -7
- {mcp_agent → fast_agent/core}/logging/transport.py +10 -11
- fast_agent/core/prompt.py +9 -0
- {mcp_agent → fast_agent}/core/validation.py +4 -4
- fast_agent/event_progress.py +61 -0
- fast_agent/history/history_exporter.py +44 -0
- {mcp_agent → fast_agent}/human_input/__init__.py +9 -12
- {mcp_agent → fast_agent}/human_input/elicitation_handler.py +26 -8
- {mcp_agent → fast_agent}/human_input/elicitation_state.py +7 -7
- {mcp_agent → fast_agent}/human_input/simple_form.py +6 -4
- {mcp_agent → fast_agent}/human_input/types.py +1 -18
- fast_agent/interfaces.py +228 -0
- fast_agent/llm/__init__.py +9 -0
- mcp_agent/llm/augmented_llm.py → fast_agent/llm/fastagent_llm.py +128 -218
- fast_agent/llm/internal/passthrough.py +137 -0
- mcp_agent/llm/augmented_llm_playback.py → fast_agent/llm/internal/playback.py +29 -25
- mcp_agent/llm/augmented_llm_silent.py → fast_agent/llm/internal/silent.py +10 -17
- fast_agent/llm/internal/slow.py +38 -0
- {mcp_agent → fast_agent}/llm/memory.py +40 -30
- {mcp_agent → fast_agent}/llm/model_database.py +35 -2
- {mcp_agent → fast_agent}/llm/model_factory.py +103 -77
- fast_agent/llm/model_info.py +126 -0
- {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/anthropic_utils.py +7 -7
- fast_agent/llm/provider/anthropic/llm_anthropic.py +603 -0
- {mcp_agent/llm/providers → fast_agent/llm/provider/anthropic}/multipart_converter_anthropic.py +79 -86
- fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
- fast_agent/llm/provider/bedrock/llm_bedrock.py +2192 -0
- {mcp_agent/llm/providers → fast_agent/llm/provider/google}/google_converter.py +66 -14
- fast_agent/llm/provider/google/llm_google_native.py +431 -0
- mcp_agent/llm/providers/augmented_llm_aliyun.py → fast_agent/llm/provider/openai/llm_aliyun.py +6 -7
- mcp_agent/llm/providers/augmented_llm_azure.py → fast_agent/llm/provider/openai/llm_azure.py +4 -4
- mcp_agent/llm/providers/augmented_llm_deepseek.py → fast_agent/llm/provider/openai/llm_deepseek.py +10 -11
- mcp_agent/llm/providers/augmented_llm_generic.py → fast_agent/llm/provider/openai/llm_generic.py +4 -4
- mcp_agent/llm/providers/augmented_llm_google_oai.py → fast_agent/llm/provider/openai/llm_google_oai.py +4 -4
- mcp_agent/llm/providers/augmented_llm_groq.py → fast_agent/llm/provider/openai/llm_groq.py +14 -16
- mcp_agent/llm/providers/augmented_llm_openai.py → fast_agent/llm/provider/openai/llm_openai.py +133 -206
- mcp_agent/llm/providers/augmented_llm_openrouter.py → fast_agent/llm/provider/openai/llm_openrouter.py +6 -6
- mcp_agent/llm/providers/augmented_llm_tensorzero_openai.py → fast_agent/llm/provider/openai/llm_tensorzero_openai.py +17 -16
- mcp_agent/llm/providers/augmented_llm_xai.py → fast_agent/llm/provider/openai/llm_xai.py +6 -6
- {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/multipart_converter_openai.py +125 -63
- {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_multipart.py +12 -12
- {mcp_agent/llm/providers → fast_agent/llm/provider/openai}/openai_utils.py +18 -16
- {mcp_agent → fast_agent}/llm/provider_key_manager.py +2 -2
- {mcp_agent → fast_agent}/llm/provider_types.py +2 -0
- {mcp_agent → fast_agent}/llm/sampling_converter.py +15 -12
- {mcp_agent → fast_agent}/llm/usage_tracking.py +23 -5
- fast_agent/mcp/__init__.py +43 -0
- {mcp_agent → fast_agent}/mcp/elicitation_factory.py +3 -3
- {mcp_agent → fast_agent}/mcp/elicitation_handlers.py +19 -10
- {mcp_agent → fast_agent}/mcp/gen_client.py +3 -3
- fast_agent/mcp/helpers/__init__.py +36 -0
- fast_agent/mcp/helpers/content_helpers.py +183 -0
- {mcp_agent → fast_agent}/mcp/helpers/server_config_helpers.py +8 -8
- {mcp_agent → fast_agent}/mcp/hf_auth.py +25 -23
- fast_agent/mcp/interfaces.py +93 -0
- {mcp_agent → fast_agent}/mcp/logger_textio.py +4 -4
- {mcp_agent → fast_agent}/mcp/mcp_agent_client_session.py +49 -44
- {mcp_agent → fast_agent}/mcp/mcp_aggregator.py +66 -115
- {mcp_agent → fast_agent}/mcp/mcp_connection_manager.py +16 -23
- {mcp_agent/core → fast_agent/mcp}/mcp_content.py +23 -15
- {mcp_agent → fast_agent}/mcp/mime_utils.py +39 -0
- fast_agent/mcp/prompt.py +159 -0
- mcp_agent/mcp/prompt_message_multipart.py → fast_agent/mcp/prompt_message_extended.py +27 -20
- {mcp_agent → fast_agent}/mcp/prompt_render.py +21 -19
- {mcp_agent → fast_agent}/mcp/prompt_serialization.py +46 -46
- fast_agent/mcp/prompts/__main__.py +7 -0
- {mcp_agent → fast_agent}/mcp/prompts/prompt_helpers.py +31 -30
- {mcp_agent → fast_agent}/mcp/prompts/prompt_load.py +8 -8
- {mcp_agent → fast_agent}/mcp/prompts/prompt_server.py +11 -19
- {mcp_agent → fast_agent}/mcp/prompts/prompt_template.py +18 -18
- {mcp_agent → fast_agent}/mcp/resource_utils.py +1 -1
- {mcp_agent → fast_agent}/mcp/sampling.py +31 -26
- {mcp_agent/mcp_server → fast_agent/mcp/server}/__init__.py +1 -1
- {mcp_agent/mcp_server → fast_agent/mcp/server}/agent_server.py +5 -6
- fast_agent/mcp/ui_agent.py +48 -0
- fast_agent/mcp/ui_mixin.py +209 -0
- fast_agent/mcp_server_registry.py +90 -0
- {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis-campaign.py +5 -4
- {mcp_agent → fast_agent}/resources/examples/data-analysis/analysis.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_forms_server.py +25 -3
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/forms_demo.py +3 -3
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character.py +2 -2
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/game_character_handler.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/tool_call.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_one.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/agent_two.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/researcher/researcher-eval.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/researcher/researcher-imp.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/researcher/researcher.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/tensorzero/agent.py +2 -2
- {mcp_agent → fast_agent}/resources/examples/tensorzero/image_demo.py +3 -3
- {mcp_agent → fast_agent}/resources/examples/tensorzero/simple_agent.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/workflows/chaining.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/workflows/evaluator.py +3 -3
- {mcp_agent → fast_agent}/resources/examples/workflows/human_input.py +5 -3
- {mcp_agent → fast_agent}/resources/examples/workflows/orchestrator.py +1 -1
- {mcp_agent → fast_agent}/resources/examples/workflows/parallel.py +2 -2
- {mcp_agent → fast_agent}/resources/examples/workflows/router.py +5 -2
- fast_agent/resources/setup/.gitignore +24 -0
- fast_agent/resources/setup/agent.py +18 -0
- fast_agent/resources/setup/fastagent.config.yaml +44 -0
- fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
- fast_agent/tools/elicitation.py +369 -0
- fast_agent/types/__init__.py +32 -0
- fast_agent/types/llm_stop_reason.py +77 -0
- fast_agent/ui/__init__.py +38 -0
- fast_agent/ui/console_display.py +1005 -0
- {mcp_agent/human_input → fast_agent/ui}/elicitation_form.py +56 -39
- mcp_agent/human_input/elicitation_forms.py → fast_agent/ui/elicitation_style.py +1 -1
- {mcp_agent/core → fast_agent/ui}/enhanced_prompt.py +96 -25
- {mcp_agent/core → fast_agent/ui}/interactive_prompt.py +330 -125
- fast_agent/ui/mcp_ui_utils.py +224 -0
- {mcp_agent → fast_agent/ui}/progress_display.py +2 -2
- {mcp_agent/logging → fast_agent/ui}/rich_progress.py +4 -4
- {mcp_agent/core → fast_agent/ui}/usage_display.py +3 -8
- {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/METADATA +7 -7
- fast_agent_mcp-0.3.0.dist-info/RECORD +202 -0
- fast_agent_mcp-0.3.0.dist-info/entry_points.txt +5 -0
- fast_agent_mcp-0.2.57.dist-info/RECORD +0 -192
- fast_agent_mcp-0.2.57.dist-info/entry_points.txt +0 -6
- mcp_agent/__init__.py +0 -114
- mcp_agent/agents/agent.py +0 -92
- mcp_agent/agents/workflow/__init__.py +0 -1
- mcp_agent/agents/workflow/orchestrator_agent.py +0 -597
- mcp_agent/app.py +0 -175
- mcp_agent/core/__init__.py +0 -26
- mcp_agent/core/prompt.py +0 -191
- mcp_agent/event_progress.py +0 -134
- mcp_agent/human_input/handler.py +0 -81
- mcp_agent/llm/__init__.py +0 -2
- mcp_agent/llm/augmented_llm_passthrough.py +0 -232
- mcp_agent/llm/augmented_llm_slow.py +0 -53
- mcp_agent/llm/providers/__init__.py +0 -8
- mcp_agent/llm/providers/augmented_llm_anthropic.py +0 -717
- mcp_agent/llm/providers/augmented_llm_bedrock.py +0 -1788
- mcp_agent/llm/providers/augmented_llm_google_native.py +0 -495
- mcp_agent/llm/providers/sampling_converter_anthropic.py +0 -57
- mcp_agent/llm/providers/sampling_converter_openai.py +0 -26
- mcp_agent/llm/sampling_format_converter.py +0 -37
- mcp_agent/logging/__init__.py +0 -0
- mcp_agent/mcp/__init__.py +0 -50
- mcp_agent/mcp/helpers/__init__.py +0 -25
- mcp_agent/mcp/helpers/content_helpers.py +0 -187
- mcp_agent/mcp/interfaces.py +0 -266
- mcp_agent/mcp/prompts/__init__.py +0 -0
- mcp_agent/mcp/prompts/__main__.py +0 -10
- mcp_agent/mcp_server_registry.py +0 -343
- mcp_agent/tools/tool_definition.py +0 -14
- mcp_agent/ui/console_display.py +0 -790
- mcp_agent/ui/console_display_legacy.py +0 -401
- {mcp_agent → fast_agent}/agents/workflow/orchestrator_prompts.py +0 -0
- {mcp_agent/agents → fast_agent/cli}/__init__.py +0 -0
- {mcp_agent → fast_agent}/cli/constants.py +0 -0
- {mcp_agent → fast_agent}/core/error_handling.py +0 -0
- {mcp_agent → fast_agent}/core/exceptions.py +0 -0
- {mcp_agent/cli → fast_agent/core/executor}/__init__.py +0 -0
- {mcp_agent → fast_agent/core}/executor/task_registry.py +0 -0
- {mcp_agent → fast_agent/core}/executor/workflow_signal.py +0 -0
- {mcp_agent → fast_agent}/human_input/form_fields.py +0 -0
- {mcp_agent → fast_agent}/llm/prompt_utils.py +0 -0
- {mcp_agent/core → fast_agent/llm}/request_params.py +0 -0
- {mcp_agent → fast_agent}/mcp/common.py +0 -0
- {mcp_agent/executor → fast_agent/mcp/prompts}/__init__.py +0 -0
- {mcp_agent → fast_agent}/mcp/prompts/prompt_constants.py +0 -0
- {mcp_agent → fast_agent}/py.typed +0 -0
- {mcp_agent → fast_agent}/resources/examples/data-analysis/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_account_server.py +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/elicitation_game_server.py +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +0 -0
- {mcp_agent → fast_agent}/resources/examples/researcher/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/.env.sample +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/Makefile +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/README.md +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/crab.png +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/docker-compose.yml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/Dockerfile +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/entrypoint.sh +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/mcp_server.py +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/mcp_server/pyproject.toml +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_schema.json +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +0 -0
- {mcp_agent → fast_agent}/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/fastagent.config.yaml +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/graded_report.md +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/short_story.md +0 -0
- {mcp_agent → fast_agent}/resources/examples/workflows/short_story.txt +0 -0
- {mcp_agent → fast_agent/ui}/console.py +0 -0
- {mcp_agent/core → fast_agent/ui}/mermaid_utils.py +0 -0
- {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.57.dist-info → fast_agent_mcp-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,22 +7,23 @@ and delegates operations to an attached AugmentedLLMProtocol instance.
|
|
|
7
7
|
|
|
8
8
|
import asyncio
|
|
9
9
|
import fnmatch
|
|
10
|
-
import
|
|
10
|
+
from abc import ABC
|
|
11
11
|
from typing import (
|
|
12
12
|
TYPE_CHECKING,
|
|
13
13
|
Any,
|
|
14
|
-
Callable,
|
|
15
14
|
Dict,
|
|
16
15
|
List,
|
|
17
16
|
Mapping,
|
|
18
17
|
Optional,
|
|
18
|
+
Sequence,
|
|
19
19
|
Tuple,
|
|
20
20
|
Type,
|
|
21
21
|
TypeVar,
|
|
22
22
|
Union,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
import mcp
|
|
26
|
+
from a2a.types import AgentCard, AgentSkill
|
|
26
27
|
from mcp.types import (
|
|
27
28
|
CallToolResult,
|
|
28
29
|
EmbeddedResource,
|
|
@@ -33,42 +34,38 @@ from mcp.types import (
|
|
|
33
34
|
TextContent,
|
|
34
35
|
Tool,
|
|
35
36
|
)
|
|
36
|
-
from opentelemetry import trace
|
|
37
37
|
from pydantic import BaseModel
|
|
38
38
|
|
|
39
|
-
from
|
|
40
|
-
from
|
|
41
|
-
from
|
|
42
|
-
from
|
|
43
|
-
from
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
from fast_agent.agents.agent_types import AgentConfig, AgentType
|
|
40
|
+
from fast_agent.agents.llm_agent import DEFAULT_CAPABILITIES
|
|
41
|
+
from fast_agent.agents.tool_agent import ToolAgent
|
|
42
|
+
from fast_agent.constants import HUMAN_INPUT_TOOL_NAME
|
|
43
|
+
from fast_agent.core.exceptions import PromptExitError
|
|
44
|
+
from fast_agent.core.logging.logger import get_logger
|
|
45
|
+
from fast_agent.interfaces import FastAgentLLMProtocol
|
|
46
|
+
from fast_agent.mcp.helpers.content_helpers import normalize_to_extended_list
|
|
47
|
+
from fast_agent.mcp.mcp_aggregator import MCPAggregator
|
|
48
|
+
from fast_agent.tools.elicitation import (
|
|
49
|
+
get_elicitation_tool,
|
|
50
|
+
run_elicitation_form,
|
|
51
|
+
set_elicitation_input_callback,
|
|
48
52
|
)
|
|
49
|
-
from
|
|
50
|
-
from mcp_agent.mcp.interfaces import AgentProtocol, AugmentedLLMProtocol
|
|
51
|
-
from mcp_agent.mcp.mcp_aggregator import MCPAggregator
|
|
52
|
-
from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
|
|
53
|
+
from fast_agent.types import PromptMessageExtended, RequestParams
|
|
53
54
|
|
|
54
55
|
# Define a TypeVar for models
|
|
55
56
|
ModelT = TypeVar("ModelT", bound=BaseModel)
|
|
56
57
|
|
|
57
58
|
# Define a TypeVar for AugmentedLLM and its subclasses
|
|
58
|
-
LLM = TypeVar("LLM", bound=
|
|
59
|
+
LLM = TypeVar("LLM", bound=FastAgentLLMProtocol)
|
|
59
60
|
|
|
60
|
-
HUMAN_INPUT_TOOL_NAME = "__human_input__"
|
|
61
61
|
if TYPE_CHECKING:
|
|
62
|
-
from
|
|
63
|
-
from mcp_agent.llm.usage_tracking import UsageAccumulator
|
|
62
|
+
from rich.text import Text
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
streaming=False, pushNotifications=False, stateTransitionHistory=False
|
|
68
|
-
)
|
|
64
|
+
from fast_agent.context import Context
|
|
65
|
+
from fast_agent.llm.usage_tracking import UsageAccumulator
|
|
69
66
|
|
|
70
67
|
|
|
71
|
-
class
|
|
68
|
+
class McpAgent(ABC, ToolAgent):
|
|
72
69
|
"""
|
|
73
70
|
A base Agent class that implements the AgentProtocol interface.
|
|
74
71
|
|
|
@@ -79,258 +76,141 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
79
76
|
def __init__(
|
|
80
77
|
self,
|
|
81
78
|
config: AgentConfig,
|
|
82
|
-
functions: Optional[List[Callable]] = None,
|
|
83
79
|
connection_persistence: bool = True,
|
|
84
|
-
|
|
85
|
-
context:
|
|
86
|
-
**kwargs
|
|
80
|
+
# legacy human_input_callback removed
|
|
81
|
+
context: "Context | None" = None,
|
|
82
|
+
**kwargs,
|
|
87
83
|
) -> None:
|
|
88
|
-
self.config = config
|
|
89
|
-
|
|
90
84
|
super().__init__(
|
|
85
|
+
config=config,
|
|
91
86
|
context=context,
|
|
87
|
+
**kwargs,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Create aggregator with composition
|
|
91
|
+
self._aggregator = MCPAggregator(
|
|
92
92
|
server_names=self.config.servers,
|
|
93
93
|
connection_persistence=connection_persistence,
|
|
94
94
|
name=self.config.name,
|
|
95
|
+
context=context,
|
|
96
|
+
config=self.config, # Pass the full config for access to elicitation_handler
|
|
95
97
|
**kwargs,
|
|
96
98
|
)
|
|
97
99
|
|
|
98
|
-
self._context = context
|
|
99
|
-
self.tracer = trace.get_tracer(__name__)
|
|
100
|
-
self.name = self.config.name
|
|
101
100
|
self.instruction = self.config.instruction
|
|
102
|
-
self.
|
|
103
|
-
self.
|
|
104
|
-
self.logger = get_logger(f"{__name__}.{self.name}")
|
|
101
|
+
self.executor = context.executor if context else None
|
|
102
|
+
self.logger = get_logger(f"{__name__}.{self._name}")
|
|
105
103
|
|
|
106
104
|
# Store the default request params from config
|
|
107
105
|
self._default_request_params = self.config.default_request_params
|
|
108
106
|
|
|
109
|
-
#
|
|
110
|
-
self._llm:
|
|
107
|
+
# set with the "attach" method
|
|
108
|
+
self._llm: FastAgentLLMProtocol | None = None
|
|
111
109
|
|
|
112
|
-
#
|
|
113
|
-
self.
|
|
110
|
+
# Instantiate human input tool once if enabled in config
|
|
111
|
+
self._human_input_tool: Tool | None = None
|
|
112
|
+
if self.config.human_input:
|
|
113
|
+
try:
|
|
114
|
+
self._human_input_tool = get_elicitation_tool()
|
|
115
|
+
except Exception:
|
|
116
|
+
self._human_input_tool = None
|
|
114
117
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
# Register the MCP UI handler as the elicitation callback so fast_agent.tools can call it
|
|
119
|
+
# without importing MCP types. This avoids circular imports and ensures the callback is ready.
|
|
120
|
+
try:
|
|
121
|
+
from fast_agent.human_input.elicitation_handler import elicitation_input_callback
|
|
122
|
+
from fast_agent.human_input.types import HumanInputRequest
|
|
123
|
+
|
|
124
|
+
async def _mcp_elicitation_adapter(
|
|
125
|
+
request_payload: dict,
|
|
126
|
+
agent_name: str | None = None,
|
|
127
|
+
server_name: str | None = None,
|
|
128
|
+
server_info: dict | None = None,
|
|
129
|
+
) -> str:
|
|
130
|
+
req = HumanInputRequest(**request_payload)
|
|
131
|
+
resp = await elicitation_input_callback(
|
|
132
|
+
request=req,
|
|
133
|
+
agent_name=agent_name,
|
|
134
|
+
server_name=server_name,
|
|
135
|
+
server_info=server_info,
|
|
136
|
+
)
|
|
137
|
+
return resp.response if isinstance(resp.response, str) else str(resp.response)
|
|
138
|
+
|
|
139
|
+
set_elicitation_input_callback(_mcp_elicitation_adapter)
|
|
140
|
+
except Exception:
|
|
141
|
+
# If UI handler import fails, leave callback unset; tool will error with a clear message
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
async def __aenter__(self):
|
|
145
|
+
"""Initialize the agent and its MCP aggregator."""
|
|
146
|
+
await self._aggregator.__aenter__()
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
150
|
+
"""Clean up the agent and its MCP aggregator."""
|
|
151
|
+
await self._aggregator.__aexit__(exc_type, exc_val, exc_tb)
|
|
121
152
|
|
|
122
153
|
async def initialize(self) -> None:
|
|
123
154
|
"""
|
|
124
155
|
Initialize the agent and connect to the MCP servers.
|
|
125
156
|
NOTE: This method is called automatically when the agent is used as an async context manager.
|
|
126
157
|
"""
|
|
127
|
-
await self.__aenter__()
|
|
128
|
-
|
|
129
|
-
async def attach_llm(
|
|
130
|
-
self,
|
|
131
|
-
llm_factory: Union[Type[AugmentedLLMProtocol], Callable[..., AugmentedLLMProtocol]],
|
|
132
|
-
model: Optional[str] = None,
|
|
133
|
-
request_params: Optional[RequestParams] = None,
|
|
134
|
-
**additional_kwargs,
|
|
135
|
-
) -> AugmentedLLMProtocol:
|
|
136
|
-
"""
|
|
137
|
-
Create and attach an LLM instance to this agent.
|
|
138
|
-
|
|
139
|
-
Parameters have the following precedence (highest to lowest):
|
|
140
|
-
1. Explicitly passed parameters to this method
|
|
141
|
-
2. Agent's default_request_params
|
|
142
|
-
3. LLM's default values
|
|
143
|
-
|
|
144
|
-
Args:
|
|
145
|
-
llm_factory: A class or callable that constructs an AugmentedLLM
|
|
146
|
-
model: Optional model name override
|
|
147
|
-
request_params: Optional request parameters override
|
|
148
|
-
**additional_kwargs: Additional parameters passed to the LLM constructor
|
|
149
|
-
|
|
150
|
-
Returns:
|
|
151
|
-
The created LLM instance
|
|
152
|
-
"""
|
|
153
|
-
# Start with agent's default params
|
|
154
|
-
effective_params = (
|
|
155
|
-
self._default_request_params.model_copy() if self._default_request_params else None
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# Override with explicitly passed request_params
|
|
159
|
-
if request_params:
|
|
160
|
-
if effective_params:
|
|
161
|
-
# Update non-None values
|
|
162
|
-
for k, v in request_params.model_dump(exclude_unset=True).items():
|
|
163
|
-
if v is not None:
|
|
164
|
-
setattr(effective_params, k, v)
|
|
165
|
-
else:
|
|
166
|
-
effective_params = request_params
|
|
167
|
-
|
|
168
|
-
# Override model if explicitly specified
|
|
169
|
-
if model and effective_params:
|
|
170
|
-
effective_params.model = model
|
|
171
|
-
|
|
172
|
-
# Create the LLM instance
|
|
173
|
-
self._llm = llm_factory(
|
|
174
|
-
agent=self, request_params=effective_params, context=self._context, **additional_kwargs
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
return self._llm
|
|
158
|
+
await self.__aenter__()
|
|
178
159
|
|
|
179
160
|
async def shutdown(self) -> None:
|
|
180
161
|
"""
|
|
181
162
|
Shutdown the agent and close all MCP server connections.
|
|
182
163
|
NOTE: This method is called automatically when the agent is used as an async context manager.
|
|
183
164
|
"""
|
|
184
|
-
await
|
|
165
|
+
await self._aggregator.close()
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def initialized(self) -> bool:
|
|
169
|
+
"""Check if both the agent and aggregator are initialized."""
|
|
170
|
+
return self._initialized and self._aggregator.initialized
|
|
171
|
+
|
|
172
|
+
@initialized.setter
|
|
173
|
+
def initialized(self, value: bool) -> None:
|
|
174
|
+
"""Set the initialized state of both agent and aggregator."""
|
|
175
|
+
self._initialized = value
|
|
176
|
+
self._aggregator.initialized = value
|
|
185
177
|
|
|
186
178
|
async def __call__(
|
|
187
179
|
self,
|
|
188
|
-
message: Union[
|
|
189
|
-
|
|
190
|
-
|
|
180
|
+
message: Union[
|
|
181
|
+
str,
|
|
182
|
+
PromptMessage,
|
|
183
|
+
PromptMessageExtended,
|
|
184
|
+
Sequence[Union[str, PromptMessage, PromptMessageExtended]],
|
|
185
|
+
],
|
|
191
186
|
) -> str:
|
|
192
|
-
|
|
193
|
-
Make the agent callable to send messages or start an interactive prompt.
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
message: Optional message to send to the agent
|
|
197
|
-
agent_name: Optional name of the agent (for consistency with DirectAgentApp)
|
|
198
|
-
default: Default message to use in interactive prompt mode
|
|
199
|
-
|
|
200
|
-
Returns:
|
|
201
|
-
The agent's response as a string or the result of the interactive session
|
|
202
|
-
"""
|
|
203
|
-
if message:
|
|
204
|
-
return await self.send(message)
|
|
205
|
-
return await self.prompt(default_prompt=default_prompt)
|
|
206
|
-
|
|
207
|
-
async def generate_str(self, message: str, request_params: RequestParams | None) -> str:
|
|
208
|
-
result: PromptMessageMultipart = await self.generate([Prompt.user(message)], request_params)
|
|
209
|
-
return result.first_text()
|
|
187
|
+
return await self.send(message)
|
|
210
188
|
|
|
211
189
|
async def send(
|
|
212
|
-
self,
|
|
213
|
-
message: Union[
|
|
214
|
-
|
|
190
|
+
self,
|
|
191
|
+
message: Union[
|
|
192
|
+
str,
|
|
193
|
+
PromptMessage,
|
|
194
|
+
PromptMessageExtended,
|
|
195
|
+
Sequence[Union[str, PromptMessage, PromptMessageExtended]],
|
|
196
|
+
],
|
|
197
|
+
request_params: RequestParams | None = None,
|
|
215
198
|
) -> str:
|
|
216
199
|
"""
|
|
217
200
|
Send a message to the agent and get a response.
|
|
218
201
|
|
|
219
202
|
Args:
|
|
220
203
|
message: Message content in various formats:
|
|
221
|
-
- String: Converted to a user
|
|
222
|
-
- PromptMessage: Converted to
|
|
223
|
-
-
|
|
204
|
+
- String: Converted to a user PromptMessageExtended
|
|
205
|
+
- PromptMessage: Converted to PromptMessageExtended
|
|
206
|
+
- PromptMessageExtended: Used directly
|
|
224
207
|
- request_params: Optional request parameters
|
|
225
208
|
|
|
226
209
|
Returns:
|
|
227
210
|
The agent's response as a string
|
|
228
211
|
"""
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
# Use the LLM to generate a response
|
|
233
|
-
response = await self.generate([prompt], request_params)
|
|
234
|
-
return response.all_text()
|
|
235
|
-
|
|
236
|
-
def _normalize_message_input(
|
|
237
|
-
self, message: Union[str, PromptMessage, PromptMessageMultipart]
|
|
238
|
-
) -> PromptMessageMultipart:
|
|
239
|
-
"""
|
|
240
|
-
Convert a message of any supported type to PromptMessageMultipart.
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
message: Message in various formats (string, PromptMessage, or PromptMessageMultipart)
|
|
244
|
-
|
|
245
|
-
Returns:
|
|
246
|
-
A PromptMessageMultipart object
|
|
247
|
-
"""
|
|
248
|
-
# Handle single message
|
|
249
|
-
if isinstance(message, str):
|
|
250
|
-
return Prompt.user(message)
|
|
251
|
-
elif isinstance(message, PromptMessage):
|
|
252
|
-
return PromptMessageMultipart(role=message.role, content=[message.content])
|
|
253
|
-
elif isinstance(message, PromptMessageMultipart):
|
|
254
|
-
return message
|
|
255
|
-
else:
|
|
256
|
-
# Try to convert to string as fallback
|
|
257
|
-
return Prompt.user(str(message))
|
|
258
|
-
|
|
259
|
-
async def prompt(self, default_prompt: str = "") -> str:
|
|
260
|
-
"""
|
|
261
|
-
Start an interactive prompt session with the agent.
|
|
262
|
-
|
|
263
|
-
Args:
|
|
264
|
-
default_prompt: The initial prompt to send to the agent
|
|
265
|
-
|
|
266
|
-
Returns:
|
|
267
|
-
The result of the interactive session
|
|
268
|
-
"""
|
|
269
|
-
...
|
|
270
|
-
|
|
271
|
-
async def request_human_input(self, request: HumanInputRequest) -> str:
|
|
272
|
-
"""
|
|
273
|
-
Request input from a human user. Pauses the workflow until input is received.
|
|
274
|
-
|
|
275
|
-
Args:
|
|
276
|
-
request: The human input request
|
|
277
|
-
|
|
278
|
-
Returns:
|
|
279
|
-
The input provided by the human
|
|
280
|
-
|
|
281
|
-
Raises:
|
|
282
|
-
TimeoutError: If the timeout is exceeded
|
|
283
|
-
"""
|
|
284
|
-
if not self.human_input_callback:
|
|
285
|
-
raise ValueError("Human input callback not set")
|
|
286
|
-
|
|
287
|
-
# Generate a unique ID for this request to avoid signal collisions
|
|
288
|
-
request_id = f"{HUMAN_INPUT_SIGNAL_NAME}_{self.name}_{uuid.uuid4()}"
|
|
289
|
-
request.request_id = request_id
|
|
290
|
-
# Use metadata as a dictionary to pass agent name
|
|
291
|
-
request.metadata = {"agent_name": self.name}
|
|
292
|
-
self.logger.debug("Requesting human input:", data=request)
|
|
293
|
-
|
|
294
|
-
if not self.executor:
|
|
295
|
-
raise ValueError("No executor available")
|
|
296
|
-
|
|
297
|
-
async def call_callback_and_signal() -> None:
|
|
298
|
-
try:
|
|
299
|
-
assert self.human_input_callback is not None
|
|
300
|
-
user_input = await self.human_input_callback(request)
|
|
301
|
-
|
|
302
|
-
self.logger.debug("Received human input:", data=user_input)
|
|
303
|
-
await self.executor.signal(signal_name=request_id, payload=user_input)
|
|
304
|
-
except PromptExitError as e:
|
|
305
|
-
# Propagate the exit error through the signal system
|
|
306
|
-
self.logger.info("User requested to exit session")
|
|
307
|
-
await self.executor.signal(
|
|
308
|
-
signal_name=request_id,
|
|
309
|
-
payload={"exit_requested": True, "error": str(e)},
|
|
310
|
-
)
|
|
311
|
-
except Exception as e:
|
|
312
|
-
await self.executor.signal(
|
|
313
|
-
request_id, payload=f"Error getting human input: {str(e)}"
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
asyncio.create_task(call_callback_and_signal())
|
|
317
|
-
|
|
318
|
-
self.logger.debug("Waiting for human input signal")
|
|
319
|
-
|
|
320
|
-
# Wait for signal (workflow is paused here)
|
|
321
|
-
result = await self.executor.wait_for_signal(
|
|
322
|
-
signal_name=request_id,
|
|
323
|
-
request_id=request_id,
|
|
324
|
-
workflow_id=request.workflow_id,
|
|
325
|
-
signal_description=request.description or request.prompt,
|
|
326
|
-
timeout_seconds=request.timeout_seconds,
|
|
327
|
-
signal_type=HumanInputResponse,
|
|
328
|
-
)
|
|
329
|
-
|
|
330
|
-
if isinstance(result, dict) and result.get("exit_requested", False):
|
|
331
|
-
raise PromptExitError(result.get("error", "User requested to exit FastAgent session"))
|
|
332
|
-
self.logger.debug("Received human input signal", data=result)
|
|
333
|
-
return result
|
|
212
|
+
response = await self.generate(message, request_params)
|
|
213
|
+
return response.last_text() or ""
|
|
334
214
|
|
|
335
215
|
def _matches_pattern(self, name: str, pattern: str, server_name: str) -> bool:
|
|
336
216
|
"""
|
|
@@ -359,11 +239,8 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
359
239
|
Returns:
|
|
360
240
|
ListToolsResult with available tools
|
|
361
241
|
"""
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
# Get all tools from the parent class
|
|
366
|
-
result = await super().list_tools()
|
|
242
|
+
# Get all tools from the aggregator
|
|
243
|
+
result = await self._aggregator.list_tools()
|
|
367
244
|
|
|
368
245
|
# Apply filtering if tools are specified in config
|
|
369
246
|
if self.config.tools is not None:
|
|
@@ -378,27 +255,16 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
378
255
|
|
|
379
256
|
# Check if this server has tool filters
|
|
380
257
|
if server_name and server_name in self.config.tools:
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
258
|
+
# Check if tool matches any pattern for this server
|
|
259
|
+
for pattern in self.config.tools[server_name]:
|
|
260
|
+
if self._matches_pattern(tool.name, pattern, server_name):
|
|
261
|
+
filtered_tools.append(tool)
|
|
262
|
+
break
|
|
386
263
|
result.tools = filtered_tools
|
|
387
264
|
|
|
388
|
-
if
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
# Add a human_input_callback as a tool
|
|
392
|
-
from mcp.server.fastmcp.tools import Tool as FastTool
|
|
393
|
-
|
|
394
|
-
human_input_tool: FastTool = FastTool.from_function(self.request_human_input)
|
|
395
|
-
result.tools.append(
|
|
396
|
-
Tool(
|
|
397
|
-
name=HUMAN_INPUT_TOOL_NAME,
|
|
398
|
-
description=human_input_tool.description,
|
|
399
|
-
inputSchema=human_input_tool.parameters,
|
|
400
|
-
)
|
|
401
|
-
)
|
|
265
|
+
# Append human input tool if enabled and available
|
|
266
|
+
if self.config.human_input and getattr(self, "_human_input_tool", None):
|
|
267
|
+
result.tools.append(self._human_input_tool)
|
|
402
268
|
|
|
403
269
|
return result
|
|
404
270
|
|
|
@@ -414,50 +280,43 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
414
280
|
Result of the tool call
|
|
415
281
|
"""
|
|
416
282
|
if name == HUMAN_INPUT_TOOL_NAME:
|
|
417
|
-
# Call the human input tool
|
|
283
|
+
# Call the elicitation-backed human input tool
|
|
418
284
|
return await self._call_human_input_tool(arguments)
|
|
419
285
|
else:
|
|
420
|
-
return await
|
|
286
|
+
return await self._aggregator.call_tool(name, arguments)
|
|
421
287
|
|
|
422
288
|
async def _call_human_input_tool(
|
|
423
289
|
self, arguments: Dict[str, Any] | None = None
|
|
424
290
|
) -> CallToolResult:
|
|
425
291
|
"""
|
|
426
|
-
Handle human input
|
|
292
|
+
Handle human input via an elicitation form.
|
|
427
293
|
|
|
428
|
-
|
|
429
|
-
|
|
294
|
+
Expected inputs:
|
|
295
|
+
- Either an object with optional 'message' and a 'schema' JSON Schema (object), or
|
|
296
|
+
- The JSON Schema (object) itself as the arguments.
|
|
430
297
|
|
|
431
|
-
|
|
432
|
-
|
|
298
|
+
Constraints:
|
|
299
|
+
- No more than 7 top-level properties are allowed in the schema.
|
|
433
300
|
"""
|
|
434
|
-
# Handle human input request
|
|
435
301
|
try:
|
|
436
|
-
#
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
result = await self.request_human_input(request=request)
|
|
453
|
-
|
|
454
|
-
# Use response attribute if available, otherwise use the result directly
|
|
455
|
-
response_text = (
|
|
456
|
-
result.response if isinstance(result, HumanInputResponse) else str(result)
|
|
457
|
-
)
|
|
458
|
-
|
|
302
|
+
# Run via shared tool runner
|
|
303
|
+
resp_text = await run_elicitation_form(arguments, agent_name=self._name)
|
|
304
|
+
if resp_text == "__DECLINED__":
|
|
305
|
+
return CallToolResult(
|
|
306
|
+
isError=False,
|
|
307
|
+
content=[TextContent(type="text", text="The Human declined the input request")],
|
|
308
|
+
)
|
|
309
|
+
if resp_text in ("__CANCELLED__", "__DISABLE_SERVER__"):
|
|
310
|
+
return CallToolResult(
|
|
311
|
+
isError=False,
|
|
312
|
+
content=[
|
|
313
|
+
TextContent(type="text", text="The Human cancelled the input request")
|
|
314
|
+
],
|
|
315
|
+
)
|
|
316
|
+
# Success path: return the (JSON) response as-is
|
|
459
317
|
return CallToolResult(
|
|
460
|
-
|
|
318
|
+
isError=False,
|
|
319
|
+
content=[TextContent(type="text", text=resp_text)],
|
|
461
320
|
)
|
|
462
321
|
|
|
463
322
|
except PromptExitError:
|
|
@@ -476,7 +335,6 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
476
335
|
import traceback
|
|
477
336
|
|
|
478
337
|
print(f"Error in _call_human_input_tool: {traceback.format_exc()}")
|
|
479
|
-
|
|
480
338
|
return CallToolResult(
|
|
481
339
|
isError=True,
|
|
482
340
|
content=[TextContent(type="text", text=f"Error requesting human input: {str(e)}")],
|
|
@@ -486,6 +344,7 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
486
344
|
self,
|
|
487
345
|
prompt_name: str,
|
|
488
346
|
arguments: Dict[str, str] | None = None,
|
|
347
|
+
namespace: str | None = None,
|
|
489
348
|
server_name: str | None = None,
|
|
490
349
|
) -> GetPromptResult:
|
|
491
350
|
"""
|
|
@@ -494,20 +353,21 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
494
353
|
Args:
|
|
495
354
|
prompt_name: Name of the prompt, optionally namespaced
|
|
496
355
|
arguments: Optional dictionary of arguments to pass to the prompt template
|
|
497
|
-
|
|
356
|
+
namespace: Optional namespace (server) to get the prompt from
|
|
498
357
|
|
|
499
358
|
Returns:
|
|
500
359
|
GetPromptResult containing the prompt information
|
|
501
360
|
"""
|
|
502
|
-
|
|
361
|
+
target = namespace if namespace is not None else server_name
|
|
362
|
+
return await self._aggregator.get_prompt(prompt_name, arguments, target)
|
|
503
363
|
|
|
504
364
|
async def apply_prompt(
|
|
505
365
|
self,
|
|
506
366
|
prompt: Union[str, GetPromptResult],
|
|
507
367
|
arguments: Dict[str, str] | None = None,
|
|
508
|
-
agent_name: str | None = None,
|
|
509
|
-
server_name: str | None = None,
|
|
510
368
|
as_template: bool = False,
|
|
369
|
+
namespace: str | None = None,
|
|
370
|
+
**_: Any,
|
|
511
371
|
) -> str:
|
|
512
372
|
"""
|
|
513
373
|
Apply an MCP Server Prompt by name or GetPromptResult and return the assistant's response.
|
|
@@ -519,9 +379,8 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
519
379
|
Args:
|
|
520
380
|
prompt: The name of the prompt to apply OR a GetPromptResult object
|
|
521
381
|
arguments: Optional dictionary of string arguments to pass to the prompt template
|
|
522
|
-
agent_name: Optional agent name (ignored at this level, used by multi-agent apps)
|
|
523
|
-
server_name: Optional name of the server to get the prompt from
|
|
524
382
|
as_template: If True, store as persistent template (always included in context)
|
|
383
|
+
namespace: Optional namespace/server to resolve the prompt from
|
|
525
384
|
|
|
526
385
|
Returns:
|
|
527
386
|
The assistant's response or error message
|
|
@@ -533,7 +392,7 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
533
392
|
# Get the prompt - this will search all servers if needed
|
|
534
393
|
self.logger.debug(f"Loading prompt '{prompt_name}'")
|
|
535
394
|
prompt_result: GetPromptResult = await self.get_prompt(
|
|
536
|
-
prompt_name, arguments,
|
|
395
|
+
prompt_name, arguments, namespace
|
|
537
396
|
)
|
|
538
397
|
|
|
539
398
|
if not prompt_result or not prompt_result.messages:
|
|
@@ -557,7 +416,7 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
557
416
|
self.logger.debug(f"Using prompt '{namespaced_name}'")
|
|
558
417
|
|
|
559
418
|
# Convert prompt messages to multipart format using the safer method
|
|
560
|
-
multipart_messages =
|
|
419
|
+
multipart_messages = PromptMessageExtended.from_get_prompt_result(prompt_result)
|
|
561
420
|
|
|
562
421
|
if as_template:
|
|
563
422
|
# Use apply_prompt_template to store as persistent prompt messages
|
|
@@ -579,13 +438,13 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
579
438
|
server_name: Optional name of the MCP server to retrieve the resource from
|
|
580
439
|
|
|
581
440
|
Returns:
|
|
582
|
-
List of EmbeddedResource objects ready to use in a
|
|
441
|
+
List of EmbeddedResource objects ready to use in a PromptMessageExtended
|
|
583
442
|
|
|
584
443
|
Raises:
|
|
585
444
|
ValueError: If the server doesn't exist or the resource couldn't be found
|
|
586
445
|
"""
|
|
587
446
|
# Get the raw resource result
|
|
588
|
-
result: ReadResourceResult = await self.get_resource(resource_uri, server_name)
|
|
447
|
+
result: ReadResourceResult = await self._aggregator.get_resource(resource_uri, server_name)
|
|
589
448
|
|
|
590
449
|
# Convert each resource content to an EmbeddedResource
|
|
591
450
|
embedded_resources: List[EmbeddedResource] = []
|
|
@@ -597,10 +456,32 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
597
456
|
|
|
598
457
|
return embedded_resources
|
|
599
458
|
|
|
459
|
+
async def get_resource(
|
|
460
|
+
self, resource_uri: str, namespace: str | None = None, server_name: str | None = None
|
|
461
|
+
) -> ReadResourceResult:
|
|
462
|
+
"""
|
|
463
|
+
Get a resource from an MCP server.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
resource_uri: URI of the resource to retrieve
|
|
467
|
+
namespace: Optional namespace (server) to retrieve the resource from
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
ReadResourceResult containing the resource data
|
|
471
|
+
|
|
472
|
+
Raises:
|
|
473
|
+
ValueError: If the server doesn't exist or the resource couldn't be found
|
|
474
|
+
"""
|
|
475
|
+
# Get the raw resource result
|
|
476
|
+
target = namespace if namespace is not None else server_name
|
|
477
|
+
result: ReadResourceResult = await self._aggregator.get_resource(resource_uri, target)
|
|
478
|
+
return result
|
|
479
|
+
|
|
600
480
|
async def with_resource(
|
|
601
481
|
self,
|
|
602
|
-
prompt_content: Union[str, PromptMessage,
|
|
482
|
+
prompt_content: Union[str, PromptMessage, PromptMessageExtended],
|
|
603
483
|
resource_uri: str,
|
|
484
|
+
namespace: str | None = None,
|
|
604
485
|
server_name: str | None = None,
|
|
605
486
|
) -> str:
|
|
606
487
|
"""
|
|
@@ -609,62 +490,96 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
609
490
|
Args:
|
|
610
491
|
prompt_content: Content in various formats:
|
|
611
492
|
- String: Converted to a user message with the text
|
|
612
|
-
- PromptMessage: Converted to
|
|
613
|
-
-
|
|
493
|
+
- PromptMessage: Converted to PromptMessageExtended
|
|
494
|
+
- PromptMessageExtended: Used directly
|
|
614
495
|
resource_uri: URI of the resource to retrieve
|
|
615
|
-
|
|
496
|
+
namespace: Optional namespace (server) to retrieve the resource from
|
|
616
497
|
|
|
617
498
|
Returns:
|
|
618
499
|
The agent's response as a string
|
|
619
500
|
"""
|
|
620
501
|
# Get the embedded resources
|
|
621
502
|
embedded_resources: List[EmbeddedResource] = await self.get_embedded_resources(
|
|
622
|
-
resource_uri, server_name
|
|
503
|
+
resource_uri, namespace if namespace is not None else server_name
|
|
623
504
|
)
|
|
624
505
|
|
|
625
506
|
# Create or update the prompt message
|
|
626
|
-
prompt:
|
|
507
|
+
prompt: PromptMessageExtended
|
|
627
508
|
if isinstance(prompt_content, str):
|
|
628
509
|
# Create a new prompt with the text and resources
|
|
629
510
|
content = [TextContent(type="text", text=prompt_content)]
|
|
630
511
|
content.extend(embedded_resources)
|
|
631
|
-
prompt =
|
|
512
|
+
prompt = PromptMessageExtended(role="user", content=content)
|
|
632
513
|
elif isinstance(prompt_content, PromptMessage):
|
|
633
|
-
# Convert PromptMessage to
|
|
514
|
+
# Convert PromptMessage to PromptMessageExtended and add resources
|
|
634
515
|
content = [prompt_content.content]
|
|
635
516
|
content.extend(embedded_resources)
|
|
636
|
-
prompt =
|
|
637
|
-
elif isinstance(prompt_content,
|
|
517
|
+
prompt = PromptMessageExtended(role=prompt_content.role, content=content)
|
|
518
|
+
elif isinstance(prompt_content, PromptMessageExtended):
|
|
638
519
|
# Add resources to the existing prompt
|
|
639
520
|
prompt = prompt_content
|
|
640
521
|
prompt.content.extend(embedded_resources)
|
|
641
522
|
else:
|
|
642
523
|
raise TypeError(
|
|
643
|
-
"prompt_content must be a string, PromptMessage, or
|
|
524
|
+
"prompt_content must be a string, PromptMessage, or PromptMessageExtended"
|
|
644
525
|
)
|
|
645
526
|
|
|
646
|
-
response:
|
|
527
|
+
response: PromptMessageExtended = await self.generate([prompt], None)
|
|
647
528
|
return response.first_text()
|
|
648
529
|
|
|
649
|
-
async def
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
530
|
+
async def run_tools(self, request: PromptMessageExtended) -> PromptMessageExtended:
|
|
531
|
+
"""Override ToolAgent's run_tools to use MCP tools via aggregator."""
|
|
532
|
+
if not request.tool_calls:
|
|
533
|
+
self.logger.warning("No tool calls found in request", data=request)
|
|
534
|
+
return PromptMessageExtended(role="user", tool_results={})
|
|
535
|
+
|
|
536
|
+
tool_results: dict[str, CallToolResult] = {}
|
|
537
|
+
|
|
538
|
+
# Cache available tool names (original, not namespaced) for display
|
|
539
|
+
available_tools = [
|
|
540
|
+
namespaced_tool.tool.name
|
|
541
|
+
for namespaced_tool in self._aggregator._namespaced_tool_map.values()
|
|
542
|
+
]
|
|
543
|
+
|
|
544
|
+
# Process each tool call using our aggregator
|
|
545
|
+
for correlation_id, tool_request in request.tool_calls.items():
|
|
546
|
+
tool_name = tool_request.params.name
|
|
547
|
+
tool_args = tool_request.params.arguments or {}
|
|
548
|
+
|
|
549
|
+
# Get the original tool name for display (not namespaced)
|
|
550
|
+
namespaced_tool = self._aggregator._namespaced_tool_map.get(tool_name)
|
|
551
|
+
display_tool_name = namespaced_tool.tool.name if namespaced_tool else tool_name
|
|
552
|
+
|
|
553
|
+
self.display.show_tool_call(
|
|
554
|
+
name=self._name,
|
|
555
|
+
tool_args=tool_args,
|
|
556
|
+
bottom_items=available_tools,
|
|
557
|
+
tool_name=display_tool_name,
|
|
558
|
+
highlight_items=tool_name,
|
|
559
|
+
max_item_length=12,
|
|
560
|
+
)
|
|
657
561
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
562
|
+
try:
|
|
563
|
+
# Use our aggregator to call the MCP tool
|
|
564
|
+
result = await self.call_tool(tool_name, tool_args)
|
|
565
|
+
tool_results[correlation_id] = result
|
|
661
566
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
567
|
+
# Show tool result (like ToolAgent does)
|
|
568
|
+
self.display.show_tool_result(name=self._name, result=result)
|
|
569
|
+
|
|
570
|
+
self.logger.debug(f"MCP tool {display_tool_name} executed successfully")
|
|
571
|
+
except Exception as e:
|
|
572
|
+
self.logger.error(f"MCP tool {display_tool_name} failed: {e}")
|
|
573
|
+
error_result = CallToolResult(
|
|
574
|
+
content=[TextContent(type="text", text=f"Error: {str(e)}")],
|
|
575
|
+
isError=True,
|
|
576
|
+
)
|
|
577
|
+
tool_results[correlation_id] = error_result
|
|
578
|
+
|
|
579
|
+
# Show error result too
|
|
580
|
+
self.display.show_tool_result(name=self._name, result=error_result)
|
|
581
|
+
|
|
582
|
+
return PromptMessageExtended(role="user", tool_results=tool_results)
|
|
668
583
|
|
|
669
584
|
async def apply_prompt_template(self, prompt_result: GetPromptResult, prompt_name: str) -> str:
|
|
670
585
|
"""
|
|
@@ -679,21 +594,30 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
679
594
|
String representation of the assistant's response if generated
|
|
680
595
|
"""
|
|
681
596
|
assert self._llm
|
|
682
|
-
with self.
|
|
597
|
+
with self._tracer.start_as_current_span(f"Agent: '{self._name}' apply_prompt_template"):
|
|
683
598
|
return await self._llm.apply_prompt_template(prompt_result, prompt_name)
|
|
684
599
|
|
|
685
600
|
async def structured(
|
|
686
601
|
self,
|
|
687
|
-
|
|
602
|
+
messages: Union[
|
|
603
|
+
str,
|
|
604
|
+
PromptMessage,
|
|
605
|
+
PromptMessageExtended,
|
|
606
|
+
List[Union[str, PromptMessage, PromptMessageExtended]],
|
|
607
|
+
],
|
|
688
608
|
model: Type[ModelT],
|
|
689
609
|
request_params: RequestParams | None = None,
|
|
690
|
-
) -> Tuple[ModelT | None,
|
|
610
|
+
) -> Tuple[ModelT | None, PromptMessageExtended]:
|
|
691
611
|
"""
|
|
692
612
|
Apply the prompt and return the result as a Pydantic model.
|
|
693
|
-
|
|
613
|
+
Normalizes input messages and delegates to the attached LLM.
|
|
694
614
|
|
|
695
615
|
Args:
|
|
696
|
-
|
|
616
|
+
messages: Message(s) in various formats:
|
|
617
|
+
- String: Converted to a user PromptMessageExtended
|
|
618
|
+
- PromptMessage: Converted to PromptMessageExtended
|
|
619
|
+
- PromptMessageExtended: Used directly
|
|
620
|
+
- List of any combination of the above
|
|
697
621
|
model: The Pydantic model class to parse the result into
|
|
698
622
|
request_params: Optional parameters to configure the LLM request
|
|
699
623
|
|
|
@@ -701,17 +625,20 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
701
625
|
An instance of the specified model, or None if coercion fails
|
|
702
626
|
"""
|
|
703
627
|
assert self._llm
|
|
704
|
-
|
|
705
|
-
|
|
628
|
+
# Normalize all input types to a list of PromptMessageExtended
|
|
629
|
+
normalized_messages = normalize_to_extended_list(messages)
|
|
630
|
+
|
|
631
|
+
with self._tracer.start_as_current_span(f"Agent: '{self._name}' structured"):
|
|
632
|
+
return await self._llm.structured(normalized_messages, model, request_params)
|
|
706
633
|
|
|
707
634
|
async def apply_prompt_messages(
|
|
708
|
-
self, prompts: List[
|
|
635
|
+
self, prompts: List[PromptMessageExtended], request_params: RequestParams | None = None
|
|
709
636
|
) -> str:
|
|
710
637
|
"""
|
|
711
638
|
Apply a list of prompt messages and return the result.
|
|
712
639
|
|
|
713
640
|
Args:
|
|
714
|
-
prompts: List of
|
|
641
|
+
prompts: List of PromptMessageExtended messages
|
|
715
642
|
request_params: Optional request parameters
|
|
716
643
|
|
|
717
644
|
Returns:
|
|
@@ -721,21 +648,21 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
721
648
|
response = await self.generate(prompts, request_params)
|
|
722
649
|
return response.first_text()
|
|
723
650
|
|
|
724
|
-
async def list_prompts(
|
|
651
|
+
async def list_prompts(
|
|
652
|
+
self, namespace: str | None = None, server_name: str | None = None
|
|
653
|
+
) -> Mapping[str, List[mcp.types.Prompt]]:
|
|
725
654
|
"""
|
|
726
655
|
List all prompts available to this agent, filtered by configuration.
|
|
727
656
|
|
|
728
657
|
Args:
|
|
729
|
-
|
|
658
|
+
namespace: Optional namespace (server) to list prompts from
|
|
730
659
|
|
|
731
660
|
Returns:
|
|
732
661
|
Dictionary mapping server names to lists of Prompt objects
|
|
733
662
|
"""
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
# Get all prompts from the parent class
|
|
738
|
-
result = await super().list_prompts(server_name)
|
|
663
|
+
# Get all prompts from the aggregator
|
|
664
|
+
target = namespace if namespace is not None else server_name
|
|
665
|
+
result = await self._aggregator.list_prompts(target)
|
|
739
666
|
|
|
740
667
|
# Apply filtering if prompts are specified in config
|
|
741
668
|
if self.config.prompts is not None:
|
|
@@ -756,21 +683,21 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
756
683
|
|
|
757
684
|
return result
|
|
758
685
|
|
|
759
|
-
async def list_resources(
|
|
686
|
+
async def list_resources(
|
|
687
|
+
self, namespace: str | None = None, server_name: str | None = None
|
|
688
|
+
) -> Dict[str, List[str]]:
|
|
760
689
|
"""
|
|
761
690
|
List all resources available to this agent, filtered by configuration.
|
|
762
691
|
|
|
763
692
|
Args:
|
|
764
|
-
|
|
693
|
+
namespace: Optional namespace (server) to list resources from
|
|
765
694
|
|
|
766
695
|
Returns:
|
|
767
696
|
Dictionary mapping server names to lists of resource URIs
|
|
768
697
|
"""
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
# Get all resources from the parent class
|
|
773
|
-
result = await super().list_resources(server_name)
|
|
698
|
+
# Get all resources from the aggregator
|
|
699
|
+
target = namespace if namespace is not None else server_name
|
|
700
|
+
result = await self._aggregator.list_resources(target)
|
|
774
701
|
|
|
775
702
|
# Apply filtering if resources are specified in config
|
|
776
703
|
if self.config.resources is not None:
|
|
@@ -791,21 +718,21 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
791
718
|
|
|
792
719
|
return result
|
|
793
720
|
|
|
794
|
-
async def list_mcp_tools(
|
|
721
|
+
async def list_mcp_tools(
|
|
722
|
+
self, namespace: str | None = None, server_name: str | None = None
|
|
723
|
+
) -> Mapping[str, List[Tool]]:
|
|
795
724
|
"""
|
|
796
725
|
List all tools available to this agent, grouped by server and filtered by configuration.
|
|
797
726
|
|
|
798
727
|
Args:
|
|
799
|
-
|
|
728
|
+
namespace: Optional namespace (server) to list tools from
|
|
800
729
|
|
|
801
730
|
Returns:
|
|
802
731
|
Dictionary mapping server names to lists of Tool objects (with original names, not namespaced)
|
|
803
732
|
"""
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
# Get all tools from the parent class
|
|
808
|
-
result = await super().list_mcp_tools(server_name)
|
|
733
|
+
# Get all tools from the aggregator
|
|
734
|
+
target = namespace if namespace is not None else server_name
|
|
735
|
+
result = await self._aggregator.list_mcp_tools(target)
|
|
809
736
|
|
|
810
737
|
# Apply filtering if tools are specified in config
|
|
811
738
|
if self.config.tools is not None:
|
|
@@ -824,24 +751,15 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
824
751
|
filtered_result[server] = filtered_tools
|
|
825
752
|
result = filtered_result
|
|
826
753
|
|
|
827
|
-
# Add human input tool to a special server if
|
|
828
|
-
if self.
|
|
829
|
-
from mcp.server.fastmcp.tools import Tool as FastTool
|
|
830
|
-
|
|
831
|
-
human_input_tool: FastTool = FastTool.from_function(self.request_human_input)
|
|
754
|
+
# Add elicitation-backed human input tool to a special server if enabled and available
|
|
755
|
+
if self.config.human_input and getattr(self, "_human_input_tool", None):
|
|
832
756
|
special_server_name = "__human_input__"
|
|
833
|
-
|
|
757
|
+
|
|
834
758
|
# If the special server doesn't exist in result, create it
|
|
835
759
|
if special_server_name not in result:
|
|
836
760
|
result[special_server_name] = []
|
|
837
|
-
|
|
838
|
-
result[special_server_name].append(
|
|
839
|
-
Tool(
|
|
840
|
-
name=HUMAN_INPUT_TOOL_NAME,
|
|
841
|
-
description=human_input_tool.description,
|
|
842
|
-
inputSchema=human_input_tool.parameters,
|
|
843
|
-
)
|
|
844
|
-
)
|
|
761
|
+
|
|
762
|
+
result[special_server_name].append(self._human_input_tool)
|
|
845
763
|
|
|
846
764
|
return result
|
|
847
765
|
|
|
@@ -863,19 +781,93 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
863
781
|
skills.append(await self.convert(tool))
|
|
864
782
|
|
|
865
783
|
return AgentCard(
|
|
866
|
-
|
|
784
|
+
skills=skills,
|
|
785
|
+
name=self._name,
|
|
867
786
|
description=self.instruction,
|
|
868
|
-
url=f"fast-agent://agents/{self.
|
|
787
|
+
url=f"fast-agent://agents/{self._name}/",
|
|
869
788
|
version="0.1",
|
|
870
789
|
capabilities=DEFAULT_CAPABILITIES,
|
|
871
|
-
|
|
872
|
-
|
|
790
|
+
default_input_modes=["text/plain"],
|
|
791
|
+
default_output_modes=["text/plain"],
|
|
873
792
|
provider=None,
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
793
|
+
documentation_url=None,
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
async def show_assistant_message(
|
|
797
|
+
self,
|
|
798
|
+
message: PromptMessageExtended,
|
|
799
|
+
bottom_items: List[str] | None = None,
|
|
800
|
+
highlight_items: str | List[str] | None = None,
|
|
801
|
+
max_item_length: int | None = None,
|
|
802
|
+
name: str | None = None,
|
|
803
|
+
model: str | None = None,
|
|
804
|
+
additional_message: Optional["Text"] = None,
|
|
805
|
+
) -> None:
|
|
806
|
+
"""
|
|
807
|
+
Display an assistant message with MCP servers in the bottom bar.
|
|
808
|
+
|
|
809
|
+
This override adds the list of connected MCP servers to the bottom bar
|
|
810
|
+
and highlights servers that were used for tool calls in this message.
|
|
811
|
+
"""
|
|
812
|
+
# Get the list of MCP servers (if not provided)
|
|
813
|
+
if bottom_items is None:
|
|
814
|
+
if self._aggregator and self._aggregator.server_names:
|
|
815
|
+
server_names = self._aggregator.server_names
|
|
816
|
+
else:
|
|
817
|
+
server_names = []
|
|
818
|
+
else:
|
|
819
|
+
server_names = bottom_items
|
|
820
|
+
|
|
821
|
+
# Extract servers from tool calls in the message for highlighting
|
|
822
|
+
if highlight_items is None:
|
|
823
|
+
highlight_servers = self._extract_servers_from_message(message)
|
|
824
|
+
else:
|
|
825
|
+
# Convert to list if needed
|
|
826
|
+
if isinstance(highlight_items, str):
|
|
827
|
+
highlight_servers = [highlight_items]
|
|
828
|
+
else:
|
|
829
|
+
highlight_servers = highlight_items
|
|
830
|
+
|
|
831
|
+
# Call parent's implementation with server information
|
|
832
|
+
await super().show_assistant_message(
|
|
833
|
+
message=message,
|
|
834
|
+
bottom_items=server_names,
|
|
835
|
+
highlight_items=highlight_servers,
|
|
836
|
+
max_item_length=max_item_length or 12,
|
|
837
|
+
name=name,
|
|
838
|
+
model=model,
|
|
839
|
+
additional_message=additional_message,
|
|
877
840
|
)
|
|
878
841
|
|
|
842
|
+
def _extract_servers_from_message(self, message: PromptMessageExtended) -> List[str]:
|
|
843
|
+
"""
|
|
844
|
+
Extract server names from tool calls in the message.
|
|
845
|
+
|
|
846
|
+
Args:
|
|
847
|
+
message: The message containing potential tool calls
|
|
848
|
+
|
|
849
|
+
Returns:
|
|
850
|
+
List of server names that were called
|
|
851
|
+
"""
|
|
852
|
+
servers = []
|
|
853
|
+
|
|
854
|
+
# Check if message has tool calls
|
|
855
|
+
if message.tool_calls:
|
|
856
|
+
for tool_request in message.tool_calls.values():
|
|
857
|
+
tool_name = tool_request.params.name
|
|
858
|
+
|
|
859
|
+
# Use aggregator's mapping to find the server for this tool
|
|
860
|
+
if tool_name in self._aggregator._namespaced_tool_map:
|
|
861
|
+
namespaced_tool = self._aggregator._namespaced_tool_map[tool_name]
|
|
862
|
+
if namespaced_tool.server_name not in servers:
|
|
863
|
+
servers.append(namespaced_tool.server_name)
|
|
864
|
+
|
|
865
|
+
return servers
|
|
866
|
+
|
|
867
|
+
async def _parse_resource_name(self, name: str, resource_type: str) -> tuple[str, str]:
|
|
868
|
+
"""Delegate resource name parsing to the aggregator."""
|
|
869
|
+
return await self._aggregator._parse_resource_name(name, resource_type)
|
|
870
|
+
|
|
879
871
|
async def convert(self, tool: Tool) -> AgentSkill:
|
|
880
872
|
"""
|
|
881
873
|
Convert a Tool to an AgentSkill.
|
|
@@ -885,26 +877,26 @@ class BaseAgent(MCPAggregator, AgentProtocol):
|
|
|
885
877
|
return AgentSkill(
|
|
886
878
|
id=tool.name,
|
|
887
879
|
name=tool_without_namespace,
|
|
888
|
-
description=tool.description,
|
|
880
|
+
description=tool.description or "",
|
|
889
881
|
tags=["tool"],
|
|
890
882
|
examples=None,
|
|
891
|
-
|
|
883
|
+
input_modes=None, # ["text/plain"],
|
|
892
884
|
# cover TextContent | ImageContent ->
|
|
893
885
|
# https://github.com/modelcontextprotocol/modelcontextprotocol/pull/223
|
|
894
886
|
# https://github.com/modelcontextprotocol/modelcontextprotocol/pull/93
|
|
895
|
-
|
|
887
|
+
output_modes=None, # ,["text/plain", "image/*"],
|
|
896
888
|
)
|
|
897
889
|
|
|
898
890
|
@property
|
|
899
|
-
def message_history(self) -> List[
|
|
891
|
+
def message_history(self) -> List[PromptMessageExtended]:
|
|
900
892
|
"""
|
|
901
|
-
Return the agent's message history as
|
|
893
|
+
Return the agent's message history as PromptMessageExtended objects.
|
|
902
894
|
|
|
903
895
|
This history can be used to transfer state between agents or for
|
|
904
896
|
analysis and debugging purposes.
|
|
905
897
|
|
|
906
898
|
Returns:
|
|
907
|
-
List of
|
|
899
|
+
List of PromptMessageExtended objects representing the conversation history
|
|
908
900
|
"""
|
|
909
901
|
if self._llm:
|
|
910
902
|
return self._llm.message_history
|