Skip to content

Commit 0104477

Browse files
hieutrtrtoreleonCopilot
authored
release 0.1.8 - refactor install optional framework (#27)
* Toreleon non config mcphub (#21) * Add CI/CD workflows for testing, version bumping, and PyPI publishing; enhance README with development and CI/CD details; implement comprehensive tests for MCPHub and MCPServers functionality. * Enhance CI workflow by installing all extras with Poetry * Refactor CI workflow to include version bumping and streamline package publishing using Poetry * Update README to clarify MCPHub installation options with framework-specific dependencies * Restrict pull request triggers to the release branch only * Update CI workflow to enable fail-fast strategy and restrict Python versions to 3.12 only * Refactor CI workflows to remove version bumping from CI and add tagging functionality; update README for clarity on automatic versioning and tagging process. * Refactor CI workflows to integrate version bumping and ensure proper versioning before publishing; update version-bump workflow to set outputs for new version. * Bump version to 0.1.6 in pyproject.toml * Refactor CI workflows: remove version-bump workflow and integrate version extraction and tagging directly in the publish job; update package version to 0.1.7 in pyproject.toml * Fix test data structure in TestMCPServersParams to match expected format Updated the mock data in the test for _load_predefined_servers_params to reflect the correct structure, ensuring that the "mcpServers" key wraps the predefined server details. This change enhances the accuracy of the test and aligns it with the actual implementation. * Toreleon cli (#22) * Add CI/CD workflows for testing, version bumping, and PyPI publishing; enhance README with development and CI/CD details; implement comprehensive tests for MCPHub and MCPServers functionality. * Enhance CI workflow by installing all extras with Poetry * Refactor CI workflow to include version bumping and streamline package publishing using Poetry * Update README to clarify MCPHub installation options with framework-specific dependencies * Restrict pull request triggers to the release branch only * Update CI workflow to enable fail-fast strategy and restrict Python versions to 3.12 only * Refactor CI workflows to remove version bumping from CI and add tagging functionality; update README for clarity on automatic versioning and tagging process. * Refactor CI workflows to integrate version bumping and ensure proper versioning before publishing; update version-bump workflow to set outputs for new version. * Bump version to 0.1.6 in pyproject.toml * Refactor CI workflows: remove version-bump workflow and integrate version extraction and tagging directly in the publish job; update package version to 0.1.7 in pyproject.toml * Fix test data structure in TestMCPServersParams to match expected format Updated the mock data in the test for _load_predefined_servers_params to reflect the correct structure, ensuring that the "mcpServers" key wraps the predefined server details. This change enhances the accuracy of the test and aligns it with the actual implementation. * Add CLI commands and utilities for MCPHub configuration management - Implemented CLI commands for initializing, adding, removing, and listing MCP servers. - Created utility functions for loading and saving configuration files. - Added tests for CLI commands to ensure proper functionality and error handling. * Enhance test for 'init' command to skip config creation when it exists * Enhance test for 'init' command to skip config creation when it exists (#23) * Enhance test for 'init' command to skip config creation when it exists * Update tests/test_cli.py Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]> * Enhance test for 'init' command to skip config creation when it exists (#24) * Refactor dependencies in pyproject.toml to separate optional dependencies and clean up main dependencies section * Toreleon fix dependencies (#25) * Enhance test for 'init' command to skip config creation when it exists * Refactor dependencies in pyproject.toml to separate optional dependencies and clean up main dependencies section * Optional init (#26) * solve test * remove legacy functions --------- Co-authored-by: Thang Le <[email protected]> Co-authored-by: toreleon <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c4f37c5 commit 0104477

22 files changed

+18901
-22553
lines changed
File renamed without changes.

pyproject.toml

Lines changed: 7 additions & 8 deletions
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 = [
@@ -21,14 +21,14 @@ classifiers = [
2121
"Programming Language :: Python :: 3.11",
2222
"Programming Language :: Python :: 3.12",
2323
]
24-
dependencies = [
25-
"python-dotenv>=1.0.0,<2.0.0",
26-
"build (>=1.2.2.post1,<2.0.0)",
27-
"twine (>=6.1.0,<7.0.0)",
28-
]
24+
dependencies = []
2925
requires-python = "<4.0,>=3.10"
3026

3127
[project.optional-dependencies]
28+
dev = [
29+
"build>=1.2.2.post1,<2.0.0",
30+
"twine>=6.1.0,<7.0.0",
31+
]
3232
openai = [
3333
"openai-agents (>=0.0.9,<0.0.10)",
3434
]
@@ -54,5 +54,4 @@ mcphub = "mcphub.cli.commands:main"
5454

5555
[tool.poetry.group.dev.dependencies]
5656
pytest = "^8.3.5"
57-
pytest-asyncio = "^0.26.0"
58-
57+
pytest-asyncio = "^0.26.0"

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/cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# CLI package for MCPHub

src/mcphub/cli/commands.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
"""CLI commands for mcphub."""
2+
import argparse
3+
import json
4+
import sys
5+
from pathlib import Path
6+
from typing import Dict, Any, List, Optional
7+
8+
from .utils import (
9+
load_config,
10+
save_config,
11+
DEFAULT_CONFIG,
12+
get_config_path,
13+
add_server_config,
14+
remove_server_config,
15+
list_available_servers,
16+
list_configured_servers
17+
)
18+
19+
def init_command(args):
20+
"""Initialize a new .mcphub.json configuration file in the current directory."""
21+
config_path = get_config_path()
22+
if config_path.exists():
23+
print(f"Configuration file already exists at: {config_path}")
24+
return
25+
26+
save_config(DEFAULT_CONFIG)
27+
print(f"Created new configuration file at: {config_path}")
28+
29+
def add_command(args):
30+
"""Add a preconfigured MCP server to the local config."""
31+
server_name = args.mcp_name
32+
non_interactive = args.non_interactive if hasattr(args, 'non_interactive') else False
33+
34+
success, missing_env_vars = add_server_config(server_name, interactive=not non_interactive)
35+
36+
if not success:
37+
print(f"Error: MCP server '{server_name}' not found in preconfigured servers")
38+
# Show available options
39+
print("\nAvailable preconfigured servers:")
40+
available_servers = list_available_servers()
41+
for name in available_servers:
42+
print(f"- {name}")
43+
sys.exit(1)
44+
45+
print(f"Added configuration for '{server_name}' to .mcphub.json")
46+
47+
# Notify about missing environment variables
48+
if missing_env_vars:
49+
print("\nWarning: The following environment variables are required but not set:")
50+
for var in missing_env_vars:
51+
print(f"- {var}")
52+
print("\nYou can either:")
53+
print("1. Set them in your environment before using this server")
54+
print("2. Run 'mcphub add-env' to add them to your configuration")
55+
print("3. Edit .mcphub.json manually to set the values")
56+
57+
def remove_command(args):
58+
"""Remove an MCP server configuration from the local config."""
59+
server_name = args.mcp_name
60+
if remove_server_config(server_name):
61+
print(f"Removed configuration for '{server_name}' from .mcphub.json")
62+
else:
63+
print(f"Error: MCP server '{server_name}' not found in current configuration")
64+
# Show what's currently configured
65+
configured = list_configured_servers()
66+
if configured:
67+
print("\nCurrently configured servers:")
68+
for name in configured:
69+
print(f"- {name}")
70+
sys.exit(1)
71+
72+
def list_command(args):
73+
"""List all configured and available MCP servers."""
74+
show_all = args.all if hasattr(args, 'all') else False
75+
76+
configured = list_configured_servers()
77+
print("Configured MCP servers:")
78+
if configured:
79+
for name in configured:
80+
print(f"- {name}")
81+
else:
82+
print(" No servers configured in local .mcphub.json")
83+
84+
if show_all:
85+
available = list_available_servers()
86+
print("\nAvailable preconfigured MCP servers:")
87+
if available:
88+
for name in available:
89+
print(f"- {name}")
90+
else:
91+
print(" No preconfigured servers available")
92+
93+
def parse_args(args=None):
94+
"""Parse command line arguments."""
95+
parser = argparse.ArgumentParser(
96+
description="MCPHub CLI tool for managing MCP server configurations"
97+
)
98+
99+
subparsers = parser.add_subparsers(dest="command", help="Commands")
100+
101+
# Init command
102+
init_parser = subparsers.add_parser(
103+
"init",
104+
help="Create a new .mcphub.json file in the current directory"
105+
)
106+
107+
# Add command
108+
add_parser = subparsers.add_parser(
109+
"add",
110+
help="Add a preconfigured MCP server to your local config"
111+
)
112+
add_parser.add_argument(
113+
"mcp_name",
114+
help="Name of the preconfigured MCP server to add"
115+
)
116+
add_parser.add_argument(
117+
"-n", "--non-interactive",
118+
action="store_true",
119+
help="Don't prompt for environment variables"
120+
)
121+
122+
# Remove command
123+
remove_parser = subparsers.add_parser(
124+
"remove",
125+
help="Remove an MCP server from your local config"
126+
)
127+
remove_parser.add_argument(
128+
"mcp_name",
129+
help="Name of the MCP server to remove"
130+
)
131+
132+
# List command
133+
list_parser = subparsers.add_parser(
134+
"list",
135+
help="List configured MCP servers"
136+
)
137+
list_parser.add_argument(
138+
"-a", "--all",
139+
action="store_true",
140+
help="Show all available preconfigured servers"
141+
)
142+
143+
return parser.parse_args(args)
144+
145+
def main():
146+
"""Main entry point for the CLI."""
147+
args = parse_args()
148+
149+
if args.command == "init":
150+
init_command(args)
151+
elif args.command == "add":
152+
add_command(args)
153+
elif args.command == "remove":
154+
remove_command(args)
155+
elif args.command == "list":
156+
list_command(args)
157+
else:
158+
# Show help if no command is provided
159+
parse_args(["-h"])
160+
161+
if __name__ == "__main__":
162+
main()

0 commit comments

Comments
 (0)