Skip to content

Commit c0ad3b1

Browse files
authored
Optional init (#26)
* solve test * remove legacy functions
1 parent bfe2783 commit c0ad3b1

20 files changed

+532
-443
lines changed
File renamed without changes.

examples/without_config/test.py

Lines changed: 0 additions & 34 deletions
This file was deleted.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "mcphub"
7-
version = "0.1.7"
7+
version = "0.1.8"
88
description = "A Python package for managing and integrating Model Context Protocol (MCP) servers with AI frameworks like OpenAI Agents, LangChain, and Autogen"
99
readme = "README.md"
1010
authors = [

src/mcphub/adapters/autogen.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
try:
2+
from typing import List
3+
4+
from autogen_ext.tools.mcp import StdioMcpToolAdapter
5+
6+
from .base import MCPBaseAdapter
7+
8+
class MCPAutogenAdapter(MCPBaseAdapter):
9+
async def create_adapters(self, mcp_name: str) -> List[StdioMcpToolAdapter]:
10+
server_params = self.get_server_params(mcp_name)
11+
async with self.create_session(mcp_name) as session:
12+
tools = await session.list_tools()
13+
return [
14+
await StdioMcpToolAdapter.from_server_params(server_params, tool.name)
15+
for tool in tools.tools
16+
]
17+
18+
except ImportError:
19+
class MCPAutogenAdapter: # type: ignore
20+
def __init__(self, *args, **kwargs):
21+
raise ImportError("Autogen dependencies not found. Install with: pip install mcphub[autogen]")

src/mcphub/adapters/base.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from abc import ABC
2+
from contextlib import asynccontextmanager
3+
from typing import AsyncGenerator, List, Tuple, Any
4+
5+
from mcp import ClientSession, StdioServerParameters, Tool
6+
from mcp.client.stdio import stdio_client
7+
from ..mcp_servers.params import MCPServersParams, MCPServerConfig
8+
from ..mcp_servers.exceptions import ServerConfigNotFoundError
9+
10+
class MCPBaseAdapter(ABC):
11+
def __init__(self, servers_params: MCPServersParams):
12+
self.servers_params = servers_params
13+
14+
def get_server_config(self, mcp_name: str) -> MCPServerConfig:
15+
"""Get server configuration or raise error if not found"""
16+
server_config = self.servers_params.retrieve_server_params(mcp_name)
17+
if not server_config:
18+
raise ServerConfigNotFoundError(f"Server configuration not found for '{mcp_name}'")
19+
return server_config
20+
21+
def get_server_params(self, mcp_name: str) -> StdioServerParameters:
22+
"""Convert server config to StdioServerParameters"""
23+
server_config = self.get_server_config(mcp_name)
24+
return StdioServerParameters(
25+
command=server_config.command,
26+
args=server_config.args,
27+
env=server_config.env,
28+
cwd=server_config.cwd
29+
)
30+
31+
async def get_tools(self, mcp_name: str) -> List[Tool]:
32+
"""Get tools from the server"""
33+
async with self.create_session(mcp_name) as session:
34+
tools = await session.list_tools()
35+
return tools.tools
36+
37+
@asynccontextmanager
38+
async def create_session(self, mcp_name: str) -> AsyncGenerator[ClientSession, None]:
39+
"""Create and initialize a client session for the given MCP server"""
40+
server_params = self.get_server_params(mcp_name)
41+
async with stdio_client(server_params) as (read, write):
42+
async with ClientSession(read, write) as session:
43+
await session.initialize()
44+
yield session

src/mcphub/adapters/langchain.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
3+
try:
4+
from typing import List
5+
6+
from langchain_core.tools import BaseTool
7+
from langchain_mcp_adapters.tools import load_mcp_tools
8+
9+
from .base import MCPBaseAdapter
10+
11+
class MCPLangChainAdapter(MCPBaseAdapter):
12+
async def create_tools(self, mcp_name: str) -> List[BaseTool]:
13+
async with self.create_session(mcp_name) as session:
14+
return await load_mcp_tools(session)
15+
16+
except ImportError:
17+
class MCPLangChainAdapter: # type: ignore
18+
def __init__(self, *args, **kwargs):
19+
raise ImportError("LangChain dependencies not found. Install with: pip install mcphub[langchain]")

src/mcphub/adapters/openai.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
try:
2+
from agents.mcp import MCPServerStdio, MCPServerStdioParams
3+
from .base import MCPBaseAdapter
4+
5+
class MCPOpenAIAgentsAdapter(MCPBaseAdapter):
6+
def create_server(self, mcp_name: str, cache_tools_list: bool = True) -> MCPServerStdio:
7+
server_config = self.get_server_config(mcp_name)
8+
server_params = MCPServerStdioParams(
9+
command=server_config.command,
10+
args=server_config.args,
11+
env=server_config.env,
12+
cwd=server_config.cwd
13+
)
14+
return MCPServerStdio(
15+
params=server_params,
16+
cache_tools_list=cache_tools_list
17+
)
18+
except ImportError:
19+
class MCPOpenAIAgentsAdapter: # type: ignore
20+
def __init__(self, *args, **kwargs):
21+
raise ImportError("OpenAI Agents dependencies not found. Install with: pip install mcphub[openai]")

src/mcphub/mcp_servers/params.py

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class MCPServerConfig:
1414
command: str
1515
args: List[str]
1616
env: Dict[str, str]
17+
server_name: Optional[str] = None
1718
description: Optional[str] = None
1819
tags: Optional[List[str]] = None
1920
repo_url: Optional[str] = None
@@ -28,7 +29,11 @@ def __init__(self, config_path: Optional[str]):
2829
@property
2930
def servers_params(self) -> List[MCPServerConfig]:
3031
"""Return the list of server parameters."""
31-
return list(self._servers_params.values())
32+
server_configs = []
33+
for server_name, server_params in self._servers_params.items():
34+
server_params.server_name = server_name
35+
server_configs.append(server_params)
36+
return server_configs
3237

3338
def _load_user_config(self) -> Dict:
3439
"""Load user configuration from JSON file."""
@@ -60,70 +65,51 @@ def _load_servers_params(self) -> Dict[str, MCPServerConfig]:
6065
predefined_servers_params = self._load_predefined_servers_params()
6166
servers = {}
6267

63-
# If we have user configuration, process it normally
64-
if config:
65-
for mcp_name, server_config in config.items():
66-
package_name = server_config.get("package_name")
67-
68-
# Check if command and args are configured in user config
69-
if "command" in server_config and "args" in server_config:
70-
# Use configuration directly from .mcphub.json
71-
servers[mcp_name] = MCPServerConfig(
72-
package_name=package_name,
73-
command=server_config["command"],
74-
args=server_config["args"],
75-
env=server_config.get("env", {}),
76-
description=server_config.get("description"),
77-
tags=server_config.get("tags"),
78-
repo_url=server_config.get("repo_url"),
79-
setup_script=server_config.get("setup_script")
80-
)
81-
# Fallback to predefined configuration
82-
elif package_name and predefined_servers_params.get("mcpServers", {}).get(package_name):
83-
cmd_info = predefined_servers_params["mcpServers"][package_name]
84-
servers[mcp_name] = MCPServerConfig(
85-
package_name=package_name,
86-
command=cmd_info.get("command"),
87-
args=cmd_info.get("args", []),
88-
env=server_config.get("env", {}),
89-
description=cmd_info.get("description"),
90-
tags=cmd_info.get("tags"),
91-
repo_url=cmd_info.get("repo_url"),
92-
setup_script=cmd_info.get("setup_script")
93-
)
94-
else:
95-
raise ServerConfigNotFoundError(
96-
f"Server '{package_name}' must either have command and args configured in .mcphub.json "
97-
f"or be defined in mcphub_preconfigured_servers.json"
98-
)
68+
for mcp_name, server_config in config.items():
69+
package_name = server_config.get("package_name")
70+
71+
# Check if command and args are configured in user config
72+
if "command" in server_config and "args" in server_config:
73+
# Use configuration directly from .mcphub.json
74+
servers[mcp_name] = MCPServerConfig(
75+
package_name=package_name,
76+
command=server_config["command"],
77+
args=server_config["args"],
78+
env=server_config.get("env", {}),
79+
description=server_config.get("description"),
80+
tags=server_config.get("tags"),
81+
repo_url=server_config.get("repo_url"),
82+
setup_script=server_config.get("setup_script")
83+
)
84+
# Fallback to predefined configuration
85+
elif package_name and predefined_servers_params.get("mcpServers", {}).get(package_name):
86+
cmd_info = predefined_servers_params["mcpServers"][package_name]
87+
servers[mcp_name] = MCPServerConfig(
88+
package_name=package_name,
89+
command=cmd_info.get("command"),
90+
args=cmd_info.get("args", []),
91+
env=server_config.get("env", {}),
92+
description=cmd_info.get("description"),
93+
tags=cmd_info.get("tags"),
94+
repo_url=cmd_info.get("repo_url"),
95+
setup_script=cmd_info.get("setup_script")
96+
)
97+
else:
98+
raise ServerConfigNotFoundError(
99+
f"Server '{package_name}' must either have command and args configured in .mcphub.json "
100+
f"or be defined in mcphub_preconfigured_servers.json"
101+
)
99102

100103
return servers
101104

105+
def list_servers(self) -> List[MCPServerConfig]:
106+
return self.servers_params
107+
102108
def retrieve_server_params(self, server_name: str) -> MCPServerConfig:
103109
# First check in the loaded servers
104110
if server_name in self._servers_params:
105111
return self._servers_params[server_name]
106-
107-
# If not found, check if it's a direct reference to a predefined server
108-
predefined_servers = self._load_predefined_servers_params().get("mcpServers", {})
109-
if server_name in predefined_servers:
110-
# This is a predefined server being referenced directly by its name
111-
cmd_info = predefined_servers[server_name]
112-
server_config = MCPServerConfig(
113-
package_name=server_name,
114-
command=cmd_info.get("command"),
115-
args=cmd_info.get("args", []),
116-
env=cmd_info.get("env", {}),
117-
description=cmd_info.get("description"),
118-
tags=cmd_info.get("tags"),
119-
repo_url=cmd_info.get("repo_url"),
120-
setup_script=cmd_info.get("setup_script")
121-
)
122-
# Store it in loaded servers for future requests
123-
self._servers_params[server_name] = server_config
124-
return server_config
125-
126-
return None
112+
raise ServerConfigNotFoundError(f"Server '{server_name}' not found")
127113

128114
def convert_to_stdio_params(self, server_name: str) -> StdioServerParameters:
129115
server_params = self.retrieve_server_params(server_name)

0 commit comments

Comments
 (0)