universal-mcp 0.1.12__py3-none-any.whl → 0.1.13rc2__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/applications/__init__.py +51 -7
- universal_mcp/cli.py +109 -17
- universal_mcp/integrations/__init__.py +1 -1
- universal_mcp/integrations/integration.py +79 -0
- universal_mcp/servers/README.md +79 -0
- universal_mcp/servers/server.py +17 -29
- universal_mcp/stores/README.md +74 -0
- universal_mcp/stores/store.py +0 -2
- universal_mcp/templates/README.md.j2 +93 -0
- universal_mcp/templates/api_client.py.j2 +27 -0
- universal_mcp/tools/README.md +86 -0
- universal_mcp/tools/tools.py +1 -1
- universal_mcp/utils/agentr.py +90 -0
- universal_mcp/utils/api_generator.py +166 -208
- universal_mcp/utils/openapi.py +221 -321
- universal_mcp/utils/singleton.py +23 -0
- {universal_mcp-0.1.12.dist-info → universal_mcp-0.1.13rc2.dist-info}/METADATA +16 -41
- universal_mcp-0.1.13rc2.dist-info/RECORD +38 -0
- universal_mcp/applications/ahrefs/README.md +0 -76
- universal_mcp/applications/ahrefs/__init__.py +0 -0
- universal_mcp/applications/ahrefs/app.py +0 -2291
- universal_mcp/applications/cal_com_v2/README.md +0 -175
- universal_mcp/applications/cal_com_v2/__init__.py +0 -0
- universal_mcp/applications/cal_com_v2/app.py +0 -5390
- universal_mcp/applications/calendly/README.md +0 -78
- universal_mcp/applications/calendly/__init__.py +0 -0
- universal_mcp/applications/calendly/app.py +0 -1195
- universal_mcp/applications/clickup/README.md +0 -160
- universal_mcp/applications/clickup/__init__.py +0 -0
- universal_mcp/applications/clickup/app.py +0 -5009
- universal_mcp/applications/coda/README.md +0 -133
- universal_mcp/applications/coda/__init__.py +0 -0
- universal_mcp/applications/coda/app.py +0 -3671
- universal_mcp/applications/e2b/README.md +0 -37
- universal_mcp/applications/e2b/app.py +0 -65
- universal_mcp/applications/elevenlabs/README.md +0 -84
- universal_mcp/applications/elevenlabs/__init__.py +0 -0
- universal_mcp/applications/elevenlabs/app.py +0 -1402
- universal_mcp/applications/falai/README.md +0 -42
- universal_mcp/applications/falai/__init__.py +0 -0
- universal_mcp/applications/falai/app.py +0 -332
- universal_mcp/applications/figma/README.md +0 -74
- universal_mcp/applications/figma/__init__.py +0 -0
- universal_mcp/applications/figma/app.py +0 -1261
- universal_mcp/applications/firecrawl/README.md +0 -45
- universal_mcp/applications/firecrawl/app.py +0 -268
- universal_mcp/applications/github/README.md +0 -47
- universal_mcp/applications/github/app.py +0 -429
- universal_mcp/applications/gong/README.md +0 -88
- universal_mcp/applications/gong/__init__.py +0 -0
- universal_mcp/applications/gong/app.py +0 -2297
- universal_mcp/applications/google_calendar/app.py +0 -442
- universal_mcp/applications/google_docs/README.md +0 -40
- universal_mcp/applications/google_docs/app.py +0 -88
- universal_mcp/applications/google_drive/README.md +0 -44
- universal_mcp/applications/google_drive/app.py +0 -286
- universal_mcp/applications/google_mail/README.md +0 -47
- universal_mcp/applications/google_mail/app.py +0 -664
- universal_mcp/applications/google_sheet/README.md +0 -42
- universal_mcp/applications/google_sheet/app.py +0 -150
- universal_mcp/applications/hashnode/app.py +0 -81
- universal_mcp/applications/hashnode/prompt.md +0 -23
- universal_mcp/applications/heygen/README.md +0 -69
- universal_mcp/applications/heygen/__init__.py +0 -0
- universal_mcp/applications/heygen/app.py +0 -956
- universal_mcp/applications/mailchimp/README.md +0 -306
- universal_mcp/applications/mailchimp/__init__.py +0 -0
- universal_mcp/applications/mailchimp/app.py +0 -10937
- universal_mcp/applications/markitdown/app.py +0 -44
- universal_mcp/applications/notion/README.md +0 -55
- universal_mcp/applications/notion/__init__.py +0 -0
- universal_mcp/applications/notion/app.py +0 -527
- universal_mcp/applications/perplexity/README.md +0 -37
- universal_mcp/applications/perplexity/app.py +0 -65
- universal_mcp/applications/reddit/README.md +0 -45
- universal_mcp/applications/reddit/app.py +0 -379
- universal_mcp/applications/replicate/README.md +0 -65
- universal_mcp/applications/replicate/__init__.py +0 -0
- universal_mcp/applications/replicate/app.py +0 -980
- universal_mcp/applications/resend/README.md +0 -38
- universal_mcp/applications/resend/app.py +0 -37
- universal_mcp/applications/retell_ai/README.md +0 -46
- universal_mcp/applications/retell_ai/__init__.py +0 -0
- universal_mcp/applications/retell_ai/app.py +0 -333
- universal_mcp/applications/rocketlane/README.md +0 -42
- universal_mcp/applications/rocketlane/__init__.py +0 -0
- universal_mcp/applications/rocketlane/app.py +0 -194
- universal_mcp/applications/serpapi/README.md +0 -37
- universal_mcp/applications/serpapi/app.py +0 -73
- universal_mcp/applications/spotify/README.md +0 -116
- universal_mcp/applications/spotify/__init__.py +0 -0
- universal_mcp/applications/spotify/app.py +0 -2526
- universal_mcp/applications/supabase/README.md +0 -112
- universal_mcp/applications/supabase/__init__.py +0 -0
- universal_mcp/applications/supabase/app.py +0 -2970
- universal_mcp/applications/tavily/README.md +0 -38
- universal_mcp/applications/tavily/app.py +0 -51
- universal_mcp/applications/wrike/README.md +0 -71
- universal_mcp/applications/wrike/__init__.py +0 -0
- universal_mcp/applications/wrike/app.py +0 -1372
- universal_mcp/applications/youtube/README.md +0 -82
- universal_mcp/applications/youtube/__init__.py +0 -0
- universal_mcp/applications/youtube/app.py +0 -1428
- universal_mcp/applications/zenquotes/README.md +0 -37
- universal_mcp/applications/zenquotes/app.py +0 -31
- universal_mcp/integrations/agentr.py +0 -112
- universal_mcp-0.1.12.dist-info/RECORD +0 -119
- {universal_mcp-0.1.12.dist-info → universal_mcp-0.1.13rc2.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.12.dist-info → universal_mcp-0.1.13rc2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
# {{ name }} MCP Server
|
2
|
+
|
3
|
+
An MCP Server for the {{ name }} API.
|
4
|
+
|
5
|
+
## 📋 Prerequisites
|
6
|
+
|
7
|
+
Before you begin, ensure you have met the following requirements:
|
8
|
+
* Python 3.11+ (Recommended)
|
9
|
+
* [uv](https://github.com/astral-sh/uv) installed globally (`pip install uv`)
|
10
|
+
|
11
|
+
## 🛠️ Setup Instructions
|
12
|
+
|
13
|
+
Follow these steps to get the development environment up and running:
|
14
|
+
|
15
|
+
### 1. Sync Project Dependencies
|
16
|
+
Navigate to the project root directory (where `pyproject.toml` is located).
|
17
|
+
```bash
|
18
|
+
uv sync
|
19
|
+
```
|
20
|
+
This command uses `uv` to install all dependencies listed in `pyproject.toml` into a virtual environment (`.venv`) located in the project root.
|
21
|
+
|
22
|
+
### 2. Activate the Virtual Environment
|
23
|
+
Activating the virtual environment ensures that you are using the project's specific dependencies and Python interpreter.
|
24
|
+
- On **Linux/macOS**:
|
25
|
+
```bash
|
26
|
+
source .venv/bin/activate
|
27
|
+
```
|
28
|
+
- On **Windows**:
|
29
|
+
```bash
|
30
|
+
.venv\\Scripts\\activate
|
31
|
+
```
|
32
|
+
|
33
|
+
### 3. Start the MCP Inspector
|
34
|
+
Use the MCP CLI to start the application in development mode.
|
35
|
+
```bash
|
36
|
+
mcp dev src/{{ name.lower() }}/mcp.py
|
37
|
+
```
|
38
|
+
The MCP inspector should now be running. Check the console output for the exact address and port.
|
39
|
+
|
40
|
+
## 🔌 Supported Integrations
|
41
|
+
|
42
|
+
- AgentR
|
43
|
+
- API Key (Coming Soon)
|
44
|
+
- OAuth (Coming Soon)
|
45
|
+
|
46
|
+
## 🛠️ Tool List
|
47
|
+
|
48
|
+
This is automatically generated from OpenAPI schema for the {{ name }} API.
|
49
|
+
|
50
|
+
{% if tools %}
|
51
|
+
| Tool | Description |
|
52
|
+
|------|-------------|
|
53
|
+
{%- for tool_name, tool_desc in tools %}
|
54
|
+
| `{{ tool_name }}` | {{ tool_desc }} |
|
55
|
+
{%- endfor %}
|
56
|
+
{% else %}
|
57
|
+
No tools with documentation were found in this API client.
|
58
|
+
{% endif %}
|
59
|
+
|
60
|
+
## 📁 Project Structure
|
61
|
+
|
62
|
+
The generated project has a standard layout:
|
63
|
+
```
|
64
|
+
.
|
65
|
+
├── src/ # Source code directory
|
66
|
+
│ └── {{ name.lower() }}/
|
67
|
+
│ ├── __init__.py
|
68
|
+
│ └── mcp.py # Server is launched here
|
69
|
+
│ └── app.py # Application tools are defined here
|
70
|
+
├── tests/ # Directory for project tests
|
71
|
+
├── .env # Environment variables (for local development)
|
72
|
+
├── pyproject.toml # Project dependencies managed by uv
|
73
|
+
├── README.md # This file
|
74
|
+
```
|
75
|
+
|
76
|
+
## 📝 License
|
77
|
+
|
78
|
+
This project is licensed under the MIT License.
|
79
|
+
|
80
|
+
---
|
81
|
+
|
82
|
+
_This project was generated using **MCP CLI** — Happy coding! 🚀_
|
83
|
+
|
84
|
+
## Usage
|
85
|
+
|
86
|
+
- Login to AgentR
|
87
|
+
- Follow the quickstart guide to setup MCP Server for your client
|
88
|
+
- Visit Apps Store and enable the {{ name }} app
|
89
|
+
- Restart the MCP Server
|
90
|
+
|
91
|
+
### Local Development
|
92
|
+
|
93
|
+
- Follow the README to test with the local MCP Server
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from typing import Any, Annotated
|
2
|
+
from universal_mcp.applications import APIApplication
|
3
|
+
from universal_mcp.integrations import Integration
|
4
|
+
|
5
|
+
class {{ class_name }}(APIApplication):
|
6
|
+
def __init__(self, integration: Integration = None, **kwargs) -> None:
|
7
|
+
super().__init__(name='{{ class_name.lower() }}', integration=integration, **kwargs)
|
8
|
+
self.base_url = "{{ base_url }}"
|
9
|
+
|
10
|
+
{% for method in methods %}
|
11
|
+
def {{ method.name }}(self, {{ method.args_str }}) -> {{ method.return_type }}:
|
12
|
+
"""
|
13
|
+
{{ method.description }}
|
14
|
+
{% if method.tags %}
|
15
|
+
Tags: {{ method.tags|join(', ') }}
|
16
|
+
{% endif %}
|
17
|
+
"""
|
18
|
+
|
19
|
+
{{ method.implementation|indent(8) }}
|
20
|
+
{% endfor %}
|
21
|
+
|
22
|
+
def list_tools(self):
|
23
|
+
return [
|
24
|
+
{% for method in methods %}
|
25
|
+
self.{{ method.name }}{% if not loop.last %},{% endif %}
|
26
|
+
{%- endfor %}
|
27
|
+
]
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# Universal MCP Tools
|
2
|
+
|
3
|
+
This directory contains the core tooling infrastructure for Universal MCP, providing a flexible and extensible framework for defining, managing, and converting tools across different formats.
|
4
|
+
|
5
|
+
## Components
|
6
|
+
|
7
|
+
### `tools.py`
|
8
|
+
The main module containing the core tool management functionality:
|
9
|
+
|
10
|
+
- `Tool` class: Represents a tool with metadata, validation, and execution capabilities
|
11
|
+
- `ToolManager` class: Manages tool registration, lookup, and execution
|
12
|
+
- Conversion utilities for different tool formats (OpenAI, LangChain, MCP)
|
13
|
+
|
14
|
+
### `adapters.py`
|
15
|
+
Contains adapters for converting tools between different formats:
|
16
|
+
- `convert_tool_to_mcp_tool`: Converts a tool to MCP format
|
17
|
+
- `convert_tool_to_langchain_tool`: Converts a tool to LangChain format
|
18
|
+
|
19
|
+
### `func_metadata.py`
|
20
|
+
Provides function metadata and argument validation:
|
21
|
+
- `FuncMetadata` class: Handles function signature analysis and argument validation
|
22
|
+
- `ArgModelBase` class: Base model for function arguments
|
23
|
+
- Utilities for parsing and validating function signatures
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
### Creating a Tool
|
28
|
+
|
29
|
+
```python
|
30
|
+
from universal_mcp.tools import Tool
|
31
|
+
|
32
|
+
def my_tool(param1: str, param2: int) -> str:
|
33
|
+
"""A simple tool that does something.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
param1: Description of param1
|
37
|
+
param2: Description of param2
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
Description of return value
|
41
|
+
"""
|
42
|
+
return f"Result: {param1} {param2}"
|
43
|
+
|
44
|
+
tool = Tool.from_function(my_tool)
|
45
|
+
```
|
46
|
+
|
47
|
+
### Managing Tools
|
48
|
+
|
49
|
+
```python
|
50
|
+
from universal_mcp.tools import ToolManager
|
51
|
+
|
52
|
+
manager = ToolManager()
|
53
|
+
manager.add_tool(my_tool)
|
54
|
+
|
55
|
+
# Get a tool by name
|
56
|
+
tool = manager.get_tool("my_tool")
|
57
|
+
|
58
|
+
# List all tools in a specific format
|
59
|
+
tools = manager.list_tools(format="openai") # or "langchain" or "mcp"
|
60
|
+
```
|
61
|
+
|
62
|
+
### Converting Tools
|
63
|
+
|
64
|
+
```python
|
65
|
+
from universal_mcp.tools import convert_tool_to_langchain_tool
|
66
|
+
|
67
|
+
langchain_tool = convert_tool_to_langchain_tool(tool)
|
68
|
+
```
|
69
|
+
|
70
|
+
## Features
|
71
|
+
|
72
|
+
- Automatic docstring parsing for tool metadata
|
73
|
+
- Type validation using Pydantic
|
74
|
+
- Support for both sync and async tools
|
75
|
+
- JSON schema generation for tool parameters
|
76
|
+
- Error handling and analytics tracking
|
77
|
+
- Tag-based tool organization
|
78
|
+
- Multiple format support (OpenAI, LangChain, MCP)
|
79
|
+
|
80
|
+
## Best Practices
|
81
|
+
|
82
|
+
1. Always provide clear docstrings for your tools
|
83
|
+
2. Use type hints for better validation
|
84
|
+
3. Handle errors appropriately in your tool implementations
|
85
|
+
4. Use tags to organize related tools
|
86
|
+
5. Consider async implementations for I/O-bound operations
|
universal_mcp/tools/tools.py
CHANGED
@@ -261,7 +261,7 @@ class ToolManager:
|
|
261
261
|
available_tool_functions = app.list_tools()
|
262
262
|
except TypeError as e:
|
263
263
|
logger.error(
|
264
|
-
f"Error calling list_tools for app '{app.name}'.
|
264
|
+
f"Error calling list_tools for app '{app.name}'. Error: {e}"
|
265
265
|
)
|
266
266
|
return
|
267
267
|
except Exception as e:
|
@@ -0,0 +1,90 @@
|
|
1
|
+
from loguru import logger
|
2
|
+
import os
|
3
|
+
import httpx
|
4
|
+
from universal_mcp.config import AppConfig
|
5
|
+
from universal_mcp.utils.singleton import Singleton
|
6
|
+
|
7
|
+
class AgentrClient(metaclass=Singleton):
|
8
|
+
"""Helper class for AgentR API operations.
|
9
|
+
|
10
|
+
This class provides utility methods for interacting with the AgentR API,
|
11
|
+
including authentication, authorization, and credential management.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
api_key (str, optional): AgentR API key. If not provided, will look for AGENTR_API_KEY env var
|
15
|
+
base_url (str, optional): Base URL for AgentR API. Defaults to https://api.agentr.dev
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self, api_key: str = None, base_url: str = None):
|
19
|
+
self.api_key = api_key or os.getenv("AGENTR_API_KEY")
|
20
|
+
if not self.api_key:
|
21
|
+
logger.error(
|
22
|
+
"API key for AgentR is missing. Please visit https://agentr.dev to create an API key, then set it as AGENTR_API_KEY environment variable."
|
23
|
+
)
|
24
|
+
raise ValueError("AgentR API key required - get one at https://agentr.dev")
|
25
|
+
self.base_url = (base_url or os.getenv("AGENTR_BASE_URL", "https://api.agentr.dev")).rstrip("/")
|
26
|
+
|
27
|
+
def get_credentials(self, integration_name: str) -> dict:
|
28
|
+
"""Get credentials for an integration from the AgentR API.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
integration_name (str): Name of the integration to get credentials for
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
dict: Credentials data from API response
|
35
|
+
|
36
|
+
Raises:
|
37
|
+
NotAuthorizedError: If credentials are not found (404 response)
|
38
|
+
HTTPError: For other API errors
|
39
|
+
"""
|
40
|
+
response = httpx.get(
|
41
|
+
f"{self.base_url}/api/{integration_name}/credentials/",
|
42
|
+
headers={"accept": "application/json", "X-API-KEY": self.api_key},
|
43
|
+
)
|
44
|
+
if response.status_code == 404:
|
45
|
+
logger.warning(
|
46
|
+
f"No credentials found for {integration_name}. Requesting authorization..."
|
47
|
+
)
|
48
|
+
action = self.get_authorization_url(integration_name)
|
49
|
+
raise NotAuthorizedError(action)
|
50
|
+
response.raise_for_status()
|
51
|
+
return response.json()
|
52
|
+
|
53
|
+
def get_authorization_url(self, integration_name: str) -> str:
|
54
|
+
"""Get authorization URL for an integration.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
integration_name (str): Name of the integration to get authorization URL for
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
str: Message containing authorization URL
|
61
|
+
|
62
|
+
Raises:
|
63
|
+
HTTPError: If API request fails
|
64
|
+
"""
|
65
|
+
response = httpx.get(
|
66
|
+
f"{self.base_url}/api/{integration_name}/authorize/",
|
67
|
+
headers={"X-API-KEY": self.api_key},
|
68
|
+
)
|
69
|
+
response.raise_for_status()
|
70
|
+
url = response.json()
|
71
|
+
return f"Please ask the user to visit the following url to authorize the application: {url}. Render the url in proper markdown format with a clickable link."
|
72
|
+
|
73
|
+
def fetch_apps(self) -> list[dict]:
|
74
|
+
"""Fetch available apps from AgentR API.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
List of application configurations
|
78
|
+
|
79
|
+
Raises:
|
80
|
+
httpx.HTTPError: If API request fails
|
81
|
+
"""
|
82
|
+
response = httpx.get(
|
83
|
+
f"{self.base_url}/api/apps/",
|
84
|
+
headers={"X-API-KEY": self.api_key},
|
85
|
+
timeout=10,
|
86
|
+
)
|
87
|
+
response.raise_for_status()
|
88
|
+
data = response.json()
|
89
|
+
return [AppConfig.model_validate(app) for app in data]
|
90
|
+
|