Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
19008e1
Initial plan
Copilot Jul 19, 2025
269a643
Complete MCP integration implementation for HelpingAI SDK
Copilot Jul 19, 2025
38a335a
Add MCP (Multi-Channel Protocol) Integration for External Tool Support
Copilot Jul 19, 2025
7563c16
chore(tests): remove obsolete MCP integration test files
OEvortex Jul 19, 2025
282ada1
docs: update README.md with MCP integration documentation
Copilot Jul 19, 2025
104c512
chore: bump version to 1.2.0 and update changelog for MCP integration
Copilot Jul 19, 2025
d7e53e8
docs(changelog): correct "Model context Protocol" spelling in MCP int…
OEvortex Jul 19, 2025
92cb1e4
Merge pull request #2 from HelpingAI/copilot/fix-a8977eba-b285-4734-b…
OEvortex Jul 19, 2025
101195c
chore(docs): update MCP integration documentation and fix protocol na…
OEvortex Jul 19, 2025
6c33a5b
docs: update MCP references to Model Context Protocol in documentatio…
OEvortex Jul 19, 2025
c0730b3
chore: migrate setup configuration from setup.py to pyproject.toml
OEvortex Jul 19, 2025
5b383ac
Initial plan
Copilot Jul 19, 2025
f79b314
Implement built-in tools integration with compatibility layer
Copilot Jul 19, 2025
1a7fc8f
Add comprehensive documentation and tests for built-in tools
Copilot Jul 19, 2025
41482b5
Final implementation with demonstration script - built-in tools integ…
Copilot Jul 19, 2025
54117f6
Remove unwanted builtin tools and update web search to use Snapzion API
Copilot Jul 19, 2025
3d7e175
Enable direct calling of built-in tools and MCP tools via client.call…
Copilot Jul 19, 2025
3dabd34
Fix client.call() to return results and handle common user mistakes
Copilot Jul 19, 2025
c154557
Fix MCP tool calling to properly match tool names with server naming …
Copilot Jul 19, 2025
d81a50b
chore(setup): update setuptools configuration for package discovery
OEvortex Jul 19, 2025
a09a756
Fix client.call() to work with configured tools and provide helpful e…
Copilot Jul 19, 2025
9500885
Implement comprehensive MCP auto-configuration system with global too…
Copilot Jul 19, 2025
8b582b0
Implement auto-configuration for common MCP tools to enable direct ca…
Copilot Jul 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,8 @@ ENV/

# Logs
*.log
lol.py
lol.py
.qodo

# Test files and temporary scripts
test_*.py
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

All notable changes to the HelpingAI Python SDK will be documented in this file.

## [1.2.0] - 2025-07-19

### Added
- **🔌 MCP Integration**: Full [Model context Protocol (MCP)](docs/mcp_integration.md) support for external tool connections
- **🖥️ Multiple Transport Types**: Support for stdio, SSE, and streamable-http MCP servers
- **🔄 Automatic Tool Discovery**: MCP tools automatically converted to OpenAI-compatible format
- **📁 Resource Support**: Built-in `list_resources` and `read_resource` tools for MCP resources
- **🔀 Mixed Tools Support**: Seamlessly combine MCP servers with regular OpenAI-format tools
- **⚡ Process Management**: Automatic cleanup of MCP server processes on exit
- **🔁 Reconnection Logic**: Handles server disconnections automatically
- **🛡️ Graceful Error Handling**: Works without MCP package installed with helpful error messages
- **📦 Optional MCP Dependency**: Install with `pip install HelpingAI[mcp]` for MCP features
- New MCP integration documentation and examples

### Enhanced
- **🛠️ Extended Tools Compatibility**: Enhanced tools framework to support MCP server configurations
- **🌐 Popular MCP Servers**: Ready support for mcp-server-time, mcp-server-fetch, mcp-server-filesystem, and more
- **🏗️ Backward Compatibility**: Fully backward compatible with no breaking changes to existing functionality

## [1.1.3] - 2025-07-18

