universal-mcp 0.1.15rc5__py3-none-any.whl → 0.1.16__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.
- universal_mcp/analytics.py +7 -1
- universal_mcp/applications/README.md +122 -0
- universal_mcp/applications/__init__.py +51 -56
- universal_mcp/applications/application.py +255 -82
- universal_mcp/cli.py +27 -43
- universal_mcp/config.py +16 -48
- universal_mcp/exceptions.py +8 -0
- universal_mcp/integrations/__init__.py +1 -3
- universal_mcp/integrations/integration.py +18 -2
- universal_mcp/logger.py +31 -29
- universal_mcp/servers/server.py +6 -18
- universal_mcp/stores/store.py +2 -12
- universal_mcp/tools/__init__.py +12 -1
- universal_mcp/tools/adapters.py +11 -0
- universal_mcp/tools/func_metadata.py +11 -15
- universal_mcp/tools/manager.py +163 -117
- universal_mcp/tools/tools.py +6 -13
- universal_mcp/utils/agentr.py +2 -6
- universal_mcp/utils/common.py +33 -0
- universal_mcp/utils/docstring_parser.py +4 -13
- universal_mcp/utils/installation.py +67 -184
- universal_mcp/utils/openapi/__inti__.py +0 -0
- universal_mcp/utils/{api_generator.py → openapi/api_generator.py} +2 -4
- universal_mcp/utils/{docgen.py → openapi/docgen.py} +17 -54
- universal_mcp/utils/openapi/openapi.py +882 -0
- universal_mcp/utils/openapi/preprocessor.py +1093 -0
- universal_mcp/utils/{readme.py → openapi/readme.py} +21 -37
- universal_mcp-0.1.16.dist-info/METADATA +282 -0
- universal_mcp-0.1.16.dist-info/RECORD +44 -0
- universal_mcp-0.1.16.dist-info/licenses/LICENSE +21 -0
- universal_mcp/utils/openapi.py +0 -646
- universal_mcp-0.1.15rc5.dist-info/METADATA +0 -245
- universal_mcp-0.1.15rc5.dist-info/RECORD +0 -39
- /universal_mcp/{templates → utils/templates}/README.md.j2 +0 -0
- /universal_mcp/{templates → utils/templates}/api_client.py.j2 +0 -0
- {universal_mcp-0.1.15rc5.dist-info → universal_mcp-0.1.16.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.15rc5.dist-info → universal_mcp-0.1.16.dist-info}/entry_points.txt +0 -0
universal_mcp/analytics.py
CHANGED
@@ -48,7 +48,12 @@ class Analytics:
|
|
48
48
|
logger.error(f"Failed to track app_loaded event: {e}")
|
49
49
|
|
50
50
|
def track_tool_called(
|
51
|
-
self,
|
51
|
+
self,
|
52
|
+
tool_name: str,
|
53
|
+
app_name: str,
|
54
|
+
status: str,
|
55
|
+
error: str = None,
|
56
|
+
user_id=None,
|
52
57
|
):
|
53
58
|
"""Track when a tool is called
|
54
59
|
|
@@ -63,6 +68,7 @@ class Analytics:
|
|
63
68
|
try:
|
64
69
|
properties = {
|
65
70
|
"tool_name": tool_name,
|
71
|
+
"app_name": app_name,
|
66
72
|
"status": status,
|
67
73
|
"error": error,
|
68
74
|
"version": self.get_version(),
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Universal MCP Applications Module
|
2
|
+
|
3
|
+
This module provides the core functionality for managing and integrating applications within the Universal MCP system. It offers a flexible framework for creating, managing, and interacting with various types of applications through a unified interface.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The applications module provides three main base classes for building application integrations:
|
8
|
+
|
9
|
+
1. `BaseApplication`: The abstract base class that defines the common interface for all applications
|
10
|
+
2. `APIApplication`: A concrete implementation for applications that communicate via HTTP APIs
|
11
|
+
3. `GraphQLApplication`: A specialized implementation for applications that use GraphQL APIs
|
12
|
+
|
13
|
+
## Key Features
|
14
|
+
|
15
|
+
- **Dynamic Application Loading**: Applications can be loaded dynamically from external packages
|
16
|
+
- **Unified Credential Management**: Centralized handling of application credentials
|
17
|
+
- **HTTP API Support**: Built-in support for RESTful API interactions
|
18
|
+
- **GraphQL Support**: Specialized support for GraphQL-based applications
|
19
|
+
- **Automatic Package Installation**: Automatic installation of application packages from GitHub
|
20
|
+
|
21
|
+
## Base Classes
|
22
|
+
|
23
|
+
### BaseApplication
|
24
|
+
|
25
|
+
The foundation class for all applications, providing:
|
26
|
+
- Basic initialization
|
27
|
+
- Credential management
|
28
|
+
- Tool listing interface
|
29
|
+
|
30
|
+
### APIApplication
|
31
|
+
|
32
|
+
Extends BaseApplication to provide:
|
33
|
+
- HTTP client management
|
34
|
+
- Authentication handling
|
35
|
+
- Common HTTP methods (GET, POST, PUT, DELETE, PATCH)
|
36
|
+
- Request/response handling
|
37
|
+
|
38
|
+
### GraphQLApplication
|
39
|
+
|
40
|
+
Specialized for GraphQL-based applications, offering:
|
41
|
+
- GraphQL client management
|
42
|
+
- Query and mutation execution
|
43
|
+
- Authentication handling
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
### Creating a New Application
|
48
|
+
|
49
|
+
1. Create a new package following the naming convention: `universal_mcp_<app_name>`
|
50
|
+
2. Implement your application class inheriting from one of the base classes
|
51
|
+
3. Name your class following the convention: `<AppName>App`
|
52
|
+
|
53
|
+
Example:
|
54
|
+
```python
|
55
|
+
from universal_mcp.applications import APIApplication
|
56
|
+
|
57
|
+
class MyApp(APIApplication):
|
58
|
+
def __init__(self, name: str, integration=None, **kwargs):
|
59
|
+
super().__init__(name, integration, **kwargs)
|
60
|
+
self.base_url = "https://api.example.com"
|
61
|
+
|
62
|
+
def list_tools(self):
|
63
|
+
return [self.my_tool]
|
64
|
+
|
65
|
+
def my_tool(self):
|
66
|
+
# Implementation here
|
67
|
+
pass
|
68
|
+
```
|
69
|
+
|
70
|
+
### Loading an Application
|
71
|
+
|
72
|
+
```python
|
73
|
+
from universal_mcp.applications import app_from_slug
|
74
|
+
|
75
|
+
# The system will automatically install the package if needed
|
76
|
+
MyApp = app_from_slug("my-app")
|
77
|
+
app = MyApp("my-app-instance")
|
78
|
+
```
|
79
|
+
|
80
|
+
## Authentication
|
81
|
+
|
82
|
+
The module supports various authentication methods:
|
83
|
+
- API Keys
|
84
|
+
- Access Tokens
|
85
|
+
- Custom Headers
|
86
|
+
- Bearer Tokens
|
87
|
+
|
88
|
+
Credentials are managed through the integration system and can be accessed via the `credentials` property.
|
89
|
+
|
90
|
+
## Error Handling
|
91
|
+
|
92
|
+
The module includes comprehensive error handling for:
|
93
|
+
- Package installation failures
|
94
|
+
- Import errors
|
95
|
+
- API request failures
|
96
|
+
- Authentication issues
|
97
|
+
|
98
|
+
## Logging
|
99
|
+
|
100
|
+
All operations are logged using the `loguru` logger, providing detailed information about:
|
101
|
+
- Application initialization
|
102
|
+
- API requests
|
103
|
+
- Authentication attempts
|
104
|
+
- Package installation
|
105
|
+
- Error conditions
|
106
|
+
|
107
|
+
## Requirements
|
108
|
+
|
109
|
+
- Python 3.8+
|
110
|
+
- httpx
|
111
|
+
- gql
|
112
|
+
- loguru
|
113
|
+
- uv (for package installation)
|
114
|
+
|
115
|
+
## Contributing
|
116
|
+
|
117
|
+
To contribute a new application:
|
118
|
+
1. Create a new package following the naming conventions
|
119
|
+
2. Implement the application class
|
120
|
+
3. Add proper error handling and logging
|
121
|
+
4. Include comprehensive documentation
|
122
|
+
5. Submit a pull request to the Universal MCP repository
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import importlib
|
2
|
-
import os
|
3
2
|
import subprocess
|
4
3
|
import sys
|
4
|
+
from pathlib import Path
|
5
5
|
|
6
6
|
from loguru import logger
|
7
7
|
|
@@ -10,55 +10,56 @@ from universal_mcp.applications.application import (
|
|
10
10
|
BaseApplication,
|
11
11
|
GraphQLApplication,
|
12
12
|
)
|
13
|
+
from universal_mcp.utils.common import (
|
14
|
+
get_default_class_name,
|
15
|
+
get_default_module_path,
|
16
|
+
get_default_package_name,
|
17
|
+
get_default_repository_path,
|
18
|
+
)
|
13
19
|
|
14
|
-
UNIVERSAL_MCP_HOME =
|
20
|
+
UNIVERSAL_MCP_HOME = Path.home() / ".universal-mcp" / "packages"
|
15
21
|
|
16
|
-
if not
|
17
|
-
|
22
|
+
if not UNIVERSAL_MCP_HOME.exists():
|
23
|
+
UNIVERSAL_MCP_HOME.mkdir(parents=True, exist_ok=True)
|
18
24
|
|
19
25
|
# set python path to include the universal-mcp home directory
|
20
|
-
sys.path.append(UNIVERSAL_MCP_HOME)
|
26
|
+
sys.path.append(str(UNIVERSAL_MCP_HOME))
|
21
27
|
|
22
28
|
|
23
29
|
# Name are in the format of "app-name", eg, google-calendar
|
24
30
|
# Class name is NameApp, eg, GoogleCalendarApp
|
25
31
|
|
26
32
|
|
27
|
-
def
|
28
|
-
"""
|
29
|
-
Helper to import a class by name from a module.
|
30
|
-
Raises ModuleNotFoundError if module or class does not exist.
|
31
|
-
"""
|
32
|
-
try:
|
33
|
-
module = importlib.import_module(module_path)
|
34
|
-
except ModuleNotFoundError as e:
|
35
|
-
logger.debug(f"Import failed for module '{module_path}': {e}")
|
36
|
-
raise
|
37
|
-
try:
|
38
|
-
return getattr(module, class_name)
|
39
|
-
except AttributeError as e:
|
40
|
-
logger.error(f"Class '{class_name}' not found in module '{module_path}'")
|
41
|
-
raise ModuleNotFoundError(
|
42
|
-
f"Class '{class_name}' not found in module '{module_path}'"
|
43
|
-
) from e
|
44
|
-
|
45
|
-
|
46
|
-
def _install_package(slug_clean: str):
|
33
|
+
def _install_or_upgrade_package(package_name: str, repository_path: str):
|
47
34
|
"""
|
48
35
|
Helper to install a package via pip from the universal-mcp GitHub repository.
|
49
36
|
"""
|
50
|
-
|
51
|
-
|
52
|
-
|
37
|
+
cmd = [
|
38
|
+
"uv",
|
39
|
+
"pip",
|
40
|
+
"install",
|
41
|
+
"--upgrade",
|
42
|
+
repository_path,
|
43
|
+
"--target",
|
44
|
+
str(UNIVERSAL_MCP_HOME),
|
45
|
+
]
|
46
|
+
logger.debug(f"Installing package '{package_name}' with command: {' '.join(cmd)}")
|
53
47
|
try:
|
54
|
-
subprocess.
|
48
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
49
|
+
if result.stdout:
|
50
|
+
logger.info(f"Command stdout: {result.stdout}")
|
51
|
+
if result.stderr:
|
52
|
+
logger.info(f"Command stderr: {result.stderr}")
|
53
|
+
result.check_returncode()
|
55
54
|
except subprocess.CalledProcessError as e:
|
56
|
-
logger.error(f"Installation failed for '{
|
57
|
-
|
58
|
-
f"
|
59
|
-
|
55
|
+
logger.error(f"Installation failed for '{package_name}': {e}")
|
56
|
+
if e.stdout:
|
57
|
+
logger.error(f"Command stdout: {e.stdout}")
|
58
|
+
if e.stderr:
|
59
|
+
logger.error(f"Command stderr: {e.stderr}")
|
60
|
+
raise ModuleNotFoundError(f"Installation failed for package '{package_name}'") from e
|
60
61
|
else:
|
61
|
-
logger.
|
62
|
+
logger.debug(f"Package {package_name} installed successfully")
|
62
63
|
|
63
64
|
|
64
65
|
def app_from_slug(slug: str):
|
@@ -66,29 +67,23 @@ def app_from_slug(slug: str):
|
|
66
67
|
Dynamically resolve and return the application class for the given slug.
|
67
68
|
Attempts installation from GitHub if the package is not found locally.
|
68
69
|
"""
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
logger.info(
|
75
|
-
f"Resolving app for slug '{slug}' → module '{module_path}', class '{class_name}'"
|
76
|
-
)
|
70
|
+
class_name = get_default_class_name(slug)
|
71
|
+
module_path = get_default_module_path(slug)
|
72
|
+
package_name = get_default_package_name(slug)
|
73
|
+
repository_path = get_default_repository_path(slug)
|
74
|
+
logger.debug(f"Resolving app for slug '{slug}' → module '{module_path}', class '{class_name}'")
|
77
75
|
try:
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
f"Still cannot import '{module_path}' after installation: {retry_err}"
|
90
|
-
)
|
91
|
-
raise
|
76
|
+
_install_or_upgrade_package(package_name, repository_path)
|
77
|
+
module = importlib.import_module(module_path)
|
78
|
+
class_ = getattr(module, class_name)
|
79
|
+
logger.debug(f"Loaded class '{class_}' from module '{module_path}'")
|
80
|
+
return class_
|
81
|
+
except ModuleNotFoundError as e:
|
82
|
+
raise ModuleNotFoundError(f"Package '{module_path}' not found locally. Please install it first.") from e
|
83
|
+
except AttributeError as e:
|
84
|
+
raise AttributeError(f"Class '{class_name}' not found in module '{module_path}'") from e
|
85
|
+
except Exception as e:
|
86
|
+
raise Exception(f"Error importing module '{module_path}': {e}") from e
|
92
87
|
|
93
88
|
|
94
89
|
__all__ = [
|