Skip to content

Commit e59be8f

Browse files
Merge pull request #1096 from majiayu000/fix/issue-1091-mcp-optional-import
fix: make MCP package import optional to prevent errors when not installed
2 parents b52708f + 98c5e8f commit e59be8f

File tree

4 files changed

+127
-9
lines changed

4 files changed

+127
-9
lines changed

src/praisonai-agents/praisonaiagents/mcp/mcp.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@
1111
from typing import Any, List, Optional, Callable, Iterable, Union
1212
from functools import wraps, partial
1313

14-
from mcp import ClientSession, StdioServerParameters
15-
from mcp.client.stdio import stdio_client
14+
try:
15+
from mcp import ClientSession, StdioServerParameters
16+
from mcp.client.stdio import stdio_client
17+
MCP_AVAILABLE = True
18+
except ImportError:
19+
MCP_AVAILABLE = False
20+
ClientSession = None
21+
StdioServerParameters = None
22+
stdio_client = None
1623

1724
class MCPToolRunner(threading.Thread):
1825
"""A dedicated thread for running MCP operations."""
@@ -196,10 +203,17 @@ def __init__(self, command_or_string=None, args=None, *, command=None, timeout=6
196203
debug: Enable debug logging for MCP operations (default: False)
197204
**kwargs: Additional parameters for StdioServerParameters
198205
"""
206+
# Check if MCP is available
207+
if not MCP_AVAILABLE:
208+
raise ImportError(
209+
"MCP (Model Context Protocol) package is not installed. "
210+
"Install it with: pip install praisonaiagents[mcp]"
211+
)
212+
199213
# Handle backward compatibility with named parameter 'command'
200214
if command_or_string is None and command is not None:
201215
command_or_string = command
202-
216+
203217
# Set up logging - default to WARNING level to hide INFO messages
204218
if debug:
205219
logging.getLogger("mcp-wrapper").setLevel(logging.DEBUG)

src/praisonai-agents/praisonaiagents/mcp/mcp_http_stream.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@
1616
from typing import List, Dict, Any, Optional, Callable, Iterable, Union
1717
from urllib.parse import urlparse, urljoin
1818

19-
from mcp import ClientSession
19+
try:
20+
from mcp import ClientSession
21+
MCP_AVAILABLE = True
22+
except ImportError:
23+
MCP_AVAILABLE = False
24+
ClientSession = None
25+
2026
try:
2127
import aiohttp
2228
except ImportError:
23-
raise ImportError("aiohttp is required for HTTP Stream transport. Install with: pip install praisonaiagents[mcp]")
29+
aiohttp = None
2430

2531
logger = logging.getLogger("mcp-http-stream")
2632

@@ -507,9 +513,23 @@ def __init__(self, server_url: str, debug: bool = False, timeout: int = 60, opti
507513
timeout: Timeout in seconds for operations (default: 60)
508514
options: Additional configuration options for the transport
509515
"""
516+
# Check if MCP is available
517+
if not MCP_AVAILABLE:
518+
raise ImportError(
519+
"MCP (Model Context Protocol) package is not installed. "
520+
"Install it with: pip install praisonaiagents[mcp]"
521+
)
522+
523+
# Check if aiohttp is available
524+
if aiohttp is None:
525+
raise ImportError(
526+
"aiohttp is required for HTTP Stream transport. "
527+
"Install it with: pip install praisonaiagents[mcp]"
528+
)
529+
510530
# Parse URL to extract base URL and endpoint
511531
parsed = urlparse(server_url)
512-
532+
513533
# If the URL already has a path, use it; otherwise use default /mcp endpoint
514534
if parsed.path and parsed.path != '/':
515535
self.base_url = server_url

src/praisonai-agents/praisonaiagents/mcp/mcp_sse.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@
1111
import json
1212
from typing import List, Dict, Any, Optional, Callable, Iterable
1313

14-
from mcp import ClientSession
15-
from mcp.client.sse import sse_client
14+
try:
15+
from mcp import ClientSession
16+
from mcp.client.sse import sse_client
17+
MCP_AVAILABLE = True
18+
except ImportError:
19+
MCP_AVAILABLE = False
20+
ClientSession = None
21+
sse_client = None
1622

1723
logger = logging.getLogger("mcp-sse")
1824

@@ -169,12 +175,19 @@ def __init__(self, server_url: str, debug: bool = False, timeout: int = 60):
169175
debug: Whether to enable debug logging
170176
timeout: Timeout in seconds for operations (default: 60)
171177
"""
178+
# Check if MCP is available
179+
if not MCP_AVAILABLE:
180+
raise ImportError(
181+
"MCP (Model Context Protocol) package is not installed. "
182+
"Install it with: pip install praisonaiagents[mcp]"
183+
)
184+
172185
self.server_url = server_url
173186
self.debug = debug
174187
self.timeout = timeout
175188
self.session = None
176189
self.tools = []
177-
190+
178191
# Set up logging
179192
if debug:
180193
logger.setLevel(logging.DEBUG)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
Test that MCP module imports are optional and don't break basic functionality.
3+
This test verifies the fix for issue #1091.
4+
"""
5+
import sys
6+
import pytest
7+
8+
9+
class TestMCPOptionalImport:
10+
"""Test that MCP is properly optional."""
11+
12+
def test_praisonaiagents_import_without_mcp(self):
13+
"""Test that praisonaiagents can be imported without mcp installed."""
14+
# This should not raise any ImportError
15+
import praisonaiagents
16+
assert praisonaiagents is not None
17+
18+
def test_agent_import_without_mcp(self):
19+
"""Test that Agent can be imported without mcp installed."""
20+
from praisonaiagents import Agent
21+
assert Agent is not None
22+
23+
def test_mcp_lazy_loading(self):
24+
"""Test that MCP is lazily loaded."""
25+
from praisonaiagents import MCP
26+
# MCP should either be available or None (not raise ImportError on access)
27+
# The actual ImportError should only be raised when trying to instantiate
28+
# if mcp package is not installed
29+
30+
def test_mcp_module_import_no_crash(self):
31+
"""Test that mcp module can be imported without crashing."""
32+
# This tests that the try/except in mcp.py works correctly
33+
try:
34+
from praisonaiagents.mcp import mcp as mcp_module
35+
assert hasattr(mcp_module, 'MCP_AVAILABLE')
36+
except ImportError:
37+
# If import fails, it should be because of missing mcp,
38+
# not because of a syntax error
39+
pass
40+
41+
def test_mcp_sse_module_import_no_crash(self):
42+
"""Test that mcp_sse module can be imported without crashing."""
43+
try:
44+
from praisonaiagents.mcp import mcp_sse
45+
assert hasattr(mcp_sse, 'MCP_AVAILABLE')
46+
except ImportError:
47+
pass
48+
49+
def test_mcp_http_stream_module_import_no_crash(self):
50+
"""Test that mcp_http_stream module can be imported without crashing."""
51+
try:
52+
from praisonaiagents.mcp import mcp_http_stream
53+
assert hasattr(mcp_http_stream, 'MCP_AVAILABLE')
54+
except ImportError:
55+
pass
56+
57+
def test_basic_agent_creation_without_mcp_tools(self):
58+
"""Test that agents can be created without MCP tools."""
59+
from praisonaiagents import Agent
60+
61+
# Creating an agent without MCP tools should work
62+
agent = Agent(
63+
name="Test Agent",
64+
instructions="You are a test agent.",
65+
llm="gpt-4o-mini"
66+
)
67+
assert agent is not None
68+
69+
70+
if __name__ == "__main__":
71+
pytest.main([__file__, "-v"])

0 commit comments

Comments
 (0)