massgen 0.0.3__py3-none-any.whl → 0.1.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 massgen might be problematic. Click here for more details.
- massgen/__init__.py +142 -8
- massgen/adapters/__init__.py +29 -0
- massgen/adapters/ag2_adapter.py +483 -0
- massgen/adapters/base.py +183 -0
- massgen/adapters/tests/__init__.py +0 -0
- massgen/adapters/tests/test_ag2_adapter.py +439 -0
- massgen/adapters/tests/test_agent_adapter.py +128 -0
- massgen/adapters/utils/__init__.py +2 -0
- massgen/adapters/utils/ag2_utils.py +236 -0
- massgen/adapters/utils/tests/__init__.py +0 -0
- massgen/adapters/utils/tests/test_ag2_utils.py +138 -0
- massgen/agent_config.py +329 -55
- massgen/api_params_handler/__init__.py +10 -0
- massgen/api_params_handler/_api_params_handler_base.py +99 -0
- massgen/api_params_handler/_chat_completions_api_params_handler.py +176 -0
- massgen/api_params_handler/_claude_api_params_handler.py +113 -0
- massgen/api_params_handler/_response_api_params_handler.py +130 -0
- massgen/backend/__init__.py +39 -4
- massgen/backend/azure_openai.py +385 -0
- massgen/backend/base.py +341 -69
- massgen/backend/base_with_mcp.py +1102 -0
- massgen/backend/capabilities.py +386 -0
- massgen/backend/chat_completions.py +577 -130
- massgen/backend/claude.py +1033 -537
- massgen/backend/claude_code.py +1203 -0
- massgen/backend/cli_base.py +209 -0
- massgen/backend/docs/BACKEND_ARCHITECTURE.md +126 -0
- massgen/backend/{CLAUDE_API_RESEARCH.md → docs/CLAUDE_API_RESEARCH.md} +18 -18
- massgen/backend/{GEMINI_API_DOCUMENTATION.md → docs/GEMINI_API_DOCUMENTATION.md} +9 -9
- massgen/backend/docs/Gemini MCP Integration Analysis.md +1050 -0
- massgen/backend/docs/MCP_IMPLEMENTATION_CLAUDE_BACKEND.md +177 -0
- massgen/backend/docs/MCP_INTEGRATION_RESPONSE_BACKEND.md +352 -0
- massgen/backend/docs/OPENAI_GPT5_MODELS.md +211 -0
- massgen/backend/{OPENAI_RESPONSES_API_FORMAT.md → docs/OPENAI_RESPONSE_API_TOOL_CALLS.md} +3 -3
- massgen/backend/docs/OPENAI_response_streaming.md +20654 -0
- massgen/backend/docs/inference_backend.md +257 -0
- massgen/backend/docs/permissions_and_context_files.md +1085 -0
- massgen/backend/external.py +126 -0
- massgen/backend/gemini.py +1850 -241
- massgen/backend/grok.py +40 -156
- massgen/backend/inference.py +156 -0
- massgen/backend/lmstudio.py +171 -0
- massgen/backend/response.py +1095 -322
- massgen/chat_agent.py +131 -113
- massgen/cli.py +1560 -275
- massgen/config_builder.py +2396 -0
- massgen/configs/BACKEND_CONFIGURATION.md +458 -0
- massgen/configs/README.md +559 -216
- massgen/configs/ag2/ag2_case_study.yaml +27 -0
- massgen/configs/ag2/ag2_coder.yaml +34 -0
- massgen/configs/ag2/ag2_coder_case_study.yaml +36 -0
- massgen/configs/ag2/ag2_gemini.yaml +27 -0
- massgen/configs/ag2/ag2_groupchat.yaml +108 -0
- massgen/configs/ag2/ag2_groupchat_gpt.yaml +118 -0
- massgen/configs/ag2/ag2_single_agent.yaml +21 -0
- massgen/configs/basic/multi/fast_timeout_example.yaml +37 -0
- massgen/configs/basic/multi/gemini_4o_claude.yaml +31 -0
- massgen/configs/basic/multi/gemini_gpt5nano_claude.yaml +36 -0
- massgen/configs/{gemini_4o_claude.yaml → basic/multi/geminicode_4o_claude.yaml} +3 -3
- massgen/configs/basic/multi/geminicode_gpt5nano_claude.yaml +36 -0
- massgen/configs/basic/multi/glm_gemini_claude.yaml +25 -0
- massgen/configs/basic/multi/gpt4o_audio_generation.yaml +30 -0
- massgen/configs/basic/multi/gpt4o_image_generation.yaml +31 -0
- massgen/configs/basic/multi/gpt5nano_glm_qwen.yaml +26 -0
- massgen/configs/basic/multi/gpt5nano_image_understanding.yaml +26 -0
- massgen/configs/{three_agents_default.yaml → basic/multi/three_agents_default.yaml} +8 -4
- massgen/configs/basic/multi/three_agents_opensource.yaml +27 -0
- massgen/configs/basic/multi/three_agents_vllm.yaml +20 -0
- massgen/configs/basic/multi/two_agents_gemini.yaml +19 -0
- massgen/configs/{two_agents.yaml → basic/multi/two_agents_gpt5.yaml} +14 -6
- massgen/configs/basic/multi/two_agents_opensource_lmstudio.yaml +31 -0
- massgen/configs/basic/multi/two_qwen_vllm_sglang.yaml +28 -0
- massgen/configs/{single_agent.yaml → basic/single/single_agent.yaml} +1 -1
- massgen/configs/{single_flash2.5.yaml → basic/single/single_flash2.5.yaml} +1 -2
- massgen/configs/basic/single/single_gemini2.5pro.yaml +16 -0
- massgen/configs/basic/single/single_gpt4o_audio_generation.yaml +22 -0
- massgen/configs/basic/single/single_gpt4o_image_generation.yaml +22 -0
- massgen/configs/basic/single/single_gpt4o_video_generation.yaml +24 -0
- massgen/configs/basic/single/single_gpt5nano.yaml +20 -0
- massgen/configs/basic/single/single_gpt5nano_file_search.yaml +18 -0
- massgen/configs/basic/single/single_gpt5nano_image_understanding.yaml +17 -0
- massgen/configs/basic/single/single_gptoss120b.yaml +15 -0
- massgen/configs/basic/single/single_openrouter_audio_understanding.yaml +15 -0
- massgen/configs/basic/single/single_qwen_video_understanding.yaml +15 -0
- massgen/configs/debug/code_execution/command_filtering_blacklist.yaml +29 -0
- massgen/configs/debug/code_execution/command_filtering_whitelist.yaml +28 -0
- massgen/configs/debug/code_execution/docker_verification.yaml +29 -0
- massgen/configs/debug/skip_coordination_test.yaml +27 -0
- massgen/configs/debug/test_sdk_migration.yaml +17 -0
- massgen/configs/docs/DISCORD_MCP_SETUP.md +208 -0
- massgen/configs/docs/TWITTER_MCP_ENESCINAR_SETUP.md +82 -0
- massgen/configs/providers/azure/azure_openai_multi.yaml +21 -0
- massgen/configs/providers/azure/azure_openai_single.yaml +19 -0
- massgen/configs/providers/claude/claude.yaml +14 -0
- massgen/configs/providers/gemini/gemini_gpt5nano.yaml +28 -0
- massgen/configs/providers/local/lmstudio.yaml +11 -0
- massgen/configs/providers/openai/gpt5.yaml +46 -0
- massgen/configs/providers/openai/gpt5_nano.yaml +46 -0
- massgen/configs/providers/others/grok_single_agent.yaml +19 -0
- massgen/configs/providers/others/zai_coding_team.yaml +108 -0
- massgen/configs/providers/others/zai_glm45.yaml +12 -0
- massgen/configs/{creative_team.yaml → teams/creative/creative_team.yaml} +16 -6
- massgen/configs/{travel_planning.yaml → teams/creative/travel_planning.yaml} +16 -6
- massgen/configs/{news_analysis.yaml → teams/research/news_analysis.yaml} +16 -6
- massgen/configs/{research_team.yaml → teams/research/research_team.yaml} +15 -7
- massgen/configs/{technical_analysis.yaml → teams/research/technical_analysis.yaml} +16 -6
- massgen/configs/tools/code-execution/basic_command_execution.yaml +25 -0
- massgen/configs/tools/code-execution/code_execution_use_case_simple.yaml +41 -0
- massgen/configs/tools/code-execution/docker_claude_code.yaml +32 -0
- massgen/configs/tools/code-execution/docker_multi_agent.yaml +32 -0
- massgen/configs/tools/code-execution/docker_simple.yaml +29 -0
- massgen/configs/tools/code-execution/docker_with_resource_limits.yaml +32 -0
- massgen/configs/tools/code-execution/multi_agent_playwright_automation.yaml +57 -0
- massgen/configs/tools/filesystem/cc_gpt5_gemini_filesystem.yaml +34 -0
- massgen/configs/tools/filesystem/claude_code_context_sharing.yaml +68 -0
- massgen/configs/tools/filesystem/claude_code_flash2.5.yaml +43 -0
- massgen/configs/tools/filesystem/claude_code_flash2.5_gptoss.yaml +49 -0
- massgen/configs/tools/filesystem/claude_code_gpt5nano.yaml +31 -0
- massgen/configs/tools/filesystem/claude_code_single.yaml +40 -0
- massgen/configs/tools/filesystem/fs_permissions_test.yaml +87 -0
- massgen/configs/tools/filesystem/gemini_gemini_workspace_cleanup.yaml +54 -0
- massgen/configs/tools/filesystem/gemini_gpt5_filesystem_casestudy.yaml +30 -0
- massgen/configs/tools/filesystem/gemini_gpt5nano_file_context_path.yaml +43 -0
- massgen/configs/tools/filesystem/gemini_gpt5nano_protected_paths.yaml +45 -0
- massgen/configs/tools/filesystem/gpt5mini_cc_fs_context_path.yaml +31 -0
- massgen/configs/tools/filesystem/grok4_gpt5_gemini_filesystem.yaml +32 -0
- massgen/configs/tools/filesystem/multiturn/grok4_gpt5_claude_code_filesystem_multiturn.yaml +58 -0
- massgen/configs/tools/filesystem/multiturn/grok4_gpt5_gemini_filesystem_multiturn.yaml +58 -0
- massgen/configs/tools/filesystem/multiturn/two_claude_code_filesystem_multiturn.yaml +47 -0
- massgen/configs/tools/filesystem/multiturn/two_gemini_flash_filesystem_multiturn.yaml +48 -0
- massgen/configs/tools/mcp/claude_code_discord_mcp_example.yaml +27 -0
- massgen/configs/tools/mcp/claude_code_simple_mcp.yaml +35 -0
- massgen/configs/tools/mcp/claude_code_twitter_mcp_example.yaml +32 -0
- massgen/configs/tools/mcp/claude_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/claude_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/five_agents_travel_mcp_test.yaml +157 -0
- massgen/configs/tools/mcp/five_agents_weather_mcp_test.yaml +103 -0
- massgen/configs/tools/mcp/gemini_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test.yaml +23 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_sharing.yaml +23 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_single_agent.yaml +17 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_with_claude_code.yaml +24 -0
- massgen/configs/tools/mcp/gemini_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/gemini_notion_mcp.yaml +52 -0
- massgen/configs/tools/mcp/gpt5_nano_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/gpt5_nano_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/gpt5mini_claude_code_discord_mcp_example.yaml +38 -0
- massgen/configs/tools/mcp/gpt_oss_mcp_example.yaml +25 -0
- massgen/configs/tools/mcp/gpt_oss_mcp_test.yaml +28 -0
- massgen/configs/tools/mcp/grok3_mini_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/grok3_mini_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/multimcp_gemini.yaml +111 -0
- massgen/configs/tools/mcp/qwen_api_mcp_example.yaml +25 -0
- massgen/configs/tools/mcp/qwen_api_mcp_test.yaml +28 -0
- massgen/configs/tools/mcp/qwen_local_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/qwen_local_mcp_test.yaml +27 -0
- massgen/configs/tools/planning/five_agents_discord_mcp_planning_mode.yaml +140 -0
- massgen/configs/tools/planning/five_agents_filesystem_mcp_planning_mode.yaml +151 -0
- massgen/configs/tools/planning/five_agents_notion_mcp_planning_mode.yaml +151 -0
- massgen/configs/tools/planning/five_agents_twitter_mcp_planning_mode.yaml +155 -0
- massgen/configs/tools/planning/gpt5_mini_case_study_mcp_planning_mode.yaml +73 -0
- massgen/configs/tools/web-search/claude_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gemini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gpt5_mini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gpt_oss_streamable_http_test.yaml +44 -0
- massgen/configs/tools/web-search/grok3_mini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/qwen_api_streamable_http_test.yaml +44 -0
- massgen/configs/tools/web-search/qwen_local_streamable_http_test.yaml +43 -0
- massgen/coordination_tracker.py +708 -0
- massgen/docker/README.md +462 -0
- massgen/filesystem_manager/__init__.py +21 -0
- massgen/filesystem_manager/_base.py +9 -0
- massgen/filesystem_manager/_code_execution_server.py +545 -0
- massgen/filesystem_manager/_docker_manager.py +477 -0
- massgen/filesystem_manager/_file_operation_tracker.py +248 -0
- massgen/filesystem_manager/_filesystem_manager.py +813 -0
- massgen/filesystem_manager/_path_permission_manager.py +1261 -0
- massgen/filesystem_manager/_workspace_tools_server.py +1815 -0
- massgen/formatter/__init__.py +10 -0
- massgen/formatter/_chat_completions_formatter.py +284 -0
- massgen/formatter/_claude_formatter.py +235 -0
- massgen/formatter/_formatter_base.py +156 -0
- massgen/formatter/_response_formatter.py +263 -0
- massgen/frontend/__init__.py +1 -2
- massgen/frontend/coordination_ui.py +471 -286
- massgen/frontend/displays/base_display.py +56 -11
- massgen/frontend/displays/create_coordination_table.py +1956 -0
- massgen/frontend/displays/rich_terminal_display.py +1259 -619
- massgen/frontend/displays/simple_display.py +9 -4
- massgen/frontend/displays/terminal_display.py +27 -68
- massgen/logger_config.py +681 -0
- massgen/mcp_tools/README.md +232 -0
- massgen/mcp_tools/__init__.py +105 -0
- massgen/mcp_tools/backend_utils.py +1035 -0
- massgen/mcp_tools/circuit_breaker.py +195 -0
- massgen/mcp_tools/client.py +894 -0
- massgen/mcp_tools/config_validator.py +138 -0
- massgen/mcp_tools/docs/circuit_breaker.md +646 -0
- massgen/mcp_tools/docs/client.md +950 -0
- massgen/mcp_tools/docs/config_validator.md +478 -0
- massgen/mcp_tools/docs/exceptions.md +1165 -0
- massgen/mcp_tools/docs/security.md +854 -0
- massgen/mcp_tools/exceptions.py +338 -0
- massgen/mcp_tools/hooks.py +212 -0
- massgen/mcp_tools/security.py +780 -0
- massgen/message_templates.py +342 -64
- massgen/orchestrator.py +1515 -241
- massgen/stream_chunk/__init__.py +35 -0
- massgen/stream_chunk/base.py +92 -0
- massgen/stream_chunk/multimodal.py +237 -0
- massgen/stream_chunk/text.py +162 -0
- massgen/tests/mcp_test_server.py +150 -0
- massgen/tests/multi_turn_conversation_design.md +0 -8
- massgen/tests/test_azure_openai_backend.py +156 -0
- massgen/tests/test_backend_capabilities.py +262 -0
- massgen/tests/test_backend_event_loop_all.py +179 -0
- massgen/tests/test_chat_completions_refactor.py +142 -0
- massgen/tests/test_claude_backend.py +15 -28
- massgen/tests/test_claude_code.py +268 -0
- massgen/tests/test_claude_code_context_sharing.py +233 -0
- massgen/tests/test_claude_code_orchestrator.py +175 -0
- massgen/tests/test_cli_backends.py +180 -0
- massgen/tests/test_code_execution.py +679 -0
- massgen/tests/test_external_agent_backend.py +134 -0
- massgen/tests/test_final_presentation_fallback.py +237 -0
- massgen/tests/test_gemini_planning_mode.py +351 -0
- massgen/tests/test_grok_backend.py +7 -10
- massgen/tests/test_http_mcp_server.py +42 -0
- massgen/tests/test_integration_simple.py +198 -0
- massgen/tests/test_mcp_blocking.py +125 -0
- massgen/tests/test_message_context_building.py +29 -47
- massgen/tests/test_orchestrator_final_presentation.py +48 -0
- massgen/tests/test_path_permission_manager.py +2087 -0
- massgen/tests/test_rich_terminal_display.py +14 -13
- massgen/tests/test_timeout.py +133 -0
- massgen/tests/test_v3_3agents.py +11 -12
- massgen/tests/test_v3_simple.py +8 -13
- massgen/tests/test_v3_three_agents.py +11 -18
- massgen/tests/test_v3_two_agents.py +8 -13
- massgen/token_manager/__init__.py +7 -0
- massgen/token_manager/token_manager.py +400 -0
- massgen/utils.py +52 -16
- massgen/v1/agent.py +45 -91
- massgen/v1/agents.py +18 -53
- massgen/v1/backends/gemini.py +50 -153
- massgen/v1/backends/grok.py +21 -54
- massgen/v1/backends/oai.py +39 -111
- massgen/v1/cli.py +36 -93
- massgen/v1/config.py +8 -12
- massgen/v1/logging.py +43 -127
- massgen/v1/main.py +18 -32
- massgen/v1/orchestrator.py +68 -209
- massgen/v1/streaming_display.py +62 -163
- massgen/v1/tools.py +8 -12
- massgen/v1/types.py +9 -23
- massgen/v1/utils.py +5 -23
- massgen-0.1.0.dist-info/METADATA +1245 -0
- massgen-0.1.0.dist-info/RECORD +273 -0
- massgen-0.1.0.dist-info/entry_points.txt +2 -0
- massgen/frontend/logging/__init__.py +0 -9
- massgen/frontend/logging/realtime_logger.py +0 -197
- massgen-0.0.3.dist-info/METADATA +0 -568
- massgen-0.0.3.dist-info/RECORD +0 -76
- massgen-0.0.3.dist-info/entry_points.txt +0 -2
- /massgen/backend/{Function calling openai responses.md → docs/Function calling openai responses.md} +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/WHEEL +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,950 @@
|
|
|
1
|
+
# MCP Client Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The MCP client module provides the core functionality for connecting to and interacting with MCP (Model Context Protocol) servers. It includes two main classes: `MCPClient` for single-server connections and `MultiMCPClient` for managing multiple servers simultaneously.
|
|
6
|
+
|
|
7
|
+
## Client Architecture
|
|
8
|
+
|
|
9
|
+
The client architecture follows an async-first design with comprehensive error handling and security validation:
|
|
10
|
+
|
|
11
|
+
- **Connection Lifecycle**: Automatic connection management with reconnection capabilities
|
|
12
|
+
- **Security Integration**: Built-in validation using the security module
|
|
13
|
+
- **Circuit Breaker**: Automatic failure detection and recovery
|
|
14
|
+
- **Resource Management**: Proper cleanup and connection pooling
|
|
15
|
+
|
|
16
|
+
## MCPClient Class
|
|
17
|
+
|
|
18
|
+
The `MCPClient` class handles connections to individual MCP servers and provides methods for tool execution, resource access, and server management.
|
|
19
|
+
|
|
20
|
+
### Initialization
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from massgen.mcp_tools import MCPClient
|
|
24
|
+
|
|
25
|
+
# Basic configuration
|
|
26
|
+
config = {
|
|
27
|
+
"name": "test_server",
|
|
28
|
+
"type": "stdio",
|
|
29
|
+
"command": "python",
|
|
30
|
+
"args": ["-m", "massgen.tests.mcp_test_server"],
|
|
31
|
+
"security": {
|
|
32
|
+
"level": "moderate"
|
|
33
|
+
},
|
|
34
|
+
"timeout": 30,
|
|
35
|
+
"max_retries": 3
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
client = MCPClient(config)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Connection Management
|
|
42
|
+
|
|
43
|
+
#### connect()
|
|
44
|
+
|
|
45
|
+
**What it does**: This function establishes a connection to an MCP server. Think of it like dialing a phone number - it sets up the communication channel so you can start using the server's tools.
|
|
46
|
+
|
|
47
|
+
**Why you need it**: Before you can use any tools from an MCP server, you need to connect to it first. This function handles all the technical details of establishing that connection.
|
|
48
|
+
|
|
49
|
+
**What happens when you call it**:
|
|
50
|
+
|
|
51
|
+
1. Validates your configuration for security
|
|
52
|
+
2. Starts the MCP server process (for stdio) or connects to the web server (for HTTP)
|
|
53
|
+
3. Performs a "handshake" to establish communication
|
|
54
|
+
4. Discovers what tools, resources, and prompts are available
|
|
55
|
+
5. Sets up the connection for use
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
async def connect_example():
|
|
59
|
+
config = {
|
|
60
|
+
"name": "my_server",
|
|
61
|
+
"type": "stdio",
|
|
62
|
+
"command": "python",
|
|
63
|
+
"args": ["-m", "my_mcp_server"]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Method 1: Using async context manager (recommended)
|
|
67
|
+
# The async context manager connects automatically
|
|
68
|
+
async with MCPClient(config) as client:
|
|
69
|
+
print("✅ Connected successfully!")
|
|
70
|
+
|
|
71
|
+
# Use the client here...
|
|
72
|
+
tools = client.get_available_tools()
|
|
73
|
+
print(f"Available tools: {tools}")
|
|
74
|
+
|
|
75
|
+
# Connection automatically closed when exiting the 'with' block
|
|
76
|
+
|
|
77
|
+
# Method 2: Manual connection management
|
|
78
|
+
client = MCPClient(config)
|
|
79
|
+
try:
|
|
80
|
+
await client.connect()
|
|
81
|
+
print("✅ Connected successfully!")
|
|
82
|
+
|
|
83
|
+
# Use the client...
|
|
84
|
+
|
|
85
|
+
finally:
|
|
86
|
+
await client.disconnect() # Always disconnect when done
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### disconnect()
|
|
90
|
+
|
|
91
|
+
**What it does**: Safely closes the connection to the MCP server. Like hanging up a phone call, it properly ends the communication and cleans up resources.
|
|
92
|
+
|
|
93
|
+
**Why you need it**: Always disconnect when you're done to free up system resources and ensure the server process is properly terminated.
|
|
94
|
+
|
|
95
|
+
**What happens when you call it**:
|
|
96
|
+
|
|
97
|
+
1. Signals the server that you're disconnecting
|
|
98
|
+
2. Closes the communication channel
|
|
99
|
+
3. Terminates the server process (for stdio servers)
|
|
100
|
+
4. Cleans up memory and file handles
|
|
101
|
+
5. Resets the client state
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
async def manual_connection():
|
|
105
|
+
client = MCPClient(config)
|
|
106
|
+
try:
|
|
107
|
+
await client.connect()
|
|
108
|
+
print("✅ Connected to server")
|
|
109
|
+
|
|
110
|
+
# Do your work here...
|
|
111
|
+
result = await client.call_tool("some_tool", {})
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"❌ Error occurred: {e}")
|
|
115
|
+
finally:
|
|
116
|
+
# ALWAYS disconnect, even if errors occurred
|
|
117
|
+
await client.disconnect()
|
|
118
|
+
print("✅ Disconnected safely")
|
|
119
|
+
|
|
120
|
+
# ✅ Better approach - use context manager (auto-disconnect)
|
|
121
|
+
async def recommended_approach():
|
|
122
|
+
# The async context manager connects automatically
|
|
123
|
+
async with MCPClient(config) as client:
|
|
124
|
+
# Work with client...
|
|
125
|
+
# Automatic disconnect happens here, even if errors occur
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### reconnect()
|
|
129
|
+
|
|
130
|
+
**What it does**: If the connection to the MCP server is lost or broken, this function tries to reconnect automatically. It's smart about retrying - it waits longer between each attempt to avoid overwhelming a struggling server.
|
|
131
|
+
|
|
132
|
+
**Why you need it**: Network connections can fail, servers can crash, or temporary issues can break the connection. Instead of giving up, this function tries to restore the connection.
|
|
133
|
+
|
|
134
|
+
**Parameters**:
|
|
135
|
+
|
|
136
|
+
- `max_retries`: How many times to try reconnecting (default: 3)
|
|
137
|
+
- `retry_delay`: How long to wait between attempts in seconds (default: 1.0)
|
|
138
|
+
|
|
139
|
+
**Returns**: True if reconnection succeeded, False if all attempts failed
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
async def reconnect_example():
|
|
143
|
+
# The async context manager connects automatically
|
|
144
|
+
async with MCPClient(config) as client:
|
|
145
|
+
print("✅ Initial connection established")
|
|
146
|
+
|
|
147
|
+
# Simulate using the client
|
|
148
|
+
try:
|
|
149
|
+
result = await client.call_tool("some_tool", {})
|
|
150
|
+
print(f"Tool result: {result}")
|
|
151
|
+
|
|
152
|
+
except MCPConnectionError as e:
|
|
153
|
+
print(f"❌ Connection lost: {e}")
|
|
154
|
+
|
|
155
|
+
# Try to reconnect
|
|
156
|
+
print("🔄 Attempting to reconnect...")
|
|
157
|
+
success = await client.reconnect(max_retries=5, retry_delay=2.0)
|
|
158
|
+
|
|
159
|
+
if success:
|
|
160
|
+
print("✅ Reconnected successfully!")
|
|
161
|
+
# Try the tool again
|
|
162
|
+
result = await client.call_tool("some_tool", {})
|
|
163
|
+
print(f"Tool result after reconnect: {result}")
|
|
164
|
+
else:
|
|
165
|
+
print("❌ Failed to reconnect after 5 attempts")
|
|
166
|
+
|
|
167
|
+
# ✅ Automatic reconnection with health checks
|
|
168
|
+
async def robust_client_usage():
|
|
169
|
+
# The async context manager connects automatically
|
|
170
|
+
async with MCPClient(config) as client:
|
|
171
|
+
|
|
172
|
+
# Check if connection is healthy before important operations
|
|
173
|
+
if not await client.health_check():
|
|
174
|
+
print("⚠️ Connection seems unhealthy, attempting reconnect...")
|
|
175
|
+
await client.reconnect()
|
|
176
|
+
|
|
177
|
+
# Now proceed with confidence
|
|
178
|
+
result = await client.call_tool("important_tool", {"data": "value"})
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Tool Operations
|
|
182
|
+
|
|
183
|
+
#### call_tool()
|
|
184
|
+
|
|
185
|
+
**What it does**: This is the main function you'll use to actually run tools on the MCP server. It's like calling a function on a remote computer - you provide the tool name and arguments, and get back the result.
|
|
186
|
+
|
|
187
|
+
**Why you need it**: This is how you actually use the MCP server's capabilities. Whether you want to read files, process data, or perform any other task, you do it through this function.
|
|
188
|
+
|
|
189
|
+
**Parameters**:
|
|
190
|
+
|
|
191
|
+
- `tool_name` (required): The name of the tool you want to run (e.g., "read_file")
|
|
192
|
+
- `arguments` (required): Dictionary of parameters the tool needs (e.g., {"path": "file.txt"})
|
|
193
|
+
|
|
194
|
+
**Returns**: The result from the tool (could be text, data, or complex objects)
|
|
195
|
+
|
|
196
|
+
**What happens when you call it**:
|
|
197
|
+
|
|
198
|
+
1. Validates that the tool exists on the server
|
|
199
|
+
2. Checks that your arguments are safe and valid
|
|
200
|
+
3. Sends the request to the MCP server
|
|
201
|
+
4. Waits for the server to process it
|
|
202
|
+
5. Returns the result or raises an error if something went wrong
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
async def tool_execution():
|
|
206
|
+
# The async context manager connects automatically
|
|
207
|
+
async with MCPClient(config) as client:
|
|
208
|
+
|
|
209
|
+
# ✅ Simple tool call - like calling a function
|
|
210
|
+
result = await client.call_tool("echo", {"message": "Hello World"})
|
|
211
|
+
print(f"Echo result: {result}")
|
|
212
|
+
# Output: "Hello World" (or whatever the echo tool returns)
|
|
213
|
+
|
|
214
|
+
# ✅ Tool call with multiple arguments
|
|
215
|
+
file_result = await client.call_tool("read_file", {
|
|
216
|
+
"path": "/safe/path/document.txt",
|
|
217
|
+
"encoding": "utf-8",
|
|
218
|
+
"max_lines": 100
|
|
219
|
+
})
|
|
220
|
+
print(f"File contents: {file_result}")
|
|
221
|
+
|
|
222
|
+
# ✅ Tool call with complex data
|
|
223
|
+
analysis_result = await client.call_tool("analyze_data", {
|
|
224
|
+
"data": [1, 2, 3, 4, 5],
|
|
225
|
+
"options": {
|
|
226
|
+
"method": "statistical",
|
|
227
|
+
"include_graphs": True
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
print(f"Analysis: {analysis_result}")
|
|
231
|
+
|
|
232
|
+
# ❌ Handle common errors gracefully
|
|
233
|
+
try:
|
|
234
|
+
# Tool doesn't exist
|
|
235
|
+
result = await client.call_tool("nonexistent_tool", {})
|
|
236
|
+
except MCPError as e:
|
|
237
|
+
print(f"❌ Tool not found: {e}")
|
|
238
|
+
# Show available tools to help user
|
|
239
|
+
available = client.get_available_tools()
|
|
240
|
+
print(f"Available tools: {available}")
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
# Wrong arguments
|
|
244
|
+
result = await client.call_tool("read_file", {"wrong_param": "value"})
|
|
245
|
+
except MCPValidationError as e:
|
|
246
|
+
print(f"❌ Invalid arguments: {e}")
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
# Server error
|
|
250
|
+
result = await client.call_tool("buggy_tool", {})
|
|
251
|
+
except MCPServerError as e:
|
|
252
|
+
print(f"❌ Server error: {e}")
|
|
253
|
+
|
|
254
|
+
try:
|
|
255
|
+
# Timeout
|
|
256
|
+
result = await client.call_tool("slow_tool", {"timeout": 1})
|
|
257
|
+
except MCPTimeoutError as e:
|
|
258
|
+
print(f"❌ Tool took too long: {e}")
|
|
259
|
+
|
|
260
|
+
# ✅ Best practices for tool calls
|
|
261
|
+
async def best_practices():
|
|
262
|
+
# The async context manager connects automatically
|
|
263
|
+
async with MCPClient(config) as client:
|
|
264
|
+
|
|
265
|
+
# 1. Always check if tool exists first
|
|
266
|
+
available_tools = client.get_available_tools()
|
|
267
|
+
if "my_tool" not in available_tools:
|
|
268
|
+
print("❌ Tool not available")
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
# 2. Use try-catch for error handling
|
|
272
|
+
try:
|
|
273
|
+
result = await client.call_tool("my_tool", {"param": "value"})
|
|
274
|
+
print(f"✅ Success: {result}")
|
|
275
|
+
except Exception as e:
|
|
276
|
+
print(f"❌ Error: {e}")
|
|
277
|
+
|
|
278
|
+
# 3. Validate your arguments match what the tool expects
|
|
279
|
+
# (Check tool documentation or use get_available_tools() for details)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Tool Discovery
|
|
283
|
+
|
|
284
|
+
**What it does**: Gets detailed information about all the tools available on the MCP server. You can use `client.get_available_tools()` for tool names. For detailed metadata, note that `client.session` is internal and subject to change - use the public API when possible.
|
|
285
|
+
|
|
286
|
+
**Why you need it**: Before you can use tools, you need to know what's available and how to use them. These functions give you that information.
|
|
287
|
+
|
|
288
|
+
**Returns**: List of tool names (get_available_tools) or detailed tool objects (internal session access)
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
async def discover_tools():
|
|
292
|
+
# The async context manager connects automatically
|
|
293
|
+
async with MCPClient(config) as client:
|
|
294
|
+
|
|
295
|
+
# Get detailed information about all tools
|
|
296
|
+
# Note: session access is internal and may change
|
|
297
|
+
tools = await client.session.list_tools()
|
|
298
|
+
|
|
299
|
+
print(f"📋 Found {len(tools.tools)} tools on this server:")
|
|
300
|
+
print()
|
|
301
|
+
|
|
302
|
+
for tool in tools.tools:
|
|
303
|
+
print(f"🔧 Tool: {tool.name}")
|
|
304
|
+
print(f" Description: {tool.description}")
|
|
305
|
+
|
|
306
|
+
# Show what parameters this tool accepts
|
|
307
|
+
if hasattr(tool, 'inputSchema') and tool.inputSchema:
|
|
308
|
+
print(f" Parameters: {tool.inputSchema.get('properties', {})}")
|
|
309
|
+
|
|
310
|
+
print() # Empty line for readability
|
|
311
|
+
|
|
312
|
+
# ✅ Practical example - exploring a file server
|
|
313
|
+
async def explore_file_server():
|
|
314
|
+
# The async context manager connects automatically
|
|
315
|
+
async with MCPClient(config) as client:
|
|
316
|
+
|
|
317
|
+
# Note: session access is internal and may change
|
|
318
|
+
tools = await client.session.list_tools()
|
|
319
|
+
|
|
320
|
+
# Look for file-related tools
|
|
321
|
+
file_tools = [tool for tool in tools.tools if 'file' in tool.name.lower()]
|
|
322
|
+
|
|
323
|
+
print("📁 File-related tools:")
|
|
324
|
+
for tool in file_tools:
|
|
325
|
+
print(f" • {tool.name}: {tool.description}")
|
|
326
|
+
|
|
327
|
+
# Show how to use a specific tool
|
|
328
|
+
if file_tools:
|
|
329
|
+
example_tool = file_tools[0]
|
|
330
|
+
print(f"\n📖 How to use '{example_tool.name}':")
|
|
331
|
+
print(f" await client.call_tool('{example_tool.name}', {{...}})")
|
|
332
|
+
|
|
333
|
+
if hasattr(example_tool, 'inputSchema') and example_tool.inputSchema:
|
|
334
|
+
print(f" Required parameters: {example_tool.inputSchema.get('properties', {})}")
|
|
335
|
+
|
|
336
|
+
# ✅ Simple way to just get tool names
|
|
337
|
+
async def get_tool_names():
|
|
338
|
+
# The async context manager connects automatically
|
|
339
|
+
async with MCPClient(config) as client:
|
|
340
|
+
# Quick way to get just the names
|
|
341
|
+
tool_names = client.get_available_tools()
|
|
342
|
+
print(f"Available tools: {tool_names}")
|
|
343
|
+
|
|
344
|
+
# Check if a specific tool exists
|
|
345
|
+
if "read_file" in tool_names:
|
|
346
|
+
print("✅ File reading is available")
|
|
347
|
+
else:
|
|
348
|
+
print("❌ No file reading capability")
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Resource Operations
|
|
352
|
+
|
|
353
|
+
#### get_resource()
|
|
354
|
+
|
|
355
|
+
**What it does**: Retrieves data or files from the MCP server using a URI (address). Resources are like files or data sources that the server makes available - they could be configuration files, databases, web content, or any other data.
|
|
356
|
+
|
|
357
|
+
**Why you need it**: Some MCP servers provide access to data sources beyond just tools. For example, a server might provide access to configuration files, documentation, or cached data.
|
|
358
|
+
|
|
359
|
+
**Parameters**:
|
|
360
|
+
|
|
361
|
+
- `uri` (required): The resource address (e.g., "file:///config.json", "http://api/data")
|
|
362
|
+
|
|
363
|
+
**Returns**: Resource content (format depends on the resource type)
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
from massgen.mcp_tools.exceptions import MCPServerError
|
|
367
|
+
# Optionally import mcp_types for type hints
|
|
368
|
+
# from mcp import types as mcp_types
|
|
369
|
+
|
|
370
|
+
async def resource_access():
|
|
371
|
+
# The async context manager connects automatically
|
|
372
|
+
async with MCPClient(config) as client:
|
|
373
|
+
# ✅ Get a configuration file
|
|
374
|
+
try:
|
|
375
|
+
config_resource = await client.get_resource("file:///app/config.json")
|
|
376
|
+
# Returns ReadResourceResult with contents
|
|
377
|
+
print(f"Config content: {config_resource.contents}")
|
|
378
|
+
except MCPServerError as e:
|
|
379
|
+
print(f"❌ Could not load config: {e}")
|
|
380
|
+
|
|
381
|
+
# ✅ Get web-based data
|
|
382
|
+
try:
|
|
383
|
+
api_data = await client.get_resource("http://internal-api/status")
|
|
384
|
+
print(f"API status: {api_data.contents}")
|
|
385
|
+
except MCPServerError as e:
|
|
386
|
+
print(f"❌ API not accessible: {e}")
|
|
387
|
+
|
|
388
|
+
# ✅ List all available resources first
|
|
389
|
+
available_resources = client.get_available_resources()
|
|
390
|
+
print(f"📋 Available resources:")
|
|
391
|
+
for uri in available_resources:
|
|
392
|
+
print(f" • {uri}")
|
|
393
|
+
|
|
394
|
+
# ✅ Access each available resource
|
|
395
|
+
for uri in available_resources:
|
|
396
|
+
try:
|
|
397
|
+
resource = await client.get_resource(uri)
|
|
398
|
+
print(f"✅ {uri}: {len(str(resource.contents))} characters")
|
|
399
|
+
except Exception as e:
|
|
400
|
+
print(f"❌ {uri}: Failed to load - {e}")
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### get_prompt()
|
|
404
|
+
|
|
405
|
+
**What it does**: Gets a pre-written text template from the server and fills it in with your data. Prompts are like form letters - they have placeholders that get replaced with your specific information.
|
|
406
|
+
|
|
407
|
+
**Why you need it**: Many MCP servers provide useful prompt templates for common tasks like code reviews, text summaries, or translations. Instead of writing these prompts yourself, you can use the server's templates.
|
|
408
|
+
|
|
409
|
+
**Parameters**:
|
|
410
|
+
|
|
411
|
+
- `name` (required): The name of the prompt template (e.g., "code_review")
|
|
412
|
+
- `arguments` (optional): Dictionary of values to fill in the template
|
|
413
|
+
|
|
414
|
+
**Returns**: Prompt object with the generated text
|
|
415
|
+
|
|
416
|
+
```python
|
|
417
|
+
from massgen.mcp_tools.exceptions import MCPServerError
|
|
418
|
+
# Optionally import mcp_types for type hints
|
|
419
|
+
# from mcp import types as mcp_types
|
|
420
|
+
|
|
421
|
+
async def prompt_usage():
|
|
422
|
+
# The async context manager connects automatically
|
|
423
|
+
async with MCPClient(config) as client:
|
|
424
|
+
# ✅ Use a code review prompt
|
|
425
|
+
try:
|
|
426
|
+
review_prompt = await client.get_prompt("code_review", {
|
|
427
|
+
"language": "python",
|
|
428
|
+
"code": "def hello():\n print('world')",
|
|
429
|
+
"focus": "best_practices"
|
|
430
|
+
})
|
|
431
|
+
# Returns GetPromptResult with messages
|
|
432
|
+
print(f"📝 Generated review prompt:")
|
|
433
|
+
print(review_prompt.messages)
|
|
434
|
+
except MCPServerError as e:
|
|
435
|
+
print(f"❌ Prompt not available: {e}")
|
|
436
|
+
|
|
437
|
+
# ✅ Use a translation prompt
|
|
438
|
+
try:
|
|
439
|
+
translate_prompt = await client.get_prompt("translate", {
|
|
440
|
+
"text": "Hello, how are you?",
|
|
441
|
+
"from_language": "english",
|
|
442
|
+
"to_language": "spanish"
|
|
443
|
+
})
|
|
444
|
+
print(f"🌍 Translation prompt: {translate_prompt.messages}")
|
|
445
|
+
except MCPServerError as e:
|
|
446
|
+
print(f"❌ Translation prompt failed: {e}")
|
|
447
|
+
|
|
448
|
+
# ✅ List available prompts first
|
|
449
|
+
available_prompts = client.get_available_prompts()
|
|
450
|
+
print(f"📋 Available prompts: {available_prompts}")
|
|
451
|
+
|
|
452
|
+
# ✅ Use prompts dynamically
|
|
453
|
+
for prompt_name in available_prompts:
|
|
454
|
+
try:
|
|
455
|
+
# Try with minimal arguments
|
|
456
|
+
prompt = await client.get_prompt(prompt_name, {})
|
|
457
|
+
print(f"✅ {prompt_name}: Template loaded")
|
|
458
|
+
except Exception as e:
|
|
459
|
+
print(f"❌ {prompt_name}: Needs specific arguments - {e}")
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Health and Monitoring
|
|
463
|
+
|
|
464
|
+
#### health_check()
|
|
465
|
+
|
|
466
|
+
**What it does**: Tests if the connection to the MCP server is still working properly. It's like pinging a server to see if it's still alive and responding.
|
|
467
|
+
|
|
468
|
+
**Why you need it**: Network connections can become unstable, servers can become overloaded, or processes can crash. This function helps you detect these problems before they cause your tools to fail.
|
|
469
|
+
|
|
470
|
+
**Returns**: True if the server is healthy and responding, False if there are problems
|
|
471
|
+
|
|
472
|
+
**What it checks**:
|
|
473
|
+
|
|
474
|
+
1. Is the connection still active?
|
|
475
|
+
2. Can the server respond to basic requests?
|
|
476
|
+
3. Are the server's core functions working?
|
|
477
|
+
|
|
478
|
+
```python
|
|
479
|
+
async def monitor_health():
|
|
480
|
+
# The async context manager connects automatically
|
|
481
|
+
async with MCPClient(config) as client:
|
|
482
|
+
# ✅ Basic health check
|
|
483
|
+
is_healthy = await client.health_check()
|
|
484
|
+
if is_healthy:
|
|
485
|
+
print("✅ Server is healthy and responding")
|
|
486
|
+
else:
|
|
487
|
+
print("❌ Server health check failed")
|
|
488
|
+
|
|
489
|
+
# ✅ Use health check before important operations
|
|
490
|
+
if await client.health_check():
|
|
491
|
+
# Safe to proceed
|
|
492
|
+
result = await client.call_tool("important_tool", {"data": "value"})
|
|
493
|
+
else:
|
|
494
|
+
print("⚠️ Server unhealthy, attempting reconnect...")
|
|
495
|
+
success = await client.reconnect()
|
|
496
|
+
if success:
|
|
497
|
+
result = await client.call_tool("important_tool", {"data": "value"})
|
|
498
|
+
else:
|
|
499
|
+
print("❌ Could not restore connection")
|
|
500
|
+
|
|
501
|
+
# ✅ Continuous health monitoring
|
|
502
|
+
async def continuous_monitoring():
|
|
503
|
+
# The async context manager connects automatically
|
|
504
|
+
async with MCPClient(config) as client:
|
|
505
|
+
import asyncio
|
|
506
|
+
|
|
507
|
+
# Check health every 30 seconds
|
|
508
|
+
while True:
|
|
509
|
+
try:
|
|
510
|
+
if await client.health_check():
|
|
511
|
+
print("✅ Health check passed")
|
|
512
|
+
else:
|
|
513
|
+
print("⚠️ Health check failed, reconnecting...")
|
|
514
|
+
await client.reconnect()
|
|
515
|
+
|
|
516
|
+
# Wait 30 seconds before next check
|
|
517
|
+
await asyncio.sleep(30)
|
|
518
|
+
|
|
519
|
+
except KeyboardInterrupt:
|
|
520
|
+
print("Stopping health monitoring")
|
|
521
|
+
break
|
|
522
|
+
except Exception as e:
|
|
523
|
+
print(f"❌ Health monitoring error: {e}")
|
|
524
|
+
await asyncio.sleep(5) # Wait before retrying
|
|
525
|
+
|
|
526
|
+
# ✅ Health check with timeout
|
|
527
|
+
async def quick_health_check():
|
|
528
|
+
# The async context manager connects automatically
|
|
529
|
+
async with MCPClient(config) as client:
|
|
530
|
+
try:
|
|
531
|
+
# Set a short timeout for health check
|
|
532
|
+
health_task = asyncio.create_task(client.health_check())
|
|
533
|
+
is_healthy = await asyncio.wait_for(health_task, timeout=5.0)
|
|
534
|
+
|
|
535
|
+
if is_healthy:
|
|
536
|
+
print("✅ Server responded quickly")
|
|
537
|
+
else:
|
|
538
|
+
print("❌ Server is slow or unresponsive")
|
|
539
|
+
|
|
540
|
+
except asyncio.TimeoutError:
|
|
541
|
+
print("❌ Health check timed out - server is very slow")
|
|
542
|
+
except Exception as e:
|
|
543
|
+
print(f"❌ Health check error: {e}")
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Utility Functions
|
|
547
|
+
|
|
548
|
+
#### get_available_tools()
|
|
549
|
+
|
|
550
|
+
**What it does**: Returns a simple list of tool names available on the server. This is a quick way to see what tools you can use without getting detailed information.
|
|
551
|
+
|
|
552
|
+
**Returns**: List of strings (tool names)
|
|
553
|
+
|
|
554
|
+
```python
|
|
555
|
+
# The async context manager connects automatically
|
|
556
|
+
async with MCPClient(config) as client:
|
|
557
|
+
# Get just the tool names
|
|
558
|
+
tools = client.get_available_tools()
|
|
559
|
+
print(f"Available tools: {tools}")
|
|
560
|
+
# Output: ['read_file', 'write_file', 'list_directory', 'search_text']
|
|
561
|
+
|
|
562
|
+
# Check if a specific tool exists
|
|
563
|
+
if 'read_file' in tools:
|
|
564
|
+
print("✅ Can read files")
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
#### get_available_resources()
|
|
568
|
+
|
|
569
|
+
**What it does**: Returns a list of resource URIs (addresses) that you can access on the server. Resources are like files or data sources that the server can provide.
|
|
570
|
+
|
|
571
|
+
**Returns**: List of strings (resource URIs)
|
|
572
|
+
|
|
573
|
+
```python
|
|
574
|
+
# The async context manager connects automatically
|
|
575
|
+
async with MCPClient(config) as client:
|
|
576
|
+
# Get available resources
|
|
577
|
+
resources = client.get_available_resources()
|
|
578
|
+
print(f"Available resources: {resources}")
|
|
579
|
+
# Output: ['file:///config.json', 'http://api/data', 'memory://cache']
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### get_available_prompts()
|
|
583
|
+
|
|
584
|
+
**What it does**: Returns a list of prompt templates available on the server. Prompts are pre-written text templates that can be customized with your data.
|
|
585
|
+
|
|
586
|
+
**Returns**: List of strings (prompt names)
|
|
587
|
+
|
|
588
|
+
```python
|
|
589
|
+
# The async context manager connects automatically
|
|
590
|
+
async with MCPClient(config) as client:
|
|
591
|
+
# Get available prompts
|
|
592
|
+
prompts = client.get_available_prompts()
|
|
593
|
+
print(f"Available prompts: {prompts}")
|
|
594
|
+
# Output: ['code_review', 'summarize_text', 'translate']
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
#### is_connected()
|
|
598
|
+
|
|
599
|
+
**What it does**: Checks if the client is currently connected to the MCP server. This is useful for verifying the connection status before trying to use tools.
|
|
600
|
+
|
|
601
|
+
**Returns**: True if connected, False if not connected
|
|
602
|
+
|
|
603
|
+
```python
|
|
604
|
+
# The async context manager connects automatically
|
|
605
|
+
async with MCPClient(config) as client:
|
|
606
|
+
# After automatic connection
|
|
607
|
+
print(f"Connected: {client.is_connected()}") # True
|
|
608
|
+
|
|
609
|
+
# Use in conditional logic
|
|
610
|
+
if client.is_connected():
|
|
611
|
+
result = await client.call_tool("some_tool", {})
|
|
612
|
+
else:
|
|
613
|
+
print("❌ Not connected - cannot use tools")
|
|
614
|
+
await client.connect()
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
## MultiMCPClient Class
|
|
618
|
+
|
|
619
|
+
The `MultiMCPClient` class manages connections to multiple MCP servers simultaneously, providing unified access to tools and resources across all connected servers.
|
|
620
|
+
|
|
621
|
+
### Initialization and Connection
|
|
622
|
+
|
|
623
|
+
#### MultiMCPClient()
|
|
624
|
+
|
|
625
|
+
Creates a MultiMCPClient instance and connects to multiple MCP servers based on configuration.
|
|
626
|
+
|
|
627
|
+
```python
|
|
628
|
+
from massgen.mcp_tools import MultiMCPClient
|
|
629
|
+
|
|
630
|
+
async def multi_server_setup():
|
|
631
|
+
# Configuration for multiple servers - use list format
|
|
632
|
+
servers_config = [
|
|
633
|
+
{
|
|
634
|
+
"name": "file_server",
|
|
635
|
+
"type": "stdio",
|
|
636
|
+
"command": "python",
|
|
637
|
+
"args": ["-m", "file_mcp_server"],
|
|
638
|
+
"security": {
|
|
639
|
+
"level": "strict"
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
{
|
|
643
|
+
"name": "web_server",
|
|
644
|
+
"type": "streamable-http",
|
|
645
|
+
"url": "http://localhost:8000/mcp",
|
|
646
|
+
"security": {
|
|
647
|
+
"level": "moderate"
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
]
|
|
651
|
+
|
|
652
|
+
# Create and connect to all servers
|
|
653
|
+
multi_client = MultiMCPClient(servers_config)
|
|
654
|
+
await multi_client.connect()
|
|
655
|
+
|
|
656
|
+
# Alternative: Transform dict format using validator
|
|
657
|
+
# from massgen.mcp_tools import MCPConfigValidator
|
|
658
|
+
# dict_config = {"server1": {...}, "server2": {...}}
|
|
659
|
+
# validated_config = MCPConfigValidator.validate_backend_mcp_config(dict_config)
|
|
660
|
+
# multi_client = MultiMCPClient(validated_config)
|
|
661
|
+
# await multi_client.connect()
|
|
662
|
+
|
|
663
|
+
try:
|
|
664
|
+
# Use the multi-client
|
|
665
|
+
tools = list(multi_client.tools.keys())
|
|
666
|
+
print(f"Total tools available: {len(tools)}")
|
|
667
|
+
finally:
|
|
668
|
+
await multi_client.disconnect()
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### Tool Routing
|
|
672
|
+
|
|
673
|
+
Tools in multi-server setups are prefixed with the server name for disambiguation.
|
|
674
|
+
|
|
675
|
+
```python
|
|
676
|
+
async def multi_server_tools():
|
|
677
|
+
multi_client = MultiMCPClient(servers_config)
|
|
678
|
+
await multi_client.connect()
|
|
679
|
+
|
|
680
|
+
try:
|
|
681
|
+
# Call tool from specific server
|
|
682
|
+
file_result = await multi_client.call_tool("mcp__file_server__read_file", {
|
|
683
|
+
"path": "example.txt"
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
web_result = await multi_client.call_tool("mcp__web_server__fetch_url", {
|
|
687
|
+
"url": "https://api.example.com/data"
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
# List tools by server - filter by server prefix
|
|
691
|
+
file_tools = [t for t in multi_client.tools if t.startswith("mcp__file_server__")]
|
|
692
|
+
web_tools = [t for t in multi_client.tools if t.startswith("mcp__web_server__")]
|
|
693
|
+
|
|
694
|
+
finally:
|
|
695
|
+
await multi_client.disconnect()
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
### Circuit Breaker Integration
|
|
699
|
+
|
|
700
|
+
The multi-client includes circuit breaker functionality for handling server failures.
|
|
701
|
+
|
|
702
|
+
```python
|
|
703
|
+
async def circuit_breaker_example():
|
|
704
|
+
# Configuration with circuit breaker settings
|
|
705
|
+
config_with_cb = [
|
|
706
|
+
{
|
|
707
|
+
"name": "file_server",
|
|
708
|
+
"type": "stdio",
|
|
709
|
+
"command": "python",
|
|
710
|
+
"args": ["-m", "unreliable_server"],
|
|
711
|
+
"circuit_breaker": {
|
|
712
|
+
"failure_threshold": 5,
|
|
713
|
+
"recovery_timeout": 60,
|
|
714
|
+
"expected_exception": "MCPConnectionError"
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
]
|
|
718
|
+
|
|
719
|
+
multi_client = MultiMCPClient(config_with_cb)
|
|
720
|
+
await multi_client.connect()
|
|
721
|
+
|
|
722
|
+
try:
|
|
723
|
+
# Circuit breaker will handle failures automatically
|
|
724
|
+
for i in range(10):
|
|
725
|
+
try:
|
|
726
|
+
result = await multi_client.call_tool("mcp__file_server__unstable_tool", {})
|
|
727
|
+
print(f"Success: {result}")
|
|
728
|
+
except Exception as e:
|
|
729
|
+
print(f"Handled by circuit breaker: {e}")
|
|
730
|
+
|
|
731
|
+
finally:
|
|
732
|
+
await multi_client.disconnect()
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## Configuration Integration
|
|
736
|
+
|
|
737
|
+
### Config Validator Integration
|
|
738
|
+
|
|
739
|
+
The client integrates with the config validator for comprehensive validation.
|
|
740
|
+
|
|
741
|
+
```python
|
|
742
|
+
from massgen.mcp_tools import MCPConfigValidator, validate_mcp_integration
|
|
743
|
+
|
|
744
|
+
async def validated_client():
|
|
745
|
+
# Raw configuration
|
|
746
|
+
raw_config = {
|
|
747
|
+
"type": "streamable-http",
|
|
748
|
+
"url": "http://localhost:8000/mcp",
|
|
749
|
+
"timeout": "invalid_timeout" # This will be caught
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
try:
|
|
753
|
+
# Validate configuration before creating client
|
|
754
|
+
validated_config = MCPConfigValidator.validate_server_config(raw_config)
|
|
755
|
+
client = MCPClient(validated_config)
|
|
756
|
+
await client.connect()
|
|
757
|
+
except MCPConfigurationError as e:
|
|
758
|
+
print(f"Configuration error: {e}")
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### Timeout Configuration
|
|
762
|
+
|
|
763
|
+
Configure timeouts for different operations:
|
|
764
|
+
|
|
765
|
+
```python
|
|
766
|
+
config_with_timeouts = {
|
|
767
|
+
"name": "slow_server",
|
|
768
|
+
"type": "stdio",
|
|
769
|
+
"command": "python",
|
|
770
|
+
"args": ["-m", "slow_server"],
|
|
771
|
+
"timeout": 60, # General timeout
|
|
772
|
+
"http_read_timeout": 300 # HTTP read timeout for streamable-http transport
|
|
773
|
+
}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
## Practical Examples
|
|
777
|
+
|
|
778
|
+
### Example 1: File Processing Server
|
|
779
|
+
|
|
780
|
+
```python
|
|
781
|
+
# Based on massgen/configs/gemini_mcp_example.yaml
|
|
782
|
+
async def file_processing_example():
|
|
783
|
+
config = {
|
|
784
|
+
"name": "file_processor",
|
|
785
|
+
"type": "stdio",
|
|
786
|
+
"command": "python",
|
|
787
|
+
"args": ["-m", "file_processor_server"],
|
|
788
|
+
"security": {
|
|
789
|
+
"level": "strict"
|
|
790
|
+
},
|
|
791
|
+
"timeout": 30
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
# The async context manager connects automatically
|
|
795
|
+
async with MCPClient(config) as client:
|
|
796
|
+
# Process multiple files
|
|
797
|
+
files = ["doc1.txt", "doc2.txt", "doc3.txt"]
|
|
798
|
+
results = []
|
|
799
|
+
|
|
800
|
+
for file_path in files:
|
|
801
|
+
try:
|
|
802
|
+
result = await client.call_tool("process_file", {
|
|
803
|
+
"path": file_path,
|
|
804
|
+
"operation": "analyze"
|
|
805
|
+
})
|
|
806
|
+
results.append(result)
|
|
807
|
+
except MCPServerError as e:
|
|
808
|
+
print(f"Failed to process {file_path}: {e}")
|
|
809
|
+
|
|
810
|
+
return results
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### Example 2: Multi-Server Coordination
|
|
814
|
+
|
|
815
|
+
```python
|
|
816
|
+
# Based on massgen/configs/claude_code_discord_mcp_example.yaml
|
|
817
|
+
async def multi_server_coordination():
|
|
818
|
+
servers_config = [
|
|
819
|
+
{
|
|
820
|
+
"name": "code_analyzer",
|
|
821
|
+
"type": "stdio",
|
|
822
|
+
"command": "python",
|
|
823
|
+
"args": ["-m", "code_analysis_server"],
|
|
824
|
+
"security": {
|
|
825
|
+
"level": "moderate"
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"name": "discord_bot",
|
|
830
|
+
"type": "streamable-http",
|
|
831
|
+
"url": "http://localhost:8001/mcp",
|
|
832
|
+
"headers": {"Authorization": "Bearer discord-token"},
|
|
833
|
+
"security": {
|
|
834
|
+
"level": "strict"
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
]
|
|
838
|
+
|
|
839
|
+
multi_client = MultiMCPClient(servers_config)
|
|
840
|
+
await multi_client.connect()
|
|
841
|
+
|
|
842
|
+
try:
|
|
843
|
+
# Analyze code
|
|
844
|
+
analysis = await multi_client.call_tool("mcp__code_analyzer__analyze_python", {
|
|
845
|
+
"file_path": "main.py"
|
|
846
|
+
})
|
|
847
|
+
|
|
848
|
+
# Send results to Discord
|
|
849
|
+
await multi_client.call_tool("mcp__discord_bot__send_message", {
|
|
850
|
+
"channel": "code-reviews",
|
|
851
|
+
"content": f"Analysis complete: {analysis['summary']}"
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
finally:
|
|
855
|
+
await multi_client.disconnect()
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
## Best Practices
|
|
859
|
+
|
|
860
|
+
### Error Handling
|
|
861
|
+
|
|
862
|
+
Always use proper exception handling with specific exception types:
|
|
863
|
+
|
|
864
|
+
```python
|
|
865
|
+
from massgen.mcp_tools.exceptions import (
|
|
866
|
+
MCPConnectionError, MCPServerError, MCPConfigurationError,
|
|
867
|
+
MCPTimeoutError, MCPValidationError
|
|
868
|
+
)
|
|
869
|
+
|
|
870
|
+
async def robust_client_usage():
|
|
871
|
+
# The async context manager connects automatically
|
|
872
|
+
async with MCPClient(config) as client:
|
|
873
|
+
try:
|
|
874
|
+
result = await client.call_tool("example_tool", args)
|
|
875
|
+
|
|
876
|
+
except MCPConnectionError:
|
|
877
|
+
# Handle connection issues
|
|
878
|
+
await client.reconnect()
|
|
879
|
+
|
|
880
|
+
except MCPServerError as e:
|
|
881
|
+
# Handle tool-specific errors
|
|
882
|
+
print(f"Tool failed: {e}")
|
|
883
|
+
|
|
884
|
+
except MCPConfigurationError as e:
|
|
885
|
+
# Handle configuration validation failures
|
|
886
|
+
print(f"Configuration error: {e}")
|
|
887
|
+
|
|
888
|
+
except MCPTimeoutError:
|
|
889
|
+
# Handle timeouts
|
|
890
|
+
print("Operation timed out")
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
### Connection Pooling
|
|
894
|
+
|
|
895
|
+
For high-throughput applications, consider connection pooling:
|
|
896
|
+
|
|
897
|
+
```python
|
|
898
|
+
class MCPConnectionPool:
|
|
899
|
+
def __init__(self, config, pool_size=5):
|
|
900
|
+
self.config = config
|
|
901
|
+
self.pool_size = pool_size
|
|
902
|
+
self.connections = []
|
|
903
|
+
|
|
904
|
+
async def get_connection(self):
|
|
905
|
+
if self.connections:
|
|
906
|
+
return self.connections.pop()
|
|
907
|
+
return MCPClient(self.config)
|
|
908
|
+
|
|
909
|
+
async def return_connection(self, client):
|
|
910
|
+
if len(self.connections) < self.pool_size:
|
|
911
|
+
self.connections.append(client)
|
|
912
|
+
else:
|
|
913
|
+
await client.disconnect()
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
### Resource Cleanup
|
|
917
|
+
|
|
918
|
+
Always ensure proper resource cleanup:
|
|
919
|
+
|
|
920
|
+
```python
|
|
921
|
+
async def proper_cleanup():
|
|
922
|
+
client = None
|
|
923
|
+
try:
|
|
924
|
+
client = MCPClient(config)
|
|
925
|
+
await client.connect()
|
|
926
|
+
# ... operations
|
|
927
|
+
finally:
|
|
928
|
+
if client:
|
|
929
|
+
await client.disconnect()
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
## Integration with MassGen
|
|
933
|
+
|
|
934
|
+
The MCP client integrates seamlessly with MassGen's orchestration system:
|
|
935
|
+
|
|
936
|
+
```python
|
|
937
|
+
from massgen.orchestrator import MassGenOrchestrator
|
|
938
|
+
|
|
939
|
+
async def massgen_integration():
|
|
940
|
+
# MassGen automatically manages MCP clients based on configuration
|
|
941
|
+
orchestrator = MassGenOrchestrator(config_path="config.yaml")
|
|
942
|
+
|
|
943
|
+
# MCP tools are available through the orchestrator
|
|
944
|
+
result = await orchestrator.execute_task({
|
|
945
|
+
"task": "analyze_code",
|
|
946
|
+
"mcp_tools": ["mcp__code_analyzer__lint", "mcp__code_analyzer__security_scan"]
|
|
947
|
+
})
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
This integration allows MassGen to automatically discover and use MCP tools as part of its task execution pipeline.
|