### Added
Expand Down
340 changes: 331 additions & 9 deletions HelpingAI/client.py

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions HelpingAI/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""

HelpingAI Tools - Easy-to-use tool calling utilities.

This module provides decorators and utilities for creating standard
Expand All @@ -9,6 +10,8 @@
- Fn class: Represent callable functions with metadata
- get_tools(): Get registered tools (preferred over get_tools_format)
- get_registry(): Access the tool registry for advanced management
- MCP integration: Support for Model Context Protocol servers
- Built-in tools: Pre-built tools inspired by Qwen-Agent (code_interpreter, web_search, etc.)
"""

from .core import Fn, tools, get_tools, get_tools_format, clear_registry, get_registry
Expand All @@ -28,6 +31,12 @@
validate_tool_compatibility,
get_compatibility_warnings
)
from .builtin_tools import (
get_builtin_tool_class,
get_available_builtin_tools,
is_builtin_tool,
BUILTIN_TOOLS_REGISTRY
)

__version__ = "1.1.3"

Expand Down Expand Up @@ -55,6 +64,12 @@
"validate_tool_compatibility",
"get_compatibility_warnings",

# Built-in tools
"get_builtin_tool_class",
"get_available_builtin_tools",
"is_builtin_tool",
"BUILTIN_TOOLS_REGISTRY",

# Error classes
"ToolExecutionError",
"SchemaValidationError",
Expand Down
65 changes: 65 additions & 0 deletions HelpingAI/tools/builtin_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""
Built-in Tools for HelpingAI SDK

This module provides built-in tools inspired by the Qwen-Agent repository.
These tools can be used alongside MCP servers by specifying simple string identifiers.

Available built-in tools:
- code_interpreter: Advanced Python code execution sandbox with data science capabilities
- web_search: Real-time web search with comprehensive results

Usage:
tools = [
{'mcpServers': {...}}, # MCP servers
'code_interpreter', # Built-in tools
'web_search'
]
"""

from .code_interpreter import CodeInterpreterTool
from .web_search import WebSearchTool

# Registry of built-in tools
BUILTIN_TOOLS_REGISTRY = {
'code_interpreter': CodeInterpreterTool,
'web_search': WebSearchTool,
}

def get_builtin_tool_class(tool_name: str):
"""Get the class for a built-in tool by name.

Args:
tool_name: Name of the built-in tool

Returns:
Tool class if found, None otherwise
"""
return BUILTIN_TOOLS_REGISTRY.get(tool_name)

def get_available_builtin_tools():
"""Get list of available built-in tool names.

Returns:
List of available built-in tool names
"""
return list(BUILTIN_TOOLS_REGISTRY.keys())

def is_builtin_tool(tool_name: str) -> bool:
"""Check if a tool name refers to a built-in tool.

Args:
tool_name: Tool name to check

Returns:
True if it's a built-in tool, False otherwise
"""
return tool_name in BUILTIN_TOOLS_REGISTRY

__all__ = [
'CodeInterpreterTool',
'WebSearchTool',
'BUILTIN_TOOLS_REGISTRY',
'get_builtin_tool_class',
'get_available_builtin_tools',
'is_builtin_tool',
]
210 changes: 210 additions & 0 deletions HelpingAI/tools/builtin_tools/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
"""
Base class for built-in tools inspired by Qwen-Agent.

This module provides the base infrastructure for implementing built-in tools
that are compatible with the HelpingAI tools framework.
"""

import os
import tempfile
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Dict, Any, Union, Optional
from urllib.parse import urlparse
from urllib.request import urlopen

from ..core import Fn
from ..errors import ToolExecutionError


class BuiltinToolBase(ABC):
"""Base class for built-in tools.

This class provides common functionality for built-in tools including
file handling, parameter validation, and integration with HelpingAI's tool framework.
"""

# To be overridden by subclasses
name: str = ""
description: str = ""
parameters: Dict[str, Any] = {}

