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
|
@@ -30,13 +30,13 @@ from mcp.types import (
|
|
|
30
30
|
)
|
|
31
31
|
from pydantic import FileUrl
|
|
32
32
|
|
|
33
|
-
from
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from
|
|
33
|
+
from fast_agent.context_dependent import ContextDependent
|
|
34
|
+
from fast_agent.core.logging.logger import get_logger
|
|
35
|
+
from fast_agent.mcp.helpers.server_config_helpers import get_server_config
|
|
36
|
+
from fast_agent.mcp.sampling import sample
|
|
37
37
|
|
|
38
38
|
if TYPE_CHECKING:
|
|
39
|
-
from
|
|
39
|
+
from fast_agent.config import MCPServerSettings
|
|
40
40
|
|
|
41
41
|
logger = get_logger(__name__)
|
|
42
42
|
|
|
@@ -88,6 +88,8 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
88
88
|
self.api_key: str | None = kwargs.pop("api_key", None)
|
|
89
89
|
# Extract custom elicitation handler if provided
|
|
90
90
|
custom_elicitation_handler = kwargs.pop("elicitation_handler", None)
|
|
91
|
+
# Extract optional context for ContextDependent mixin without passing it to ClientSession
|
|
92
|
+
self._context = kwargs.pop("context", None)
|
|
91
93
|
|
|
92
94
|
version = version("fast-agent-mcp") or "dev"
|
|
93
95
|
fast_agent: Implementation = Implementation(name="fast-agent-mcp", version=version)
|
|
@@ -119,9 +121,9 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
119
121
|
# Try to resolve using factory
|
|
120
122
|
elicitation_handler = None
|
|
121
123
|
try:
|
|
122
|
-
from
|
|
123
|
-
from
|
|
124
|
-
from
|
|
124
|
+
from fast_agent.agents.agent_types import AgentConfig
|
|
125
|
+
from fast_agent.context import get_current_context
|
|
126
|
+
from fast_agent.mcp.elicitation_factory import resolve_elicitation_handler
|
|
125
127
|
|
|
126
128
|
context = get_current_context()
|
|
127
129
|
if context and context.config:
|
|
@@ -141,7 +143,7 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
141
143
|
# Fallback to forms handler only if factory resolution wasn't attempted
|
|
142
144
|
# If factory was attempted and returned None, respect that (means no elicitation capability)
|
|
143
145
|
if elicitation_handler is None and not self.server_config:
|
|
144
|
-
from
|
|
146
|
+
from fast_agent.mcp.elicitation_handlers import forms_elicitation_handler
|
|
145
147
|
|
|
146
148
|
elicitation_handler = forms_elicitation_handler
|
|
147
149
|
|
|
@@ -157,7 +159,7 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
157
159
|
def _should_enable_auto_sampling(self) -> bool:
|
|
158
160
|
"""Check if auto_sampling is enabled at the application level."""
|
|
159
161
|
try:
|
|
160
|
-
from
|
|
162
|
+
from fast_agent.context import get_current_context
|
|
161
163
|
|
|
162
164
|
context = get_current_context()
|
|
163
165
|
if context and context.config:
|
|
@@ -190,12 +192,16 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
190
192
|
return result
|
|
191
193
|
except Exception as e:
|
|
192
194
|
# Handle connection errors cleanly
|
|
195
|
+
# Looking at the MCP SDK, this should probably handle MCPError
|
|
193
196
|
from anyio import ClosedResourceError
|
|
194
|
-
|
|
197
|
+
|
|
195
198
|
if isinstance(e, ClosedResourceError):
|
|
196
199
|
# Show clean offline message and convert to ConnectionError
|
|
197
|
-
from
|
|
198
|
-
|
|
200
|
+
from fast_agent.ui import console
|
|
201
|
+
|
|
202
|
+
console.console.print(
|
|
203
|
+
f"[dim red]MCP server {self.session_server_name} offline[/dim red]"
|
|
204
|
+
)
|
|
199
205
|
raise ConnectionError(f"MCP server {self.session_server_name} offline") from e
|
|
200
206
|
else:
|
|
201
207
|
logger.error(f"send_request failed: {str(e)}")
|
|
@@ -206,7 +212,7 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
206
212
|
Can be overridden by subclasses to handle a notification without needing
|
|
207
213
|
to listen on the message stream.
|
|
208
214
|
"""
|
|
209
|
-
logger.
|
|
215
|
+
logger.debug(
|
|
210
216
|
"_received_notification: notification=",
|
|
211
217
|
data=notification.model_dump(),
|
|
212
218
|
)
|
|
@@ -245,86 +251,85 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
|
|
|
245
251
|
except Exception as e:
|
|
246
252
|
logger.error(f"Error in tool list changed callback: {e}")
|
|
247
253
|
|
|
254
|
+
# TODO -- decide whether to make this override type safe or not (modify SDK)
|
|
248
255
|
async def call_tool(
|
|
249
|
-
self,
|
|
250
|
-
name: str,
|
|
251
|
-
arguments: dict | None = None,
|
|
252
|
-
_meta: dict | None = None,
|
|
253
|
-
**kwargs
|
|
256
|
+
self, name: str, arguments: dict | None = None, _meta: dict | None = None, **kwargs
|
|
254
257
|
) -> CallToolResult:
|
|
255
258
|
"""Call a tool with optional metadata support."""
|
|
256
259
|
if _meta:
|
|
257
260
|
from mcp.types import RequestParams
|
|
258
|
-
|
|
261
|
+
|
|
259
262
|
# Safe merge - preserve existing meta fields like progressToken
|
|
260
|
-
existing_meta = kwargs.get(
|
|
263
|
+
existing_meta = kwargs.get("meta")
|
|
261
264
|
if existing_meta:
|
|
262
|
-
meta_dict =
|
|
265
|
+
meta_dict = (
|
|
266
|
+
existing_meta.model_dump() if hasattr(existing_meta, "model_dump") else {}
|
|
267
|
+
)
|
|
263
268
|
meta_dict.update(_meta)
|
|
264
269
|
meta_obj = RequestParams.Meta(**meta_dict)
|
|
265
270
|
else:
|
|
266
271
|
meta_obj = RequestParams.Meta(**_meta)
|
|
267
|
-
|
|
272
|
+
|
|
268
273
|
# Create CallToolRequestParams without meta, then add _meta via model_dump
|
|
269
274
|
params = CallToolRequestParams(name=name, arguments=arguments)
|
|
270
275
|
params_dict = params.model_dump(by_alias=True)
|
|
271
276
|
params_dict["_meta"] = meta_obj.model_dump()
|
|
272
|
-
|
|
277
|
+
|
|
273
278
|
# Create request with proper types
|
|
274
279
|
request = CallToolRequest(
|
|
275
|
-
method="tools/call",
|
|
276
|
-
params=CallToolRequestParams.model_validate(params_dict)
|
|
280
|
+
method="tools/call", params=CallToolRequestParams.model_validate(params_dict)
|
|
277
281
|
)
|
|
278
|
-
|
|
282
|
+
|
|
279
283
|
return await self.send_request(request, CallToolResult)
|
|
280
284
|
else:
|
|
281
285
|
return await super().call_tool(name, arguments, **kwargs)
|
|
282
286
|
|
|
283
|
-
async def read_resource(
|
|
287
|
+
async def read_resource(
|
|
288
|
+
self, uri: str, _meta: dict | None = None, **kwargs
|
|
289
|
+
) -> ReadResourceResult:
|
|
284
290
|
"""Read a resource with optional metadata support."""
|
|
285
291
|
if _meta:
|
|
286
292
|
from mcp.types import RequestParams
|
|
287
|
-
|
|
293
|
+
|
|
288
294
|
# Safe merge - preserve existing meta fields like progressToken
|
|
289
|
-
existing_meta = kwargs.get(
|
|
295
|
+
existing_meta = kwargs.get("meta")
|
|
290
296
|
if existing_meta:
|
|
291
|
-
meta_dict =
|
|
297
|
+
meta_dict = (
|
|
298
|
+
existing_meta.model_dump() if hasattr(existing_meta, "model_dump") else {}
|
|
299
|
+
)
|
|
292
300
|
meta_dict.update(_meta)
|
|
293
301
|
meta_obj = RequestParams.Meta(**meta_dict)
|
|
294
302
|
else:
|
|
295
303
|
meta_obj = RequestParams.Meta(**_meta)
|
|
296
|
-
|
|
304
|
+
|
|
297
305
|
request = ReadResourceRequest(
|
|
298
|
-
method="resources/read",
|
|
299
|
-
params=ReadResourceRequestParams(uri=uri, meta=meta_obj)
|
|
306
|
+
method="resources/read", params=ReadResourceRequestParams(uri=uri, meta=meta_obj)
|
|
300
307
|
)
|
|
301
308
|
return await self.send_request(request, ReadResourceResult)
|
|
302
309
|
else:
|
|
303
310
|
return await super().read_resource(uri, **kwargs)
|
|
304
311
|
|
|
305
312
|
async def get_prompt(
|
|
306
|
-
self,
|
|
307
|
-
name: str,
|
|
308
|
-
arguments: dict | None = None,
|
|
309
|
-
_meta: dict | None = None,
|
|
310
|
-
**kwargs
|
|
313
|
+
self, name: str, arguments: dict | None = None, _meta: dict | None = None, **kwargs
|
|
311
314
|
) -> GetPromptResult:
|
|
312
315
|
"""Get a prompt with optional metadata support."""
|
|
313
316
|
if _meta:
|
|
314
317
|
from mcp.types import RequestParams
|
|
315
|
-
|
|
318
|
+
|
|
316
319
|
# Safe merge - preserve existing meta fields like progressToken
|
|
317
|
-
existing_meta = kwargs.get(
|
|
320
|
+
existing_meta = kwargs.get("meta")
|
|
318
321
|
if existing_meta:
|
|
319
|
-
meta_dict =
|
|
322
|
+
meta_dict = (
|
|
323
|
+
existing_meta.model_dump() if hasattr(existing_meta, "model_dump") else {}
|
|
324
|
+
)
|
|
320
325
|
meta_dict.update(_meta)
|
|
321
326
|
meta_obj = RequestParams.Meta(**meta_dict)
|
|
322
327
|
else:
|
|
323
328
|
meta_obj = RequestParams.Meta(**_meta)
|
|
324
|
-
|
|
329
|
+
|
|
325
330
|
request = GetPromptRequest(
|
|
326
331
|
method="prompts/get",
|
|
327
|
-
params=GetPromptRequestParams(name=name, arguments=arguments, meta=meta_obj)
|
|
332
|
+
params=GetPromptRequestParams(name=name, arguments=arguments, meta=meta_obj),
|
|
328
333
|
)
|
|
329
334
|
return await self.send_request(request, GetPromptResult)
|
|
330
335
|
else:
|
|
@@ -23,16 +23,16 @@ from mcp.types import (
|
|
|
23
23
|
from opentelemetry import trace
|
|
24
24
|
from pydantic import AnyUrl, BaseModel, ConfigDict
|
|
25
25
|
|
|
26
|
-
from
|
|
27
|
-
from
|
|
28
|
-
from
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
from
|
|
26
|
+
from fast_agent.context_dependent import ContextDependent
|
|
27
|
+
from fast_agent.core.logging.logger import get_logger
|
|
28
|
+
from fast_agent.event_progress import ProgressAction
|
|
29
|
+
from fast_agent.mcp.common import SEP, create_namespaced_name, is_namespaced_name
|
|
30
|
+
from fast_agent.mcp.gen_client import gen_client
|
|
31
|
+
from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
|
|
32
|
+
from fast_agent.mcp.mcp_connection_manager import MCPConnectionManager
|
|
33
33
|
|
|
34
34
|
if TYPE_CHECKING:
|
|
35
|
-
from
|
|
35
|
+
from fast_agent.context import Context
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
logger = get_logger(__name__) # This will be replaced per-instance when agent_name is available
|
|
@@ -84,7 +84,7 @@ class MCPAggregator(ContextDependent):
|
|
|
84
84
|
self._persistent_connection_manager = self.context._connection_manager
|
|
85
85
|
|
|
86
86
|
# Import the display component here to avoid circular imports
|
|
87
|
-
from
|
|
87
|
+
from fast_agent.ui.console_display import ConsoleDisplay
|
|
88
88
|
|
|
89
89
|
# Initialize the display component
|
|
90
90
|
self.display = ConsoleDisplay(config=self.context.config)
|
|
@@ -99,14 +99,16 @@ class MCPAggregator(ContextDependent):
|
|
|
99
99
|
def __init__(
|
|
100
100
|
self,
|
|
101
101
|
server_names: List[str],
|
|
102
|
-
connection_persistence: bool = True,
|
|
102
|
+
connection_persistence: bool = True,
|
|
103
103
|
context: Optional["Context"] = None,
|
|
104
104
|
name: str = None,
|
|
105
|
+
config: Optional[Any] = None, # Accept the agent config for elicitation_handler access
|
|
105
106
|
**kwargs,
|
|
106
107
|
) -> None:
|
|
107
108
|
"""
|
|
108
109
|
:param server_names: A list of server names to connect to.
|
|
109
110
|
:param connection_persistence: Whether to maintain persistent connections to servers (default: True).
|
|
111
|
+
:param config: Optional agent config containing elicitation_handler and other settings.
|
|
110
112
|
Note: The server names must be resolvable by the gen_client function, and specified in the server registry.
|
|
111
113
|
"""
|
|
112
114
|
super().__init__(
|
|
@@ -117,6 +119,7 @@ class MCPAggregator(ContextDependent):
|
|
|
117
119
|
self.server_names = server_names
|
|
118
120
|
self.connection_persistence = connection_persistence
|
|
119
121
|
self.agent_name = name
|
|
122
|
+
self.config = config # Store the config for access in session factory
|
|
120
123
|
self._persistent_connection_manager: MCPConnectionManager = None
|
|
121
124
|
|
|
122
125
|
# Set up logger with agent name in namespace if available
|
|
@@ -186,10 +189,6 @@ class MCPAggregator(ContextDependent):
|
|
|
186
189
|
) -> "MCPAggregator":
|
|
187
190
|
"""
|
|
188
191
|
Factory method to create and initialize an MCPAggregator.
|
|
189
|
-
Use this instead of constructor since we need async initialization.
|
|
190
|
-
If connection_persistence is True, the aggregator will maintain a
|
|
191
|
-
persistent connection to the servers for as long as this aggregator is around.
|
|
192
|
-
By default we do not maintain a persistent connection.
|
|
193
192
|
"""
|
|
194
193
|
|
|
195
194
|
logger.info(f"Creating MCPAggregator with servers: {server_names}")
|
|
@@ -211,6 +210,47 @@ class MCPAggregator(ContextDependent):
|
|
|
211
210
|
logger.error(f"Error creating MCPAggregator: {e}")
|
|
212
211
|
await instance.__aexit__(None, None, None)
|
|
213
212
|
|
|
213
|
+
def _create_session_factory(self, server_name: str):
|
|
214
|
+
"""
|
|
215
|
+
Create a session factory function for the given server.
|
|
216
|
+
This centralizes the logic for creating MCPAgentClientSession instances.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
server_name: The name of the server to create a session for
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
A factory function that creates MCPAgentClientSession instances
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def session_factory(read_stream, write_stream, read_timeout, **kwargs):
|
|
226
|
+
# Get agent's model and name from config if available
|
|
227
|
+
agent_model: str | None = None
|
|
228
|
+
agent_name: str | None = None
|
|
229
|
+
elicitation_handler = None
|
|
230
|
+
api_key: str | None = None
|
|
231
|
+
|
|
232
|
+
# Access config directly if it was passed from BaseAgent
|
|
233
|
+
if self.config:
|
|
234
|
+
agent_model = self.config.model
|
|
235
|
+
agent_name = self.config.name
|
|
236
|
+
elicitation_handler = self.config.elicitation_handler
|
|
237
|
+
api_key = self.config.api_key
|
|
238
|
+
|
|
239
|
+
return MCPAgentClientSession(
|
|
240
|
+
read_stream,
|
|
241
|
+
write_stream,
|
|
242
|
+
read_timeout,
|
|
243
|
+
server_name=server_name,
|
|
244
|
+
agent_model=agent_model,
|
|
245
|
+
agent_name=agent_name,
|
|
246
|
+
api_key=api_key,
|
|
247
|
+
elicitation_handler=elicitation_handler,
|
|
248
|
+
tool_list_changed_callback=self._handle_tool_list_changed,
|
|
249
|
+
**kwargs, # Pass through any additional kwargs like server_config
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
return session_factory
|
|
253
|
+
|
|
214
254
|
async def load_servers(self) -> None:
|
|
215
255
|
"""
|
|
216
256
|
Discover tools from each server in parallel and build an index of namespaced tool names.
|
|
@@ -238,39 +278,8 @@ class MCPAggregator(ContextDependent):
|
|
|
238
278
|
},
|
|
239
279
|
)
|
|
240
280
|
|
|
241
|
-
# Create a wrapper to capture the parameters for the client session
|
|
242
|
-
def session_factory(read_stream, write_stream, read_timeout, **kwargs):
|
|
243
|
-
# Get agent's model and name if this aggregator is part of an agent
|
|
244
|
-
agent_model: str | None = None
|
|
245
|
-
agent_name: str | None = None
|
|
246
|
-
elicitation_handler = None
|
|
247
|
-
api_key: str | None = None
|
|
248
|
-
|
|
249
|
-
# Check if this aggregator is part of an Agent (which has config)
|
|
250
|
-
# Import here to avoid circular dependency
|
|
251
|
-
from mcp_agent.agents.base_agent import BaseAgent
|
|
252
|
-
|
|
253
|
-
if isinstance(self, BaseAgent):
|
|
254
|
-
agent_model = self.config.model
|
|
255
|
-
agent_name = self.config.name
|
|
256
|
-
elicitation_handler = self.config.elicitation_handler
|
|
257
|
-
api_key = self.config.api_key
|
|
258
|
-
|
|
259
|
-
return MCPAgentClientSession(
|
|
260
|
-
read_stream,
|
|
261
|
-
write_stream,
|
|
262
|
-
read_timeout,
|
|
263
|
-
server_name=server_name,
|
|
264
|
-
agent_model=agent_model,
|
|
265
|
-
agent_name=agent_name,
|
|
266
|
-
api_key=api_key,
|
|
267
|
-
elicitation_handler=elicitation_handler,
|
|
268
|
-
tool_list_changed_callback=self._handle_tool_list_changed,
|
|
269
|
-
**kwargs, # Pass through any additional kwargs like server_config
|
|
270
|
-
)
|
|
271
|
-
|
|
272
281
|
await self._persistent_connection_manager.get_server(
|
|
273
|
-
server_name, client_session_factory=
|
|
282
|
+
server_name, client_session_factory=self._create_session_factory(server_name)
|
|
274
283
|
)
|
|
275
284
|
|
|
276
285
|
logger.info(
|
|
@@ -313,46 +322,15 @@ class MCPAggregator(ContextDependent):
|
|
|
313
322
|
|
|
314
323
|
if self.connection_persistence:
|
|
315
324
|
server_connection = await self._persistent_connection_manager.get_server(
|
|
316
|
-
server_name, client_session_factory=
|
|
325
|
+
server_name, client_session_factory=self._create_session_factory(server_name)
|
|
317
326
|
)
|
|
318
327
|
tools = await fetch_tools(server_connection.session, server_name)
|
|
319
328
|
prompts = await fetch_prompts(server_connection.session, server_name)
|
|
320
329
|
else:
|
|
321
|
-
# Create a factory function for the client session
|
|
322
|
-
def create_session(read_stream, write_stream, read_timeout, **kwargs):
|
|
323
|
-
# Get agent's model and name if this aggregator is part of an agent
|
|
324
|
-
agent_model: str | None = None
|
|
325
|
-
agent_name: str | None = None
|
|
326
|
-
elicitation_handler = None
|
|
327
|
-
api_key: str | None = None
|
|
328
|
-
|
|
329
|
-
# Check if this aggregator is part of an Agent (which has config)
|
|
330
|
-
# Import here to avoid circular dependency
|
|
331
|
-
from mcp_agent.agents.base_agent import BaseAgent
|
|
332
|
-
|
|
333
|
-
if isinstance(self, BaseAgent):
|
|
334
|
-
agent_model = self.config.model
|
|
335
|
-
agent_name = self.config.name
|
|
336
|
-
elicitation_handler = self.config.elicitation_handler
|
|
337
|
-
api_key = self.config.api_key
|
|
338
|
-
|
|
339
|
-
return MCPAgentClientSession(
|
|
340
|
-
read_stream,
|
|
341
|
-
write_stream,
|
|
342
|
-
read_timeout,
|
|
343
|
-
server_name=server_name,
|
|
344
|
-
agent_model=agent_model,
|
|
345
|
-
agent_name=agent_name,
|
|
346
|
-
api_key=api_key,
|
|
347
|
-
elicitation_handler=elicitation_handler,
|
|
348
|
-
tool_list_changed_callback=self._handle_tool_list_changed,
|
|
349
|
-
**kwargs, # Pass through any additional kwargs like server_config
|
|
350
|
-
)
|
|
351
|
-
|
|
352
330
|
async with gen_client(
|
|
353
331
|
server_name,
|
|
354
332
|
server_registry=self.context.server_registry,
|
|
355
|
-
client_session_factory=
|
|
333
|
+
client_session_factory=self._create_session_factory(server_name),
|
|
356
334
|
) as client:
|
|
357
335
|
tools = await fetch_tools(client, server_name)
|
|
358
336
|
prompts = await fetch_prompts(client, server_name)
|
|
@@ -410,7 +388,7 @@ class MCPAggregator(ContextDependent):
|
|
|
410
388
|
|
|
411
389
|
try:
|
|
412
390
|
server_conn = await self._persistent_connection_manager.get_server(
|
|
413
|
-
server_name, client_session_factory=
|
|
391
|
+
server_name, client_session_factory=self._create_session_factory(server_name)
|
|
414
392
|
)
|
|
415
393
|
# server_capabilities is a property, not a coroutine
|
|
416
394
|
return server_conn.server_capabilities
|
|
@@ -516,7 +494,7 @@ class MCPAggregator(ContextDependent):
|
|
|
516
494
|
# Get metadata from context for tool, resource, and prompt calls
|
|
517
495
|
metadata = None
|
|
518
496
|
if method_name in ["call_tool", "read_resource", "get_prompt"]:
|
|
519
|
-
from
|
|
497
|
+
from fast_agent.llm.fastagent_llm import _mcp_metadata_var
|
|
520
498
|
|
|
521
499
|
metadata = _mcp_metadata_var.get()
|
|
522
500
|
|
|
@@ -549,7 +527,7 @@ class MCPAggregator(ContextDependent):
|
|
|
549
527
|
try:
|
|
550
528
|
if self.connection_persistence:
|
|
551
529
|
server_connection = await self._persistent_connection_manager.get_server(
|
|
552
|
-
server_name, client_session_factory=
|
|
530
|
+
server_name, client_session_factory=self._create_session_factory(server_name)
|
|
553
531
|
)
|
|
554
532
|
return await try_execute(server_connection.session)
|
|
555
533
|
else:
|
|
@@ -576,7 +554,7 @@ class MCPAggregator(ContextDependent):
|
|
|
576
554
|
return result
|
|
577
555
|
except ConnectionError:
|
|
578
556
|
# Server offline - attempt reconnection
|
|
579
|
-
from
|
|
557
|
+
from fast_agent.ui import console
|
|
580
558
|
|
|
581
559
|
console.console.print(
|
|
582
560
|
f"[dim yellow]MCP server {server_name} reconnecting...[/dim yellow]"
|
|
@@ -591,7 +569,8 @@ class MCPAggregator(ContextDependent):
|
|
|
591
569
|
await asyncio.sleep(0.1)
|
|
592
570
|
|
|
593
571
|
server_connection = await self._persistent_connection_manager.get_server(
|
|
594
|
-
server_name,
|
|
572
|
+
server_name,
|
|
573
|
+
client_session_factory=self._create_session_factory(server_name),
|
|
595
574
|
)
|
|
596
575
|
result = await try_execute(server_connection.session)
|
|
597
576
|
else:
|
|
@@ -1069,39 +1048,11 @@ class MCPAggregator(ContextDependent):
|
|
|
1069
1048
|
|
|
1070
1049
|
async with self._refresh_lock:
|
|
1071
1050
|
try:
|
|
1072
|
-
# Create a factory function that will include our parameters
|
|
1073
|
-
def create_session(read_stream, write_stream, read_timeout):
|
|
1074
|
-
# Get agent name if available
|
|
1075
|
-
agent_model: str | None = None
|
|
1076
|
-
agent_name: str | None = None
|
|
1077
|
-
elicitation_handler = None
|
|
1078
|
-
api_key: str | None = None
|
|
1079
|
-
|
|
1080
|
-
# Import here to avoid circular dependency
|
|
1081
|
-
from mcp_agent.agents.base_agent import BaseAgent
|
|
1082
|
-
|
|
1083
|
-
if isinstance(self, BaseAgent):
|
|
1084
|
-
agent_model = self.config.model
|
|
1085
|
-
agent_name = self.config.name
|
|
1086
|
-
elicitation_handler = self.config.elicitation_handler
|
|
1087
|
-
api_key = self.config.api_key
|
|
1088
|
-
|
|
1089
|
-
return MCPAgentClientSession(
|
|
1090
|
-
read_stream,
|
|
1091
|
-
write_stream,
|
|
1092
|
-
read_timeout,
|
|
1093
|
-
server_name=server_name,
|
|
1094
|
-
agent_model=agent_model,
|
|
1095
|
-
agent_name=agent_name,
|
|
1096
|
-
api_key=api_key,
|
|
1097
|
-
elicitation_handler=elicitation_handler,
|
|
1098
|
-
tool_list_changed_callback=self._handle_tool_list_changed,
|
|
1099
|
-
)
|
|
1100
|
-
|
|
1101
1051
|
# Fetch new tools from the server
|
|
1102
1052
|
if self.connection_persistence:
|
|
1103
1053
|
server_connection = await self._persistent_connection_manager.get_server(
|
|
1104
|
-
server_name,
|
|
1054
|
+
server_name,
|
|
1055
|
+
client_session_factory=self._create_session_factory(server_name),
|
|
1105
1056
|
)
|
|
1106
1057
|
tools_result = await server_connection.session.list_tools()
|
|
1107
1058
|
new_tools = tools_result.tools or []
|
|
@@ -1109,7 +1060,7 @@ class MCPAggregator(ContextDependent):
|
|
|
1109
1060
|
async with gen_client(
|
|
1110
1061
|
server_name,
|
|
1111
1062
|
server_registry=self.context.server_registry,
|
|
1112
|
-
client_session_factory=
|
|
1063
|
+
client_session_factory=self._create_session_factory(server_name),
|
|
1113
1064
|
) as client:
|
|
1114
1065
|
tools_result = await client.list_tools()
|
|
1115
1066
|
new_tools = tools_result.tools or []
|
|
@@ -26,17 +26,17 @@ from mcp.client.stdio import (
|
|
|
26
26
|
from mcp.client.streamable_http import GetSessionIdCallback, streamablehttp_client
|
|
27
27
|
from mcp.types import JSONRPCMessage, ServerCapabilities
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
from
|
|
33
|
-
from
|
|
34
|
-
from
|
|
35
|
-
from
|
|
29
|
+
from fast_agent.config import MCPServerSettings
|
|
30
|
+
from fast_agent.context_dependent import ContextDependent
|
|
31
|
+
from fast_agent.core.exceptions import ServerInitializationError
|
|
32
|
+
from fast_agent.core.logging.logger import get_logger
|
|
33
|
+
from fast_agent.event_progress import ProgressAction
|
|
34
|
+
from fast_agent.mcp.logger_textio import get_stderr_handler
|
|
35
|
+
from fast_agent.mcp.mcp_agent_client_session import MCPAgentClientSession
|
|
36
36
|
|
|
37
37
|
if TYPE_CHECKING:
|
|
38
|
-
from
|
|
39
|
-
from
|
|
38
|
+
from fast_agent.context import Context
|
|
39
|
+
from fast_agent.mcp_server_registry import ServerRegistry
|
|
40
40
|
|
|
41
41
|
logger = get_logger(__name__)
|
|
42
42
|
|
|
@@ -88,13 +88,11 @@ class ServerConnection:
|
|
|
88
88
|
[MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
|
|
89
89
|
ClientSession,
|
|
90
90
|
],
|
|
91
|
-
init_hook: Optional["InitHookCallable"] = None,
|
|
92
91
|
) -> None:
|
|
93
92
|
self.server_name = server_name
|
|
94
93
|
self.server_config = server_config
|
|
95
94
|
self.session: ClientSession | None = None
|
|
96
95
|
self._client_session_factory = client_session_factory
|
|
97
|
-
self._init_hook = init_hook
|
|
98
96
|
self._transport_context_factory = transport_context_factory
|
|
99
97
|
# Signal that session is fully up and initialized
|
|
100
98
|
self._initialized_event = Event()
|
|
@@ -137,9 +135,6 @@ class ServerConnection:
|
|
|
137
135
|
|
|
138
136
|
self.server_capabilities = result.capabilities
|
|
139
137
|
# If there's an init hook, run it
|
|
140
|
-
if self._init_hook:
|
|
141
|
-
logger.info(f"{self.server_name}: Executing init hook.")
|
|
142
|
-
self._init_hook(self.session, self.server_config.auth)
|
|
143
138
|
|
|
144
139
|
# Now the session is ready for use
|
|
145
140
|
self._initialized_event.set()
|
|
@@ -305,13 +300,15 @@ class MCPConnectionManager(ContextDependent):
|
|
|
305
300
|
"""Suppress MCP library's 'Error in sse_reader' messages."""
|
|
306
301
|
if self._mcp_sse_filter_added:
|
|
307
302
|
return
|
|
308
|
-
|
|
303
|
+
|
|
309
304
|
import logging
|
|
310
|
-
|
|
305
|
+
|
|
311
306
|
class MCPSSEErrorFilter(logging.Filter):
|
|
312
307
|
def filter(self, record):
|
|
313
|
-
return not (
|
|
314
|
-
|
|
308
|
+
return not (
|
|
309
|
+
record.name == "mcp.client.sse" and "Error in sse_reader" in record.getMessage()
|
|
310
|
+
)
|
|
311
|
+
|
|
315
312
|
mcp_sse_logger = logging.getLogger("mcp.client.sse")
|
|
316
313
|
mcp_sse_logger.addFilter(MCPSSEErrorFilter())
|
|
317
314
|
self._mcp_sse_filter_added = True
|
|
@@ -323,7 +320,6 @@ class MCPConnectionManager(ContextDependent):
|
|
|
323
320
|
[MemoryObjectReceiveStream, MemoryObjectSendStream, timedelta | None],
|
|
324
321
|
ClientSession,
|
|
325
322
|
],
|
|
326
|
-
init_hook: Optional["InitHookCallable"] = None,
|
|
327
323
|
) -> ServerConnection:
|
|
328
324
|
"""
|
|
329
325
|
Connect to a server and return a RunningServer instance that will persist
|
|
@@ -359,7 +355,7 @@ class MCPConnectionManager(ContextDependent):
|
|
|
359
355
|
elif config.transport == "sse":
|
|
360
356
|
# Suppress MCP library error spam
|
|
361
357
|
self._suppress_mcp_sse_errors()
|
|
362
|
-
|
|
358
|
+
|
|
363
359
|
return _add_none_to_context(
|
|
364
360
|
sse_client(
|
|
365
361
|
config.url,
|
|
@@ -377,7 +373,6 @@ class MCPConnectionManager(ContextDependent):
|
|
|
377
373
|
server_config=config,
|
|
378
374
|
transport_context_factory=transport_context_factory,
|
|
379
375
|
client_session_factory=client_session_factory,
|
|
380
|
-
init_hook=init_hook or self.server_registry.init_hooks.get(server_name),
|
|
381
376
|
)
|
|
382
377
|
|
|
383
378
|
async with self._lock:
|
|
@@ -395,7 +390,6 @@ class MCPConnectionManager(ContextDependent):
|
|
|
395
390
|
self,
|
|
396
391
|
server_name: str,
|
|
397
392
|
client_session_factory: Callable,
|
|
398
|
-
init_hook: Optional["InitHookCallable"] = None,
|
|
399
393
|
) -> ServerConnection:
|
|
400
394
|
"""
|
|
401
395
|
Get a running server instance, launching it if needed.
|
|
@@ -416,7 +410,6 @@ class MCPConnectionManager(ContextDependent):
|
|
|
416
410
|
server_conn = await self.launch_server(
|
|
417
411
|
server_name=server_name,
|
|
418
412
|
client_session_factory=client_session_factory,
|
|
419
|
-
init_hook=init_hook,
|
|
420
413
|
)
|
|
421
414
|
|
|
422
415
|
# Wait until it's fully initialized, or an error occurs
|