fast-agent-mcp 0.4.7__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/__init__.py +183 -0
- fast_agent/acp/__init__.py +19 -0
- fast_agent/acp/acp_aware_mixin.py +304 -0
- fast_agent/acp/acp_context.py +437 -0
- fast_agent/acp/content_conversion.py +136 -0
- fast_agent/acp/filesystem_runtime.py +427 -0
- fast_agent/acp/permission_store.py +269 -0
- fast_agent/acp/server/__init__.py +5 -0
- fast_agent/acp/server/agent_acp_server.py +1472 -0
- fast_agent/acp/slash_commands.py +1050 -0
- fast_agent/acp/terminal_runtime.py +408 -0
- fast_agent/acp/tool_permission_adapter.py +125 -0
- fast_agent/acp/tool_permissions.py +474 -0
- fast_agent/acp/tool_progress.py +814 -0
- fast_agent/agents/__init__.py +85 -0
- fast_agent/agents/agent_types.py +64 -0
- fast_agent/agents/llm_agent.py +350 -0
- fast_agent/agents/llm_decorator.py +1139 -0
- fast_agent/agents/mcp_agent.py +1337 -0
- fast_agent/agents/tool_agent.py +271 -0
- fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
- fast_agent/agents/workflow/chain_agent.py +212 -0
- fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
- fast_agent/agents/workflow/iterative_planner.py +652 -0
- fast_agent/agents/workflow/maker_agent.py +379 -0
- fast_agent/agents/workflow/orchestrator_models.py +218 -0
- fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
- fast_agent/agents/workflow/parallel_agent.py +250 -0
- fast_agent/agents/workflow/router_agent.py +353 -0
- fast_agent/cli/__init__.py +0 -0
- fast_agent/cli/__main__.py +73 -0
- fast_agent/cli/commands/acp.py +159 -0
- fast_agent/cli/commands/auth.py +404 -0
- fast_agent/cli/commands/check_config.py +783 -0
- fast_agent/cli/commands/go.py +514 -0
- fast_agent/cli/commands/quickstart.py +557 -0
- fast_agent/cli/commands/serve.py +143 -0
- fast_agent/cli/commands/server_helpers.py +114 -0
- fast_agent/cli/commands/setup.py +174 -0
- fast_agent/cli/commands/url_parser.py +190 -0
- fast_agent/cli/constants.py +40 -0
- fast_agent/cli/main.py +115 -0
- fast_agent/cli/terminal.py +24 -0
- fast_agent/config.py +798 -0
- fast_agent/constants.py +41 -0
- fast_agent/context.py +279 -0
- fast_agent/context_dependent.py +50 -0
- fast_agent/core/__init__.py +92 -0
- fast_agent/core/agent_app.py +448 -0
- fast_agent/core/core_app.py +137 -0
- fast_agent/core/direct_decorators.py +784 -0
- fast_agent/core/direct_factory.py +620 -0
- fast_agent/core/error_handling.py +27 -0
- fast_agent/core/exceptions.py +90 -0
- fast_agent/core/executor/__init__.py +0 -0
- fast_agent/core/executor/executor.py +280 -0
- fast_agent/core/executor/task_registry.py +32 -0
- fast_agent/core/executor/workflow_signal.py +324 -0
- fast_agent/core/fastagent.py +1186 -0
- fast_agent/core/logging/__init__.py +5 -0
- fast_agent/core/logging/events.py +138 -0
- fast_agent/core/logging/json_serializer.py +164 -0
- fast_agent/core/logging/listeners.py +309 -0
- fast_agent/core/logging/logger.py +278 -0
- fast_agent/core/logging/transport.py +481 -0
- fast_agent/core/prompt.py +9 -0
- fast_agent/core/prompt_templates.py +183 -0
- fast_agent/core/validation.py +326 -0
- fast_agent/event_progress.py +62 -0
- fast_agent/history/history_exporter.py +49 -0
- fast_agent/human_input/__init__.py +47 -0
- fast_agent/human_input/elicitation_handler.py +123 -0
- fast_agent/human_input/elicitation_state.py +33 -0
- fast_agent/human_input/form_elements.py +59 -0
- fast_agent/human_input/form_fields.py +256 -0
- fast_agent/human_input/simple_form.py +113 -0
- fast_agent/human_input/types.py +40 -0
- fast_agent/interfaces.py +310 -0
- fast_agent/llm/__init__.py +9 -0
- fast_agent/llm/cancellation.py +22 -0
- fast_agent/llm/fastagent_llm.py +931 -0
- fast_agent/llm/internal/passthrough.py +161 -0
- fast_agent/llm/internal/playback.py +129 -0
- fast_agent/llm/internal/silent.py +41 -0
- fast_agent/llm/internal/slow.py +38 -0
- fast_agent/llm/memory.py +275 -0
- fast_agent/llm/model_database.py +490 -0
- fast_agent/llm/model_factory.py +388 -0
- fast_agent/llm/model_info.py +102 -0
- fast_agent/llm/prompt_utils.py +155 -0
- fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
- fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
- fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
- fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
- fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
- fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
- fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
- fast_agent/llm/provider/google/google_converter.py +466 -0
- fast_agent/llm/provider/google/llm_google_native.py +681 -0
- fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
- fast_agent/llm/provider/openai/llm_azure.py +143 -0
- fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
- fast_agent/llm/provider/openai/llm_generic.py +35 -0
- fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
- fast_agent/llm/provider/openai/llm_groq.py +42 -0
- fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
- fast_agent/llm/provider/openai/llm_openai.py +1195 -0
- fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
- fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
- fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
- fast_agent/llm/provider/openai/llm_xai.py +38 -0
- fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
- fast_agent/llm/provider/openai/openai_multipart.py +169 -0
- fast_agent/llm/provider/openai/openai_utils.py +67 -0
- fast_agent/llm/provider/openai/responses.py +133 -0
- fast_agent/llm/provider_key_manager.py +139 -0
- fast_agent/llm/provider_types.py +34 -0
- fast_agent/llm/request_params.py +61 -0
- fast_agent/llm/sampling_converter.py +98 -0
- fast_agent/llm/stream_types.py +9 -0
- fast_agent/llm/usage_tracking.py +445 -0
- fast_agent/mcp/__init__.py +56 -0
- fast_agent/mcp/common.py +26 -0
- fast_agent/mcp/elicitation_factory.py +84 -0
- fast_agent/mcp/elicitation_handlers.py +164 -0
- fast_agent/mcp/gen_client.py +83 -0
- fast_agent/mcp/helpers/__init__.py +36 -0
- fast_agent/mcp/helpers/content_helpers.py +352 -0
- fast_agent/mcp/helpers/server_config_helpers.py +25 -0
- fast_agent/mcp/hf_auth.py +147 -0
- fast_agent/mcp/interfaces.py +92 -0
- fast_agent/mcp/logger_textio.py +108 -0
- fast_agent/mcp/mcp_agent_client_session.py +411 -0
- fast_agent/mcp/mcp_aggregator.py +2175 -0
- fast_agent/mcp/mcp_connection_manager.py +723 -0
- fast_agent/mcp/mcp_content.py +262 -0
- fast_agent/mcp/mime_utils.py +108 -0
- fast_agent/mcp/oauth_client.py +509 -0
- fast_agent/mcp/prompt.py +159 -0
- fast_agent/mcp/prompt_message_extended.py +155 -0
- fast_agent/mcp/prompt_render.py +84 -0
- fast_agent/mcp/prompt_serialization.py +580 -0
- fast_agent/mcp/prompts/__init__.py +0 -0
- fast_agent/mcp/prompts/__main__.py +7 -0
- fast_agent/mcp/prompts/prompt_constants.py +18 -0
- fast_agent/mcp/prompts/prompt_helpers.py +238 -0
- fast_agent/mcp/prompts/prompt_load.py +186 -0
- fast_agent/mcp/prompts/prompt_server.py +552 -0
- fast_agent/mcp/prompts/prompt_template.py +438 -0
- fast_agent/mcp/resource_utils.py +215 -0
- fast_agent/mcp/sampling.py +200 -0
- fast_agent/mcp/server/__init__.py +4 -0
- fast_agent/mcp/server/agent_server.py +613 -0
- fast_agent/mcp/skybridge.py +44 -0
- fast_agent/mcp/sse_tracking.py +287 -0
- fast_agent/mcp/stdio_tracking_simple.py +59 -0
- fast_agent/mcp/streamable_http_tracking.py +309 -0
- fast_agent/mcp/tool_execution_handler.py +137 -0
- fast_agent/mcp/tool_permission_handler.py +88 -0
- fast_agent/mcp/transport_tracking.py +634 -0
- fast_agent/mcp/types.py +24 -0
- fast_agent/mcp/ui_agent.py +48 -0
- fast_agent/mcp/ui_mixin.py +209 -0
- fast_agent/mcp_server_registry.py +89 -0
- fast_agent/py.typed +0 -0
- fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
- fast_agent/resources/examples/data-analysis/analysis.py +68 -0
- fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
- fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
- fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
- fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
- fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
- fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
- fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
- fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
- fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
- fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
- fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
- fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
- fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
- fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
- fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
- fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
- fast_agent/resources/examples/researcher/researcher.py +36 -0
- fast_agent/resources/examples/tensorzero/.env.sample +2 -0
- fast_agent/resources/examples/tensorzero/Makefile +31 -0
- fast_agent/resources/examples/tensorzero/README.md +56 -0
- fast_agent/resources/examples/tensorzero/agent.py +35 -0
- fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
- fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
- fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
- fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
- fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
- fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
- fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
- fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
- fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
- fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
- fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
- fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
- fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
- fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
- fast_agent/resources/examples/workflows/chaining.py +37 -0
- fast_agent/resources/examples/workflows/evaluator.py +77 -0
- fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
- fast_agent/resources/examples/workflows/graded_report.md +89 -0
- fast_agent/resources/examples/workflows/human_input.py +28 -0
- fast_agent/resources/examples/workflows/maker.py +156 -0
- fast_agent/resources/examples/workflows/orchestrator.py +70 -0
- fast_agent/resources/examples/workflows/parallel.py +56 -0
- fast_agent/resources/examples/workflows/router.py +69 -0
- fast_agent/resources/examples/workflows/short_story.md +13 -0
- fast_agent/resources/examples/workflows/short_story.txt +19 -0
- fast_agent/resources/setup/.gitignore +30 -0
- fast_agent/resources/setup/agent.py +28 -0
- fast_agent/resources/setup/fastagent.config.yaml +65 -0
- fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
- fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
- fast_agent/skills/__init__.py +9 -0
- fast_agent/skills/registry.py +235 -0
- fast_agent/tools/elicitation.py +369 -0
- fast_agent/tools/shell_runtime.py +402 -0
- fast_agent/types/__init__.py +59 -0
- fast_agent/types/conversation_summary.py +294 -0
- fast_agent/types/llm_stop_reason.py +78 -0
- fast_agent/types/message_search.py +249 -0
- fast_agent/ui/__init__.py +38 -0
- fast_agent/ui/console.py +59 -0
- fast_agent/ui/console_display.py +1080 -0
- fast_agent/ui/elicitation_form.py +946 -0
- fast_agent/ui/elicitation_style.py +59 -0
- fast_agent/ui/enhanced_prompt.py +1400 -0
- fast_agent/ui/history_display.py +734 -0
- fast_agent/ui/interactive_prompt.py +1199 -0
- fast_agent/ui/markdown_helpers.py +104 -0
- fast_agent/ui/markdown_truncator.py +1004 -0
- fast_agent/ui/mcp_display.py +857 -0
- fast_agent/ui/mcp_ui_utils.py +235 -0
- fast_agent/ui/mermaid_utils.py +169 -0
- fast_agent/ui/message_primitives.py +50 -0
- fast_agent/ui/notification_tracker.py +205 -0
- fast_agent/ui/plain_text_truncator.py +68 -0
- fast_agent/ui/progress_display.py +10 -0
- fast_agent/ui/rich_progress.py +195 -0
- fast_agent/ui/streaming.py +774 -0
- fast_agent/ui/streaming_buffer.py +449 -0
- fast_agent/ui/tool_display.py +422 -0
- fast_agent/ui/usage_display.py +204 -0
- fast_agent/utils/__init__.py +5 -0
- fast_agent/utils/reasoning_stream_parser.py +77 -0
- fast_agent/utils/time.py +22 -0
- fast_agent/workflow_telemetry.py +261 -0
- fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
- fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
- fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
- fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
- fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Factory for resolving elicitation handlers with proper precedence.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from mcp.client.session import ElicitationFnT
|
|
8
|
+
|
|
9
|
+
from fast_agent.agents.agent_types import AgentConfig
|
|
10
|
+
from fast_agent.core.logging.logger import get_logger
|
|
11
|
+
from fast_agent.mcp.elicitation_handlers import (
|
|
12
|
+
auto_cancel_elicitation_handler,
|
|
13
|
+
forms_elicitation_handler,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
logger = get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def resolve_elicitation_handler(
|
|
20
|
+
agent_config: AgentConfig, app_config: Any, server_config: Any = None
|
|
21
|
+
) -> ElicitationFnT | None:
|
|
22
|
+
"""Resolve elicitation handler with proper precedence.
|
|
23
|
+
|
|
24
|
+
Precedence order:
|
|
25
|
+
1. Agent decorator supplied (highest precedence)
|
|
26
|
+
2. Server-specific config file setting
|
|
27
|
+
3. Global config file setting
|
|
28
|
+
4. Default forms handler (lowest precedence)
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
agent_config: Agent configuration from decorator
|
|
32
|
+
app_config: Application configuration from YAML
|
|
33
|
+
server_config: Server-specific configuration (optional)
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
ElicitationFnT handler or None (no elicitation capability)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# 1. Decorator takes highest precedence
|
|
40
|
+
if agent_config.elicitation_handler:
|
|
41
|
+
logger.debug(f"Using decorator-provided elicitation handler for agent {agent_config.name}")
|
|
42
|
+
return agent_config.elicitation_handler
|
|
43
|
+
|
|
44
|
+
# 2. Check server-specific config first
|
|
45
|
+
if server_config:
|
|
46
|
+
elicitation_config = getattr(server_config, "elicitation", {})
|
|
47
|
+
if isinstance(elicitation_config, dict):
|
|
48
|
+
mode = elicitation_config.get("mode")
|
|
49
|
+
else:
|
|
50
|
+
mode = getattr(elicitation_config, "mode", None)
|
|
51
|
+
|
|
52
|
+
if mode:
|
|
53
|
+
if mode == "none":
|
|
54
|
+
logger.debug(f"Elicitation disabled by server config for agent {agent_config.name}")
|
|
55
|
+
return None # Don't advertise elicitation capability
|
|
56
|
+
elif mode == "auto-cancel":
|
|
57
|
+
logger.debug(
|
|
58
|
+
f"Using auto-cancel elicitation handler (server config) for agent {agent_config.name}"
|
|
59
|
+
)
|
|
60
|
+
return auto_cancel_elicitation_handler
|
|
61
|
+
else: # "forms" or other
|
|
62
|
+
logger.debug(
|
|
63
|
+
f"Using forms elicitation handler (server config) for agent {agent_config.name}"
|
|
64
|
+
)
|
|
65
|
+
return forms_elicitation_handler
|
|
66
|
+
|
|
67
|
+
# 3. Check global config file
|
|
68
|
+
elicitation_config = getattr(app_config, "elicitation", {})
|
|
69
|
+
if isinstance(elicitation_config, dict):
|
|
70
|
+
mode = elicitation_config.get("mode", "forms")
|
|
71
|
+
else:
|
|
72
|
+
mode = getattr(elicitation_config, "mode", "forms")
|
|
73
|
+
|
|
74
|
+
if mode == "none":
|
|
75
|
+
logger.debug(f"Elicitation disabled by global config for agent {agent_config.name}")
|
|
76
|
+
return None # Don't advertise elicitation capability
|
|
77
|
+
elif mode == "auto-cancel":
|
|
78
|
+
logger.debug(
|
|
79
|
+
f"Using auto-cancel elicitation handler (global config) for agent {agent_config.name}"
|
|
80
|
+
)
|
|
81
|
+
return auto_cancel_elicitation_handler
|
|
82
|
+
else: # "forms" or default
|
|
83
|
+
logger.debug(f"Using default forms elicitation handler for agent {agent_config.name}")
|
|
84
|
+
return forms_elicitation_handler
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Predefined elicitation handlers for different use cases.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
from mcp.shared.context import RequestContext
|
|
9
|
+
from mcp.types import ElicitRequestParams, ElicitResult, ErrorData
|
|
10
|
+
|
|
11
|
+
from fast_agent.core.logging.logger import get_logger
|
|
12
|
+
from fast_agent.human_input.elicitation_handler import elicitation_input_callback
|
|
13
|
+
from fast_agent.human_input.types import HumanInputRequest
|
|
14
|
+
from fast_agent.mcp.helpers.server_config_helpers import get_server_config
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from mcp import ClientSession
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def auto_cancel_elicitation_handler(
|
|
23
|
+
context: RequestContext["ClientSession", Any],
|
|
24
|
+
params: ElicitRequestParams,
|
|
25
|
+
) -> ElicitResult | ErrorData:
|
|
26
|
+
"""Handler that automatically cancels all elicitation requests.
|
|
27
|
+
|
|
28
|
+
Useful for production deployments where you want to advertise elicitation
|
|
29
|
+
capability but automatically decline all requests.
|
|
30
|
+
"""
|
|
31
|
+
logger.info(f"Auto-cancelling elicitation request: {params.message}")
|
|
32
|
+
return ElicitResult(action="cancel")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def forms_elicitation_handler(
|
|
36
|
+
context: RequestContext["ClientSession", Any], params: ElicitRequestParams
|
|
37
|
+
) -> ElicitResult:
|
|
38
|
+
"""
|
|
39
|
+
Interactive forms-based elicitation handler using enhanced input handler.
|
|
40
|
+
"""
|
|
41
|
+
logger.info(f"Eliciting response for params: {params}")
|
|
42
|
+
|
|
43
|
+
# Get server config for additional context
|
|
44
|
+
server_config = get_server_config(context)
|
|
45
|
+
server_name = server_config.name if server_config else "Unknown Server"
|
|
46
|
+
server_info = (
|
|
47
|
+
{"command": server_config.command} if server_config and server_config.command else None
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Get agent name - try multiple sources in order of preference
|
|
51
|
+
agent_name: str | None = None
|
|
52
|
+
|
|
53
|
+
# 1. Check if we have an MCPAgentClientSession in the context
|
|
54
|
+
from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
|
|
55
|
+
|
|
56
|
+
if hasattr(context, "session") and isinstance(context.session, MCPAgentClientSession):
|
|
57
|
+
agent_name = context.session.agent_name
|
|
58
|
+
|
|
59
|
+
# 2. If no agent name yet, use a sensible default
|
|
60
|
+
if not agent_name:
|
|
61
|
+
agent_name = "Unknown Agent"
|
|
62
|
+
|
|
63
|
+
# Create human input request
|
|
64
|
+
request = HumanInputRequest(
|
|
65
|
+
prompt=params.message,
|
|
66
|
+
description=f"Schema: {params.requestedSchema}" if params.requestedSchema else None,
|
|
67
|
+
request_id=f"elicit_{id(params)}",
|
|
68
|
+
metadata={
|
|
69
|
+
"agent_name": agent_name,
|
|
70
|
+
"server_name": server_name,
|
|
71
|
+
"elicitation": True,
|
|
72
|
+
"requested_schema": params.requestedSchema,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
# Call the enhanced elicitation handler
|
|
78
|
+
response = await elicitation_input_callback(
|
|
79
|
+
request=request,
|
|
80
|
+
agent_name=agent_name,
|
|
81
|
+
server_name=server_name,
|
|
82
|
+
server_info=server_info,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Check for special action responses
|
|
86
|
+
response_data = response.response.strip()
|
|
87
|
+
|
|
88
|
+
# Handle special responses
|
|
89
|
+
if response_data == "__DECLINED__":
|
|
90
|
+
return ElicitResult(action="decline")
|
|
91
|
+
elif response_data == "__CANCELLED__":
|
|
92
|
+
return ElicitResult(action="cancel")
|
|
93
|
+
elif response_data == "__DISABLE_SERVER__":
|
|
94
|
+
# Respect user's request: disable elicitation for this server for this session
|
|
95
|
+
logger.warning(
|
|
96
|
+
f"User requested to disable elicitation for server: {server_name} — disabling for session"
|
|
97
|
+
)
|
|
98
|
+
try:
|
|
99
|
+
from fast_agent.human_input.elicitation_state import elicitation_state
|
|
100
|
+
|
|
101
|
+
elicitation_state.disable_server(server_name)
|
|
102
|
+
except Exception:
|
|
103
|
+
# Do not fail the flow if state update fails
|
|
104
|
+
pass
|
|
105
|
+
return ElicitResult(action="cancel")
|
|
106
|
+
|
|
107
|
+
# Parse response based on schema if provided
|
|
108
|
+
if params.requestedSchema:
|
|
109
|
+
# Check if the response is already JSON (from our form)
|
|
110
|
+
try:
|
|
111
|
+
# Try to parse as JSON first (from schema-driven form)
|
|
112
|
+
content = json.loads(response_data)
|
|
113
|
+
# Validate that all required fields are present
|
|
114
|
+
required_fields = params.requestedSchema.get("required", [])
|
|
115
|
+
for field in required_fields:
|
|
116
|
+
if field not in content:
|
|
117
|
+
logger.warning(f"Missing required field '{field}' in elicitation response")
|
|
118
|
+
return ElicitResult(action="decline")
|
|
119
|
+
except json.JSONDecodeError:
|
|
120
|
+
# Not JSON, try to handle as simple text response
|
|
121
|
+
# This is a fallback for simple schemas or text-based responses
|
|
122
|
+
properties = params.requestedSchema.get("properties", {})
|
|
123
|
+
if len(properties) == 1:
|
|
124
|
+
# Single field schema - try to parse based on type
|
|
125
|
+
field_name = next(iter(properties))
|
|
126
|
+
field_def = properties[field_name]
|
|
127
|
+
field_type = field_def.get("type")
|
|
128
|
+
|
|
129
|
+
if field_type == "boolean":
|
|
130
|
+
# Parse boolean values
|
|
131
|
+
if response_data.lower() in ["yes", "y", "true", "1"]:
|
|
132
|
+
content = {field_name: True}
|
|
133
|
+
elif response_data.lower() in ["no", "n", "false", "0"]:
|
|
134
|
+
content = {field_name: False}
|
|
135
|
+
else:
|
|
136
|
+
return ElicitResult(action="decline")
|
|
137
|
+
elif field_type == "string":
|
|
138
|
+
content = {field_name: response_data}
|
|
139
|
+
elif field_type in ["number", "integer"]:
|
|
140
|
+
try:
|
|
141
|
+
value = (
|
|
142
|
+
int(response_data)
|
|
143
|
+
if field_type == "integer"
|
|
144
|
+
else float(response_data)
|
|
145
|
+
)
|
|
146
|
+
content = {field_name: value}
|
|
147
|
+
except ValueError:
|
|
148
|
+
return ElicitResult(action="decline")
|
|
149
|
+
else:
|
|
150
|
+
# Unknown type, just pass as string
|
|
151
|
+
content = {field_name: response_data}
|
|
152
|
+
else:
|
|
153
|
+
# Multiple fields but text response - can't parse reliably
|
|
154
|
+
logger.warning("Text response provided for multi-field schema")
|
|
155
|
+
return ElicitResult(action="decline")
|
|
156
|
+
else:
|
|
157
|
+
# No schema, just return the raw response
|
|
158
|
+
content = {"response": response_data}
|
|
159
|
+
|
|
160
|
+
# Return the response wrapped in ElicitResult with accept action
|
|
161
|
+
return ElicitResult(action="accept", content=content)
|
|
162
|
+
except (KeyboardInterrupt, EOFError, TimeoutError):
|
|
163
|
+
# User cancelled or timeout
|
|
164
|
+
return ElicitResult(action="cancel")
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
from typing import AsyncGenerator, Callable
|
|
4
|
+
|
|
5
|
+
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
6
|
+
from mcp import ClientSession
|
|
7
|
+
|
|
8
|
+
from fast_agent.core.logging.logger import get_logger
|
|
9
|
+
from fast_agent.mcp.interfaces import ServerRegistryProtocol
|
|
10
|
+
from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@asynccontextmanager
|
|
16
|
+
async def gen_client(
|
|
17
|
+
server_name: str,
|
|
18
|
+
server_registry: ServerRegistryProtocol,
|
|
19
|
+
client_session_factory: Callable[
|
|
20
|
+
[MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
|
|
21
|
+
ClientSession,
|
|
22
|
+
] = MCPAgentClientSession,
|
|
23
|
+
) -> AsyncGenerator[ClientSession, None]:
|
|
24
|
+
"""
|
|
25
|
+
Create a client session to the specified server.
|
|
26
|
+
Handles server startup, initialization, and message receive loop setup.
|
|
27
|
+
If required, callers can specify their own message receive loop and ClientSession class constructor to customize further.
|
|
28
|
+
For persistent connections, use connect() or MCPConnectionManager instead.
|
|
29
|
+
"""
|
|
30
|
+
if not server_registry:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
"Server registry not found in the context. Please specify one either on this method, or in the context."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
async with server_registry.initialize_server(
|
|
36
|
+
server_name=server_name,
|
|
37
|
+
client_session_factory=client_session_factory,
|
|
38
|
+
) as session:
|
|
39
|
+
yield session
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def connect(
|
|
43
|
+
server_name: str,
|
|
44
|
+
server_registry: ServerRegistryProtocol,
|
|
45
|
+
client_session_factory: Callable[
|
|
46
|
+
[MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
|
|
47
|
+
ClientSession,
|
|
48
|
+
] = MCPAgentClientSession,
|
|
49
|
+
) -> ClientSession:
|
|
50
|
+
"""
|
|
51
|
+
Create a persistent client session to the specified server.
|
|
52
|
+
Handles server startup, initialization, and message receive loop setup.
|
|
53
|
+
If required, callers can specify their own message receive loop and ClientSession class constructor to customize further.
|
|
54
|
+
"""
|
|
55
|
+
if not server_registry:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
"Server registry not found in the context. Please specify one either on this method, or in the context."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
server_connection = await server_registry.connection_manager.get_server(
|
|
61
|
+
server_name=server_name,
|
|
62
|
+
client_session_factory=client_session_factory,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return server_connection.session
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def disconnect(
|
|
69
|
+
server_name: str | None,
|
|
70
|
+
server_registry: ServerRegistryProtocol,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Disconnect from the specified server. If server_name is None, disconnect from all servers.
|
|
74
|
+
"""
|
|
75
|
+
if not server_registry:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
"Server registry not found in the context. Please specify one either on this method, or in the context."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if server_name:
|
|
81
|
+
await server_registry.connection_manager.disconnect_server(server_name=server_name)
|
|
82
|
+
else:
|
|
83
|
+
await server_registry.connection_manager.disconnect_all_servers()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Helper modules for working with MCP content (Fast Agent namespace).
|
|
3
|
+
|
|
4
|
+
This mirrors the legacy fast_agent.mcp.helpers API to provide a stable,
|
|
5
|
+
cycle-free import path now that PromptMessageExtended lives in fast_agent.mcp.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .content_helpers import (
|
|
9
|
+
ensure_multipart_messages,
|
|
10
|
+
get_image_data,
|
|
11
|
+
get_resource_text,
|
|
12
|
+
get_resource_uri,
|
|
13
|
+
get_text,
|
|
14
|
+
is_image_content,
|
|
15
|
+
is_resource_content,
|
|
16
|
+
is_resource_link,
|
|
17
|
+
is_text_content,
|
|
18
|
+
normalize_to_extended_list,
|
|
19
|
+
split_thinking_content,
|
|
20
|
+
text_content,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"get_text",
|
|
25
|
+
"get_image_data",
|
|
26
|
+
"get_resource_uri",
|
|
27
|
+
"is_text_content",
|
|
28
|
+
"is_image_content",
|
|
29
|
+
"is_resource_content",
|
|
30
|
+
"is_resource_link",
|
|
31
|
+
"get_resource_text",
|
|
32
|
+
"ensure_multipart_messages",
|
|
33
|
+
"normalize_to_extended_list",
|
|
34
|
+
"split_thinking_content",
|
|
35
|
+
"text_content",
|
|
36
|
+
]
|