universal-mcp 0.1.14__py3-none-any.whl → 0.1.15__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 +48 -46
- universal_mcp/applications/application.py +249 -40
- universal_mcp/cli.py +49 -49
- universal_mcp/config.py +95 -22
- universal_mcp/exceptions.py +8 -0
- universal_mcp/integrations/integration.py +18 -2
- universal_mcp/logger.py +59 -8
- universal_mcp/servers/__init__.py +2 -2
- universal_mcp/stores/store.py +2 -12
- universal_mcp/tools/__init__.py +14 -2
- universal_mcp/tools/adapters.py +25 -0
- universal_mcp/tools/func_metadata.py +12 -2
- universal_mcp/tools/manager.py +236 -0
- universal_mcp/tools/tools.py +5 -249
- universal_mcp/utils/common.py +33 -0
- universal_mcp/utils/openapi/__inti__.py +0 -0
- universal_mcp/utils/{api_generator.py → openapi/api_generator.py} +1 -1
- universal_mcp/utils/openapi/openapi.py +930 -0
- universal_mcp/utils/openapi/preprocessor.py +1223 -0
- universal_mcp/utils/{readme.py → openapi/readme.py} +21 -31
- universal_mcp/utils/templates/README.md.j2 +17 -0
- {universal_mcp-0.1.14.dist-info → universal_mcp-0.1.15.dist-info}/METADATA +6 -3
- universal_mcp-0.1.15.dist-info/RECORD +44 -0
- universal_mcp-0.1.15.dist-info/licenses/LICENSE +21 -0
- universal_mcp/templates/README.md.j2 +0 -93
- universal_mcp/utils/dump_app_tools.py +0 -78
- universal_mcp/utils/openapi.py +0 -697
- universal_mcp-0.1.14.dist-info/RECORD +0 -39
- /universal_mcp/utils/{docgen.py → openapi/docgen.py} +0 -0
- /universal_mcp/{templates → utils/templates}/api_client.py.j2 +0 -0
- {universal_mcp-0.1.14.dist-info → universal_mcp-0.1.15.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.14.dist-info → universal_mcp-0.1.15.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import ast
|
1
2
|
import importlib
|
2
3
|
from pathlib import Path
|
3
4
|
|
@@ -6,10 +7,6 @@ from loguru import logger
|
|
6
7
|
|
7
8
|
|
8
9
|
def _import_class(module_path: str, class_name: str):
|
9
|
-
"""
|
10
|
-
Helper to import a class by name from a module.
|
11
|
-
Raises ModuleNotFoundError if module or class does not exist.
|
12
|
-
"""
|
13
10
|
try:
|
14
11
|
spec = importlib.util.spec_from_file_location("temp_module", module_path)
|
15
12
|
module = importlib.util.module_from_spec(spec)
|
@@ -26,35 +23,33 @@ def _import_class(module_path: str, class_name: str):
|
|
26
23
|
) from e
|
27
24
|
|
28
25
|
|
29
|
-
def
|
30
|
-
|
26
|
+
def _get_single_class_name(file_path: Path) -> str:
|
27
|
+
with open(file_path) as file:
|
28
|
+
tree = ast.parse(file.read(), filename=str(file_path))
|
29
|
+
class_defs = [
|
30
|
+
node.name for node in ast.walk(tree) if isinstance(node, ast.ClassDef)
|
31
|
+
]
|
32
|
+
if len(class_defs) == 1:
|
33
|
+
logger.info(f"Auto-detected class: {class_defs[0]}")
|
34
|
+
return class_defs[0]
|
35
|
+
elif len(class_defs) == 0:
|
36
|
+
raise ValueError(f"No class found in {file_path}")
|
37
|
+
else:
|
38
|
+
raise ValueError(f"Multiple classes found in {file_path}; please specify one.")
|
31
39
|
|
32
|
-
Args:
|
33
|
-
app_dir: Directory where the README will be generated
|
34
|
-
folder_name: Name of the application folder
|
35
|
-
tools: List of Function objects from the OpenAPI schema
|
36
40
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
Raises:
|
41
|
-
FileNotFoundError: If the template directory doesn't exist
|
42
|
-
TemplateError: If there's an error rendering the template
|
43
|
-
IOError: If there's an error writing the README file
|
44
|
-
"""
|
45
|
-
|
46
|
-
# Import the app
|
41
|
+
def generate_readme(app: Path) -> Path:
|
42
|
+
"""Generate README.md with API documentation from a file containing one class."""
|
43
|
+
class_name = _get_single_class_name(app)
|
47
44
|
app_instance = _import_class(app, class_name)(integration=None)
|
48
|
-
# List tools by calling list_tools()
|
49
45
|
tools = app_instance.list_tools()
|
50
|
-
|
46
|
+
|
51
47
|
formatted_tools = []
|
52
48
|
for tool in tools:
|
53
49
|
name = tool.__name__
|
54
50
|
description = tool.__doc__.strip().split("\n")[0]
|
55
51
|
formatted_tools.append((name, description))
|
56
|
-
|
57
|
-
# Set up Jinja2 environment
|
52
|
+
|
58
53
|
template_dir = Path(__file__).parent.parent / "templates"
|
59
54
|
if not template_dir.exists():
|
60
55
|
logger.error(f"Template directory not found: {template_dir}")
|
@@ -68,19 +63,14 @@ def generate_readme(app: Path, class_name: str) -> Path:
|
|
68
63
|
except Exception as e:
|
69
64
|
logger.error(f"Error loading template: {e}")
|
70
65
|
raise TemplateError(f"Error loading template: {e}") from e
|
71
|
-
|
66
|
+
|
72
67
|
try:
|
73
68
|
readme_content = template.render(name=class_name, tools=formatted_tools)
|
74
69
|
except Exception as e:
|
75
70
|
logger.error(f"Error rendering template: {e}")
|
76
71
|
raise TemplateError(f"Error rendering template: {e}") from e
|
77
72
|
|
78
|
-
|
79
|
-
app_dir = app.parent
|
80
|
-
readme_file = app_dir / "README.md"
|
81
|
-
|
82
|
-
# Write the README file
|
83
|
-
readme_file = app_dir / "README.md"
|
73
|
+
readme_file = app.parent / "README.md"
|
84
74
|
try:
|
85
75
|
with open(readme_file, "w") as f:
|
86
76
|
f.write(readme_content)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# {{ name }} MCP Server
|
2
|
+
|
3
|
+
An MCP Server for the {{ name }} API.
|
4
|
+
|
5
|
+
## 🛠️ Tool List
|
6
|
+
|
7
|
+
This is automatically generated from OpenAPI schema for the {{ name }} API.
|
8
|
+
|
9
|
+
{% if tools %}
|
10
|
+
| Tool | Description |
|
11
|
+
|------|-------------|
|
12
|
+
{%- for tool_name, tool_desc in tools %}
|
13
|
+
| `{{ tool_name }}` | {{ tool_desc }} |
|
14
|
+
{%- endfor %}
|
15
|
+
{% else %}
|
16
|
+
No tools with documentation were found in this API client.
|
17
|
+
{% endif %}
|
@@ -1,17 +1,20 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: universal-mcp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.15
|
4
4
|
Summary: Universal MCP acts as a middle ware for your API applications. It can store your credentials, authorize, enable disable apps on the fly and much more.
|
5
5
|
Author-email: Manoj Bajaj <manojbajaj95@gmail.com>
|
6
6
|
License: MIT
|
7
|
+
License-File: LICENSE
|
7
8
|
Requires-Python: >=3.11
|
8
9
|
Requires-Dist: cookiecutter>=2.6.0
|
9
10
|
Requires-Dist: gql[all]>=3.5.2
|
10
11
|
Requires-Dist: jinja2>=3.1.3
|
12
|
+
Requires-Dist: jsonref>=1.1.0
|
11
13
|
Requires-Dist: keyring>=25.6.0
|
14
|
+
Requires-Dist: langchain-mcp-adapters>=0.0.3
|
12
15
|
Requires-Dist: litellm>=1.30.7
|
13
16
|
Requires-Dist: loguru>=0.7.3
|
14
|
-
Requires-Dist: mcp>=1.
|
17
|
+
Requires-Dist: mcp>=1.7.1
|
15
18
|
Requires-Dist: posthog>=3.24.0
|
16
19
|
Requires-Dist: pydantic-settings>=2.8.1
|
17
20
|
Requires-Dist: pydantic>=2.11.1
|
@@ -26,10 +29,10 @@ Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
|
|
26
29
|
Requires-Dist: pytest>=8.3.5; extra == 'dev'
|
27
30
|
Requires-Dist: ruff>=0.11.4; extra == 'dev'
|
28
31
|
Provides-Extra: playground
|
29
|
-
Requires-Dist: langchain-mcp-adapters>=0.0.3; extra == 'playground'
|
30
32
|
Requires-Dist: langchain-openai>=0.3.12; extra == 'playground'
|
31
33
|
Requires-Dist: langgraph-checkpoint-sqlite>=2.0.6; extra == 'playground'
|
32
34
|
Requires-Dist: langgraph>=0.3.24; extra == 'playground'
|
35
|
+
Requires-Dist: litellm>=1.30.7; extra == 'playground'
|
33
36
|
Requires-Dist: python-dotenv>=1.0.1; extra == 'playground'
|
34
37
|
Requires-Dist: streamlit>=1.44.1; extra == 'playground'
|
35
38
|
Requires-Dist: watchdog>=6.0.0; extra == 'playground'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
universal_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
universal_mcp/analytics.py,sha256=Dkv8mkc_2T2t5NxLSZzcr3BlmOispj1RKtbB86V1i4M,2306
|
3
|
+
universal_mcp/cli.py,sha256=fqB4CzcA2CAg1m4tzhK0k9X4otHWxaAy2edP1QQYUIQ,8974
|
4
|
+
universal_mcp/config.py,sha256=xqz5VNxtk6Clepjw-aK-HrgMFQLzFuxiDb1fuHGpbxE,3717
|
5
|
+
universal_mcp/exceptions.py,sha256=2A_aIzcwjB99tR4xCFjPkWPf10rBK_785FerJI7Tzns,564
|
6
|
+
universal_mcp/logger.py,sha256=JtAC8ImO74lvt5xepV3W5BIz-u3nZOAY1ecdhmQhub0,2081
|
7
|
+
universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
universal_mcp/applications/README.md,sha256=eqbizxaTxKH2O1tyIJR2yI0Db5TQxtgPd_vbpWyCa2Y,3527
|
9
|
+
universal_mcp/applications/__init__.py,sha256=zyV_0GyunGBA5OEO1FQsTOsj34SYjnERu5dDJLxd6c0,3133
|
10
|
+
universal_mcp/applications/application.py,sha256=9PfmvVx1csmy0RzaTC71S4DkMD8VxvY3yIsMAt3dQLw,14425
|
11
|
+
universal_mcp/integrations/README.md,sha256=lTAPXO2nivcBe1q7JT6PRa6v9Ns_ZersQMIdw-nmwEA,996
|
12
|
+
universal_mcp/integrations/__init__.py,sha256=tg6Yk59AEhwPsrTp0hZQ3NBfmJuYGu2sNCOXuph-h9k,922
|
13
|
+
universal_mcp/integrations/integration.py,sha256=4SRcD78ZF9-m65aTxkoF-CZl7XKUsfSr1wppKz_ZgiA,13078
|
14
|
+
universal_mcp/servers/README.md,sha256=ytFlgp8-LO0oogMrHkMOp8SvFTwgsKgv7XhBVZGNTbM,2284
|
15
|
+
universal_mcp/servers/__init__.py,sha256=etFrqXFODIl9oGeqQS-aUxzw4J6pjzYjHl4VPvQaR3A,508
|
16
|
+
universal_mcp/servers/server.py,sha256=0oJQQUiwPdG2q79tzsVv3WPMV5YIFbF14PRvBF-SxMQ,9395
|
17
|
+
universal_mcp/stores/README.md,sha256=jrPh_ow4ESH4BDGaSafilhOVaN8oQ9IFlFW-j5Z5hLA,2465
|
18
|
+
universal_mcp/stores/__init__.py,sha256=quvuwhZnpiSLuojf0NfmBx2xpaCulv3fbKtKaSCEmuM,603
|
19
|
+
universal_mcp/stores/store.py,sha256=mxnmOVlDNrr8OKhENWDtCIfK7YeCBQcGdS6I2ogRCsU,6756
|
20
|
+
universal_mcp/tools/README.md,sha256=RuxliOFqV1ZEyeBdj3m8UKfkxAsfrxXh-b6V4ZGAk8I,2468
|
21
|
+
universal_mcp/tools/__init__.py,sha256=Fatza_R0qYWmNF1WQSfUZZKQFu5qf-16JhZzdmyx3KY,333
|
22
|
+
universal_mcp/tools/adapters.py,sha256=gz_sNDc_bseMHWmpQmqhOq65veE-DuK_kJYXGIx0Wi8,1427
|
23
|
+
universal_mcp/tools/func_metadata.py,sha256=4LrOeQ81RnfWjPp9N-tOkw-nr6XosRw6P0EUriNhpu4,8328
|
24
|
+
universal_mcp/tools/manager.py,sha256=OArAbckBay5Lbpu2-oI38zEEbJyM8E_WLB8985B6a70,7718
|
25
|
+
universal_mcp/tools/tools.py,sha256=lg1rM9644OLAmi-yXar-olPCAfsu1ol2Aw9XRuIN7Fk,3383
|
26
|
+
universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
|
27
|
+
universal_mcp/utils/agentr.py,sha256=3sobve7Odk8pIAZm3RHTX4Rc21rkBClcXQgXXslbSUA,3490
|
28
|
+
universal_mcp/utils/common.py,sha256=F2Teso1Cs4E1dgMq0ESLJOWJ0n9arLX-YuawfYfZg3I,911
|
29
|
+
universal_mcp/utils/docstring_parser.py,sha256=j7aE-LLnBOPTJI0qXayf0NlYappzxICv5E_hUPNmAlc,11459
|
30
|
+
universal_mcp/utils/installation.py,sha256=1n5X_aIiuY8WNQn6Oji_gZ-aiRmNXxrg-qYRv-pGjxw,10195
|
31
|
+
universal_mcp/utils/singleton.py,sha256=kolHnbS9yd5C7z-tzaUAD16GgI-thqJXysNi3sZM4No,733
|
32
|
+
universal_mcp/utils/openapi/__inti__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
|
+
universal_mcp/utils/openapi/api_generator.py,sha256=aNzrcglYXL6mQizFQUuiFFKTPuEPgTC1unr6NT_U0VM,4771
|
34
|
+
universal_mcp/utils/openapi/docgen.py,sha256=zPmZ-b-fK6xk-dwHEx2hwShN-iquPD_O15CGuPwlj2k,21870
|
35
|
+
universal_mcp/utils/openapi/openapi.py,sha256=PeCj01skdvBaYvGedSShgUrzZ2GhEpPX8IOyTvq-ubk,37284
|
36
|
+
universal_mcp/utils/openapi/preprocessor.py,sha256=zMCsHv9lNIrIlAMyF39cIm0-c2vydrtbtQjJ0rjcLL8,48765
|
37
|
+
universal_mcp/utils/openapi/readme.py,sha256=wTg2D0sPKppPmGn7b-vtD5hAGRv8DLEAuxMNzqBeuEs,3032
|
38
|
+
universal_mcp/utils/templates/README.md.j2,sha256=Mrm181YX-o_-WEfKs01Bi2RJy43rBiq2j6fTtbWgbTA,401
|
39
|
+
universal_mcp/utils/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
|
40
|
+
universal_mcp-0.1.15.dist-info/METADATA,sha256=iHo5gK5ZOO1OoNOYLAalp3X1qnH3JBsEpkp9YqXBiKM,9883
|
41
|
+
universal_mcp-0.1.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
42
|
+
universal_mcp-0.1.15.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
|
43
|
+
universal_mcp-0.1.15.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
|
44
|
+
universal_mcp-0.1.15.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Agentr
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -1,93 +0,0 @@
|
|
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
|
@@ -1,78 +0,0 @@
|
|
1
|
-
import csv
|
2
|
-
from pathlib import Path
|
3
|
-
|
4
|
-
from universal_mcp.applications import app_from_slug
|
5
|
-
|
6
|
-
|
7
|
-
def discover_available_app_slugs():
|
8
|
-
apps_dir = Path(__file__).resolve().parent.parent / "applications"
|
9
|
-
app_slugs = []
|
10
|
-
|
11
|
-
for item in apps_dir.iterdir():
|
12
|
-
if not item.is_dir() or item.name.startswith("_"):
|
13
|
-
continue
|
14
|
-
|
15
|
-
if (item / "app.py").exists():
|
16
|
-
slug = item.name.replace("_", "-")
|
17
|
-
app_slugs.append(slug)
|
18
|
-
|
19
|
-
return app_slugs
|
20
|
-
|
21
|
-
|
22
|
-
def extract_app_tools(app_slugs):
|
23
|
-
all_apps_tools = []
|
24
|
-
|
25
|
-
for slug in app_slugs:
|
26
|
-
try:
|
27
|
-
print(f"Loading app: {slug}")
|
28
|
-
app_class = app_from_slug(slug)
|
29
|
-
|
30
|
-
app_instance = app_class(integration=None)
|
31
|
-
|
32
|
-
tools = app_instance.list_tools()
|
33
|
-
|
34
|
-
for tool in tools:
|
35
|
-
tool_name = tool.__name__
|
36
|
-
description = (
|
37
|
-
tool.__doc__.strip().split("\n")[0]
|
38
|
-
if tool.__doc__
|
39
|
-
else "No description"
|
40
|
-
)
|
41
|
-
|
42
|
-
all_apps_tools.append(
|
43
|
-
{
|
44
|
-
"app_name": slug,
|
45
|
-
"tool_name": tool_name,
|
46
|
-
"description": description,
|
47
|
-
}
|
48
|
-
)
|
49
|
-
|
50
|
-
except Exception as e:
|
51
|
-
print(f"Error loading app {slug}: {e}")
|
52
|
-
|
53
|
-
return all_apps_tools
|
54
|
-
|
55
|
-
|
56
|
-
def write_to_csv(app_tools, output_file="app_tools.csv"):
|
57
|
-
fieldnames = ["app_name", "tool_name", "description"]
|
58
|
-
|
59
|
-
with open(output_file, "w", newline="") as csvfile:
|
60
|
-
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
61
|
-
writer.writeheader()
|
62
|
-
writer.writerows(app_tools)
|
63
|
-
|
64
|
-
print(f"CSV file created: {output_file}")
|
65
|
-
|
66
|
-
|
67
|
-
def main():
|
68
|
-
app_slugs = discover_available_app_slugs()
|
69
|
-
print(f"Found {len(app_slugs)} app slugs: {', '.join(app_slugs)}")
|
70
|
-
|
71
|
-
app_tools = extract_app_tools(app_slugs)
|
72
|
-
print(f"Extracted {len(app_tools)} tools from all apps")
|
73
|
-
|
74
|
-
write_to_csv(app_tools)
|
75
|
-
|
76
|
-
|
77
|
-
if __name__ == "__main__":
|
78
|
-
main()
|