def __init__(self, config: Optional[Dict[str, Any]] = None):
"""Initialize the built-in tool.

Args:
config: Optional configuration dictionary
"""
self.config = config or {}

# Set up working directory
default_work_dir = os.path.join(tempfile.gettempdir(), 'helpingai_tools', self.name)
self.work_dir = self.config.get('work_dir', default_work_dir)
os.makedirs(self.work_dir, exist_ok=True)

if not self.name:
raise ValueError(f"Tool class {self.__class__.__name__} must define a 'name' attribute")

if not self.description:
raise ValueError(f"Tool class {self.__class__.__name__} must define a 'description' attribute")

@abstractmethod
def execute(self, **kwargs) -> str:
"""Execute the tool with given parameters.

Args:
**kwargs: Tool parameters

Returns:
Tool execution result as string

Raises:
ToolExecutionError: If execution fails
"""
raise NotImplementedError

def to_fn(self) -> Fn:
"""Convert this built-in tool to an Fn object.

Returns:
Fn object that can be used with HelpingAI's tool framework
"""
def tool_function(**kwargs) -> str:
"""Wrapper function for tool execution."""
try:
return self.execute(**kwargs)
except Exception as e:
raise ToolExecutionError(
f"Failed to execute built-in tool '{self.name}': {e}",
tool_name=self.name,
original_error=e
)

return Fn(
name=self.name,
description=self.description,
parameters=self.parameters,
function=tool_function
)

def _validate_parameters(self, params: Dict[str, Any]) -> None:
"""Validate tool parameters against schema.

Args:
params: Parameters to validate

Raises:
ValueError: If validation fails
"""
# Check required parameters
required_params = self.parameters.get('required', [])
for param in required_params:
if param not in params:
raise ValueError(f"Missing required parameter '{param}' for tool '{self.name}'")

# Check for unknown parameters
allowed_params = set(self.parameters.get('properties', {}).keys())
provided_params = set(params.keys())
unknown_params = provided_params - allowed_params

if unknown_params:
raise ValueError(f"Unknown parameters for tool '{self.name}': {', '.join(unknown_params)}")

def _download_file(self, url: str, filename: str = None) -> str:
"""Download a file from URL to working directory.

Args:
url: URL to download from
filename: Optional filename, will be inferred from URL if not provided

Returns:
Path to downloaded file

Raises:
ToolExecutionError: If download fails
"""
try:
if not filename:
parsed_url = urlparse(url)
filename = os.path.basename(parsed_url.path) or 'downloaded_file'

file_path = os.path.join(self.work_dir, filename)

with urlopen(url) as response:
with open(file_path, 'wb') as f:
f.write(response.read())

return file_path

except Exception as e:
raise ToolExecutionError(
f"Failed to download file from {url}: {e}",
tool_name=self.name,
original_error=e
)

def _read_file(self, file_path: str) -> str:
"""Read file content as text.

Args:
file_path: Path to file

Returns:
File content as string

Raises:
ToolExecutionError: If reading fails
"""
try:
if file_path.startswith(('http://', 'https://')):
# Download the file first
local_path = self._download_file(file_path)
file_path = local_path

with open(file_path, 'r', encoding='utf-8') as f:
return f.read()

except Exception as e:
raise ToolExecutionError(
f"Failed to read file {file_path}: {e}",
tool_name=self.name,
original_error=e
)

def _write_file(self, content: str, filename: str) -> str:
"""Write content to file in working directory.

Args:
content: Content to write
filename: Filename

Returns:
Path to written file

Raises:
ToolExecutionError: If writing fails
"""
try:
file_path = os.path.join(self.work_dir, filename)

with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)

return file_path

except Exception as e:
raise ToolExecutionError(
f"Failed to write file {filename}: {e}",
tool_name=self.name,
original_error=e
)

def _cleanup_work_dir(self) -> None:
"""Clean up the working directory."""
try:
import shutil
if os.path.exists(self.work_dir):
shutil.rmtree(self.work_dir)
except Exception:
# Ignore cleanup errors
pass
Loading