universal-mcp 0.1.23rc2__py3-none-any.whl → 0.1.24rc3__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/agentr/__init__.py +6 -0
- universal_mcp/agentr/agentr.py +30 -0
- universal_mcp/{utils/agentr.py → agentr/client.py} +22 -7
- universal_mcp/agentr/integration.py +104 -0
- universal_mcp/agentr/registry.py +91 -0
- universal_mcp/agentr/server.py +51 -0
- universal_mcp/agents/__init__.py +6 -0
- universal_mcp/agents/auto.py +576 -0
- universal_mcp/agents/base.py +88 -0
- universal_mcp/agents/cli.py +27 -0
- universal_mcp/agents/codeact/__init__.py +243 -0
- universal_mcp/agents/codeact/sandbox.py +27 -0
- universal_mcp/agents/codeact/test.py +15 -0
- universal_mcp/agents/codeact/utils.py +61 -0
- universal_mcp/agents/hil.py +104 -0
- universal_mcp/agents/llm.py +10 -0
- universal_mcp/agents/react.py +58 -0
- universal_mcp/agents/simple.py +40 -0
- universal_mcp/agents/utils.py +111 -0
- universal_mcp/analytics.py +44 -14
- universal_mcp/applications/__init__.py +42 -75
- universal_mcp/applications/application.py +187 -133
- universal_mcp/applications/sample/app.py +245 -0
- universal_mcp/cli.py +14 -231
- universal_mcp/client/oauth.py +122 -18
- universal_mcp/client/token_store.py +62 -3
- universal_mcp/client/{client.py → transport.py} +127 -48
- universal_mcp/config.py +189 -49
- universal_mcp/exceptions.py +54 -6
- universal_mcp/integrations/__init__.py +0 -18
- universal_mcp/integrations/integration.py +185 -168
- universal_mcp/servers/__init__.py +2 -14
- universal_mcp/servers/server.py +84 -258
- universal_mcp/stores/store.py +126 -93
- universal_mcp/tools/__init__.py +3 -0
- universal_mcp/tools/adapters.py +20 -11
- universal_mcp/tools/func_metadata.py +1 -1
- universal_mcp/tools/manager.py +38 -53
- universal_mcp/tools/registry.py +41 -0
- universal_mcp/tools/tools.py +24 -3
- universal_mcp/types.py +10 -0
- universal_mcp/utils/common.py +245 -0
- universal_mcp/utils/installation.py +3 -4
- universal_mcp/utils/openapi/api_generator.py +71 -17
- universal_mcp/utils/openapi/api_splitter.py +0 -1
- universal_mcp/utils/openapi/cli.py +669 -0
- universal_mcp/utils/openapi/filters.py +114 -0
- universal_mcp/utils/openapi/openapi.py +315 -23
- universal_mcp/utils/openapi/postprocessor.py +275 -0
- universal_mcp/utils/openapi/preprocessor.py +63 -8
- universal_mcp/utils/openapi/test_generator.py +287 -0
- universal_mcp/utils/prompts.py +634 -0
- universal_mcp/utils/singleton.py +4 -1
- universal_mcp/utils/testing.py +196 -8
- universal_mcp-0.1.24rc3.dist-info/METADATA +68 -0
- universal_mcp-0.1.24rc3.dist-info/RECORD +70 -0
- universal_mcp/applications/README.md +0 -122
- universal_mcp/client/__main__.py +0 -30
- universal_mcp/client/agent.py +0 -96
- universal_mcp/integrations/README.md +0 -25
- universal_mcp/servers/README.md +0 -79
- universal_mcp/stores/README.md +0 -74
- universal_mcp/tools/README.md +0 -86
- universal_mcp-0.1.23rc2.dist-info/METADATA +0 -283
- universal_mcp-0.1.23rc2.dist-info/RECORD +0 -51
- /universal_mcp/{utils → tools}/docstring_parser.py +0 -0
- {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc3.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc3.dist-info}/entry_points.txt +0 -0
- {universal_mcp-0.1.23rc2.dist-info → universal_mcp-0.1.24rc3.dist-info}/licenses/LICENSE +0 -0
universal_mcp/utils/testing.py
CHANGED
@@ -1,13 +1,48 @@
|
|
1
|
+
import os
|
2
|
+
from dataclasses import dataclass
|
3
|
+
|
4
|
+
from langchain_core.messages import AIMessage, HumanMessage
|
5
|
+
from langgraph.prebuilt import create_react_agent
|
1
6
|
from loguru import logger
|
7
|
+
from pydantic import BaseModel, SecretStr
|
8
|
+
|
9
|
+
from universal_mcp.agentr import AgentrIntegration
|
10
|
+
from universal_mcp.agentr.client import AgentrClient
|
11
|
+
from universal_mcp.applications import APIApplication, BaseApplication
|
12
|
+
from universal_mcp.tools import Tool, ToolManager
|
13
|
+
from universal_mcp.types import ToolFormat
|
14
|
+
|
15
|
+
|
16
|
+
class ValidateResult(BaseModel):
|
17
|
+
success: bool
|
18
|
+
reasoning: str
|
19
|
+
|
20
|
+
|
21
|
+
def check_application_instance(app_instance: BaseApplication, app_name: str):
|
22
|
+
"""
|
23
|
+
Performs a series of assertions to validate an application instance and its tools.
|
2
24
|
|
3
|
-
|
25
|
+
This function checks for the following:
|
26
|
+
- The application instance is not None.
|
27
|
+
- The application instance's name matches the expected application name.
|
28
|
+
- The application has at least one tool.
|
29
|
+
- Each tool has a non-None name with a valid length (1-47 characters).
|
30
|
+
- Each tool has a non-None description.
|
31
|
+
- All tool names are unique within the application.
|
32
|
+
- The application has at least one tool tagged as "important".
|
4
33
|
|
34
|
+
Args:
|
35
|
+
app_instance: The application instance to check. Must be an instance of BaseApplication.
|
36
|
+
app_name: The expected name of the application.
|
37
|
+
|
38
|
+
Raises:
|
39
|
+
AssertionError: If any of the validation checks fail.
|
40
|
+
"""
|
5
41
|
|
6
|
-
def check_application_instance(app_instance, app_name):
|
7
42
|
assert app_instance is not None, f"Application object is None for {app_name}"
|
8
|
-
assert (
|
9
|
-
app_instance.name
|
10
|
-
)
|
43
|
+
assert app_instance.name == app_name, (
|
44
|
+
f"Application instance name '{app_instance.name}' does not match expected name '{app_name}'"
|
45
|
+
)
|
11
46
|
|
12
47
|
tools = app_instance.list_tools()
|
13
48
|
logger.info(f"Tools for {app_name}: {len(tools)}")
|
@@ -19,9 +54,9 @@ def check_application_instance(app_instance, app_name):
|
|
19
54
|
|
20
55
|
for tool in tools:
|
21
56
|
assert tool.name is not None, f"Tool name is None for a tool in {app_name}"
|
22
|
-
assert (
|
23
|
-
|
24
|
-
)
|
57
|
+
assert 0 < len(tool.name) <= 48, (
|
58
|
+
f"Tool name '{tool.name}' for {app_name} has invalid length (must be between 1 and 47 characters)"
|
59
|
+
)
|
25
60
|
assert tool.description is not None, f"Tool description is None for tool '{tool.name}' in {app_name}"
|
26
61
|
# assert 0 < len(tool.description) <= 255, f"Tool description for '{tool.name}' in {app_name} has invalid length (must be between 1 and 255 characters)"
|
27
62
|
assert tool.name not in seen_names, f"Duplicate tool name: '{tool.name}' found for {app_name}"
|
@@ -29,3 +64,156 @@ def check_application_instance(app_instance, app_name):
|
|
29
64
|
if "important" in tool.tags:
|
30
65
|
important_tools.append(tool.name)
|
31
66
|
assert len(important_tools) > 0, f"No important tools found for {app_name}"
|
67
|
+
|
68
|
+
|
69
|
+
@dataclass
|
70
|
+
class AutomationTestCase:
|
71
|
+
"""Generic test case for automation testing."""
|
72
|
+
|
73
|
+
app: str
|
74
|
+
app_instance: APIApplication | None = None
|
75
|
+
tools: list[str] | None = None
|
76
|
+
tasks: list[str] | None = None
|
77
|
+
validate_query: str | None = None
|
78
|
+
|
79
|
+
|
80
|
+
def create_agentr_client(app_name: str) -> AgentrClient:
|
81
|
+
"""
|
82
|
+
Create an AgentrClient with appropriate API key and base URL.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
app_name: Name of the application (used for app-specific environment variables)
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
AgentrClient instance
|
89
|
+
"""
|
90
|
+
api_key = os.environ.get(f"{app_name.upper()}_API_KEY") or os.environ.get("AGENTR_API_KEY")
|
91
|
+
base_url = os.environ.get(f"{app_name.upper()}_BASE_URL") or os.environ.get(
|
92
|
+
"AGENTR_BASE_URL", "https://api.agentr.dev"
|
93
|
+
)
|
94
|
+
return AgentrClient(api_key=api_key, base_url=base_url)
|
95
|
+
|
96
|
+
|
97
|
+
def create_integration(app_name: str) -> AgentrIntegration:
|
98
|
+
"""
|
99
|
+
Create an AgentRIntegration instance with appropriate client.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
app_name: Name of the application
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
AgentRIntegration instance
|
106
|
+
"""
|
107
|
+
client = create_agentr_client(app_name)
|
108
|
+
return AgentrIntegration(name=app_name, client=client)
|
109
|
+
|
110
|
+
|
111
|
+
def create_app_with_integration(app_name: str, app_class: type[APIApplication]) -> APIApplication:
|
112
|
+
"""
|
113
|
+
Create an application instance with integration.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
app_name: Name of the application
|
117
|
+
app_class: Class of the application to instantiate
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
Application instance with integration
|
121
|
+
"""
|
122
|
+
integration = create_integration(app_name)
|
123
|
+
return app_class(integration=integration)
|
124
|
+
|
125
|
+
|
126
|
+
def load_app_with_integration(app_name: str, app_class: type[APIApplication]) -> APIApplication:
|
127
|
+
"""
|
128
|
+
Load application instance with real integration.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
app_name: Name of the application
|
132
|
+
app_class: Class of the application to instantiate
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Instantiated application with integration
|
136
|
+
"""
|
137
|
+
integration = create_integration(app_name)
|
138
|
+
return app_class(integration=integration)
|
139
|
+
|
140
|
+
|
141
|
+
async def execute_automation_test(test_case: AutomationTestCase, app_instance: APIApplication | None = None) -> None:
|
142
|
+
"""
|
143
|
+
Execute an automation test case using LangGraph ReAct agent.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
test_case: Test case to execute
|
147
|
+
app_instance: The application instance to test (optional if provided in test_case)
|
148
|
+
"""
|
149
|
+
tool_manager = ToolManager()
|
150
|
+
|
151
|
+
if app_instance is None:
|
152
|
+
app_instance = test_case.app_instance
|
153
|
+
if app_instance is None:
|
154
|
+
raise ValueError("No app_instance provided in test_case or as parameter")
|
155
|
+
|
156
|
+
all_tools = app_instance.list_tools()
|
157
|
+
logger.info(f"Available tools from app: {[getattr(t, '__name__', str(t)) for t in all_tools]}")
|
158
|
+
|
159
|
+
tool_manager.register_tools_from_app(app_instance)
|
160
|
+
|
161
|
+
all_registered = tool_manager.get_tools_by_app(app_name=app_instance.name)
|
162
|
+
logger.info(f"All registered tools: {[t.name for t in all_registered]}")
|
163
|
+
|
164
|
+
if test_case.tools:
|
165
|
+
tools = tool_manager.list_tools(
|
166
|
+
format=ToolFormat.LANGCHAIN, app_name=app_instance.name, tool_names=test_case.tools
|
167
|
+
)
|
168
|
+
else:
|
169
|
+
tools = tool_manager.list_tools(format=ToolFormat.LANGCHAIN, app_name=app_instance.name)
|
170
|
+
|
171
|
+
logger.info(f"Tools for test: {[tool.name for tool in tools]}")
|
172
|
+
|
173
|
+
azure_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
|
174
|
+
azure_api_key = os.environ.get("AZURE_OPENAI_API_KEY")
|
175
|
+
azure_deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT", "o4-mini")
|
176
|
+
api_version = os.environ.get("AZURE_OPENAI_API_VERSION", "2025-03-01-preview")
|
177
|
+
|
178
|
+
if not azure_endpoint or not azure_api_key:
|
179
|
+
raise ValueError(
|
180
|
+
"Azure OpenAI credentials not found. Please set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY environment variables."
|
181
|
+
)
|
182
|
+
|
183
|
+
from langchain_openai import AzureChatOpenAI
|
184
|
+
|
185
|
+
llm = AzureChatOpenAI(
|
186
|
+
azure_endpoint=azure_endpoint,
|
187
|
+
azure_deployment=azure_deployment,
|
188
|
+
api_key=SecretStr(azure_api_key) if azure_api_key else None,
|
189
|
+
api_version=api_version,
|
190
|
+
)
|
191
|
+
logger.info(f"Using Azure OpenAI with deployment: {azure_deployment}")
|
192
|
+
|
193
|
+
agent = create_react_agent(
|
194
|
+
model=llm,
|
195
|
+
tools=tools,
|
196
|
+
)
|
197
|
+
|
198
|
+
messages = []
|
199
|
+
for task in test_case.tasks or []:
|
200
|
+
try:
|
201
|
+
messages.append(HumanMessage(content=task))
|
202
|
+
response = await agent.ainvoke({"messages": messages})
|
203
|
+
messages.append(AIMessage(content=response["messages"][-1].content))
|
204
|
+
logger.info(f"Task: {task}")
|
205
|
+
logger.info(f"Response: {response['messages'][-1].content}")
|
206
|
+
logger.info("---")
|
207
|
+
except Exception as e:
|
208
|
+
logger.error(f"Error: {e}")
|
209
|
+
import traceback
|
210
|
+
|
211
|
+
traceback.print_exc()
|
212
|
+
raise AssertionError(f"Task execution failed: {e}") from e
|
213
|
+
|
214
|
+
if test_case.validate_query:
|
215
|
+
messages.append(HumanMessage(content=test_case.validate_query))
|
216
|
+
structured_llm = llm.with_structured_output(ValidateResult)
|
217
|
+
result = await structured_llm.ainvoke(messages)
|
218
|
+
logger.info(f"Validation result: {result}")
|
219
|
+
assert result.success, f"Validation failed: {result.reasoning}"
|
@@ -0,0 +1,68 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: universal-mcp
|
3
|
+
Version: 0.1.24rc3
|
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
|
+
Author-email: Manoj Bajaj <manojbajaj95@gmail.com>
|
6
|
+
License: MIT
|
7
|
+
License-File: LICENSE
|
8
|
+
Requires-Python: >=3.11
|
9
|
+
Requires-Dist: black>=25.1.0
|
10
|
+
Requires-Dist: cookiecutter>=2.6.0
|
11
|
+
Requires-Dist: gql[all]>=3.5.2
|
12
|
+
Requires-Dist: jinja2>=3.1.3
|
13
|
+
Requires-Dist: jsonref>=1.1.0
|
14
|
+
Requires-Dist: keyring>=25.6.0
|
15
|
+
Requires-Dist: langchain-mcp-adapters>=0.1.9
|
16
|
+
Requires-Dist: langchain-openai>=0.3.27
|
17
|
+
Requires-Dist: langgraph-cli[inmem]>=0.3.4
|
18
|
+
Requires-Dist: langgraph>=0.5.2
|
19
|
+
Requires-Dist: langsmith>=0.4.5
|
20
|
+
Requires-Dist: loguru>=0.7.3
|
21
|
+
Requires-Dist: mcp>=1.10.0
|
22
|
+
Requires-Dist: mkdocs-material>=9.6.15
|
23
|
+
Requires-Dist: mkdocs>=1.6.1
|
24
|
+
Requires-Dist: posthog>=3.24.0
|
25
|
+
Requires-Dist: pydantic-settings>=2.8.1
|
26
|
+
Requires-Dist: pydantic>=2.11.1
|
27
|
+
Requires-Dist: pyyaml>=6.0.2
|
28
|
+
Requires-Dist: rich>=14.0.0
|
29
|
+
Requires-Dist: streamlit>=1.46.1
|
30
|
+
Requires-Dist: ty>=0.0.1a17
|
31
|
+
Requires-Dist: typer>=0.15.2
|
32
|
+
Provides-Extra: dev
|
33
|
+
Requires-Dist: litellm>=1.30.7; extra == 'dev'
|
34
|
+
Requires-Dist: mypy>=1.16.0; extra == 'dev'
|
35
|
+
Requires-Dist: pre-commit>=4.2.0; extra == 'dev'
|
36
|
+
Requires-Dist: pyright>=1.1.398; extra == 'dev'
|
37
|
+
Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
|
38
|
+
Requires-Dist: pytest>=8.3.5; extra == 'dev'
|
39
|
+
Requires-Dist: ruff>=0.11.4; extra == 'dev'
|
40
|
+
Provides-Extra: docs
|
41
|
+
Requires-Dist: mkdocs-glightbox>=0.4.0; extra == 'docs'
|
42
|
+
Requires-Dist: mkdocs-material[imaging]>=9.5.45; extra == 'docs'
|
43
|
+
Requires-Dist: mkdocs>=1.6.1; extra == 'docs'
|
44
|
+
Requires-Dist: mkdocstrings-python>=1.12.2; extra == 'docs'
|
45
|
+
Description-Content-Type: text/markdown
|
46
|
+
|
47
|
+
# Universal MCP
|
48
|
+
|
49
|
+
Universal MCP acts as a middleware layer for your API applications, enabling seamless integration with various services through the Model Control Protocol (MCP). It simplifies credential management, authorization, dynamic app enablement, and provides a robust framework for building and managing AI-powered tools.
|
50
|
+
|
51
|
+
## Documentation
|
52
|
+
|
53
|
+
The primary documentation for Universal MCP is available in `/docs` folder in this repository.
|
54
|
+
|
55
|
+
Please refer to the following for more detailed information:
|
56
|
+
|
57
|
+
* **[Main Documentation](docs/index.md)**: For an overview of the project, features, quick start, and installation.
|
58
|
+
* **[Playground Usage](docs/playground.md)**: For instructions on using the interactive playground.
|
59
|
+
* **[Applications Framework](docs/applications.md)**: For details on the applications module.
|
60
|
+
* **[Integrations & Authentication](docs/integrations.md)**: For information on integration types.
|
61
|
+
* **[Server Implementations](docs/servers.md)**: For details on server types.
|
62
|
+
* **[Credential Stores](docs/stores.md)**: For information on credential stores.
|
63
|
+
* **[Tools Framework](docs/tools_framework.md)**: For details on the tool management system.
|
64
|
+
* **[Contributing Guidelines](CONTRIBUTING.md)**: For information on how to contribute to the project.
|
65
|
+
|
66
|
+
## License
|
67
|
+
|
68
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
@@ -0,0 +1,70 @@
|
|
1
|
+
universal_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
universal_mcp/analytics.py,sha256=RzS88HSvJRGMjdJeLHnOgWzfKSb1jVnvOcD7NHqfERw,3733
|
3
|
+
universal_mcp/cli.py,sha256=pPnIWLhSrLV9ukI8YAg2znehCR3VovhEkmh8XkRT3MU,2505
|
4
|
+
universal_mcp/config.py,sha256=pkKs0gST65umzmNEvjHiOAtmiBaaICi45WG4Z0My0ak,11983
|
5
|
+
universal_mcp/exceptions.py,sha256=Uen8UFgLHGlSwXgRUyF-nhqTwdiBuL3okgBVRV2AgtA,2150
|
6
|
+
universal_mcp/logger.py,sha256=VmH_83efpErLEDTJqz55Dp0dioTXfGvMBLZUx5smOLc,2116
|
7
|
+
universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
universal_mcp/types.py,sha256=dVK7uSMuhvx5Xk6L7GGdjeaAIKiEwQskTmaVFwIS8LQ,176
|
9
|
+
universal_mcp/agentr/__init__.py,sha256=ogOhH_OJwkoUZu_2nQJc7-vEGmYQxEjOE511-6ubrX0,217
|
10
|
+
universal_mcp/agentr/agentr.py,sha256=JfawuREfXAyeNUE7o58DzTPhmQXuwsB_Da7c1Gf3Qxw,1059
|
11
|
+
universal_mcp/agentr/client.py,sha256=oyF6VKq56UMVf5L1WnFTMSZ85W8Qcy-5HZ5XOGiIELM,4139
|
12
|
+
universal_mcp/agentr/integration.py,sha256=V5GjqocqS02tRoI8MeV9PL6m-BzejwBzgJhOHo4MxAE,4212
|
13
|
+
universal_mcp/agentr/registry.py,sha256=b9sr5JyT3HLj3e7GFpdXpT7ofGwLQc--y8k2DqF5dE0,3542
|
14
|
+
universal_mcp/agentr/server.py,sha256=bIPmHMiKKwnUYnxmfZVRh1thcn7Rytm_-bNiXTfANzc,2098
|
15
|
+
universal_mcp/agents/__init__.py,sha256=vgixOLTCcCmSweENV7GSAuOPyHXlE4XAbvOXyr4MrRA,185
|
16
|
+
universal_mcp/agents/auto.py,sha256=o__71BCOHSfaj7Xt0PhsamVXdeP4o7irhtmu1q6-3Fo,25336
|
17
|
+
universal_mcp/agents/base.py,sha256=U5JtpOopmUi73qcxtY9T2qJpYD7e6c62iVlIr3m5Chc,3430
|
18
|
+
universal_mcp/agents/cli.py,sha256=7GdRBpu9rhZPiC2vaNQXWI7K-0yCnvdlmE0IFpvy2Gk,539
|
19
|
+
universal_mcp/agents/hil.py,sha256=CTgX7CoFEyTFIaNaL-id2WALOPd0VBb79pHkQK8quM8,3671
|
20
|
+
universal_mcp/agents/llm.py,sha256=YNxN43bVhGfdYs09yPkdkGCKJkj-2UNqkB1EFmtnUS4,309
|
21
|
+
universal_mcp/agents/react.py,sha256=6L--LcuU5Ityi2UiZSYJWgp-lXGkxvpsx8mjvpoNRBQ,2021
|
22
|
+
universal_mcp/agents/simple.py,sha256=UfmQIIff--_Y0DQ6oivRciHqSZvRqy_qwQn_UYVzYy8,1146
|
23
|
+
universal_mcp/agents/utils.py,sha256=7kwFpD0Rv6JqHG-LlNCVwSu_xRX-N119mUmiBroHJL4,4109
|
24
|
+
universal_mcp/agents/codeact/__init__.py,sha256=5D_I3lI_3tWjZERRoFav_bPe9UDaJ53pDzZYtyixg3E,10097
|
25
|
+
universal_mcp/agents/codeact/sandbox.py,sha256=lGRzhuXTHCB1qauuOI3bH1-fPTsyL6Lf9EmMIz4C2xQ,1039
|
26
|
+
universal_mcp/agents/codeact/test.py,sha256=bva-KkBNbGZn2f9nmmo9SNPQnY24Ni5gLHhJ5I0cm0k,481
|
27
|
+
universal_mcp/agents/codeact/utils.py,sha256=VuMvLTxBBh3pgaJk8RWj5AK8XZFF-1gnZJ6jFLeM_CI,1690
|
28
|
+
universal_mcp/applications/__init__.py,sha256=HrCnGdAT7w4puw2_VulBfjOLku9D5DuMaOwAuQzu6nI,2067
|
29
|
+
universal_mcp/applications/application.py,sha256=pGF9Rb2D6qzlaSwlcfZ-dNqPtsLkQTqL3jpsRuJ6-qE,23835
|
30
|
+
universal_mcp/applications/sample/app.py,sha256=E0JwaWD7qytwawb_iWc1pBnJ-Te7MMtab4MxOOebLdc,8972
|
31
|
+
universal_mcp/client/oauth.py,sha256=O00zOUfQxINaruFU2zt-64DIR1_mAqrY8ykLQo-teJU,8679
|
32
|
+
universal_mcp/client/token_store.py,sha256=6VAzjzJG49wYvmEDqksFvb-fVqdjHIKWv7yYyh_AuF8,3912
|
33
|
+
universal_mcp/client/transport.py,sha256=xgAKBJ1-yCcTtl9cxzJgRn6to5Y9EvCwLc_WpDck3Dg,11838
|
34
|
+
universal_mcp/integrations/__init__.py,sha256=tfzLyPEPly5tfIcT8K6-oKCr_MEFGxOROHy_NeVy0KM,200
|
35
|
+
universal_mcp/integrations/integration.py,sha256=H-hOoDHqk78A4Fi_TGN7OOFS7PDfqXK_nedH8iSz-6A,16459
|
36
|
+
universal_mcp/servers/__init__.py,sha256=speBb_E94UJa4A6Fv8RHFeoJ7cR-q2bCMtKV7R21P5w,142
|
37
|
+
universal_mcp/servers/server.py,sha256=qHeHm4UFVUr8TAailbEkWGq7EdlOASkOevY_0lyrWWs,5882
|
38
|
+
universal_mcp/stores/__init__.py,sha256=quvuwhZnpiSLuojf0NfmBx2xpaCulv3fbKtKaSCEmuM,603
|
39
|
+
universal_mcp/stores/store.py,sha256=yWbEGZb53z3fpVyqGWbes63z1CtIzC_IuM49OXy__UY,10137
|
40
|
+
universal_mcp/tools/__init__.py,sha256=jC8hsqfTdtn32yU57AVFUXiU3ZmUOCfCERSCaNEIH7E,395
|
41
|
+
universal_mcp/tools/adapters.py,sha256=YJ2oqgc8JgmtsdRRtvO-PO0Q0bKqTJ4Y3J6yxlESoTo,3947
|
42
|
+
universal_mcp/tools/docstring_parser.py,sha256=efEOE-ME7G5Jbbzpn7pN2xNuyu2M5zfZ1Tqu1lRB0Gk,8392
|
43
|
+
universal_mcp/tools/func_metadata.py,sha256=F4jd--hoZWKPBbZihVtluYKUsIdXdq4a0VWRgMl5k-Q,10838
|
44
|
+
universal_mcp/tools/manager.py,sha256=MajVskIptgXv1uZzwnSRycj1TSi7nhn4ebNSRkSSEDs,10455
|
45
|
+
universal_mcp/tools/registry.py,sha256=XsmVZL1rY5XgIBPTmvKKBWFLAvB3d9LfYMb11b4wSPI,1169
|
46
|
+
universal_mcp/tools/tools.py,sha256=1Q8bKiqj1E_-swvjmNHv16Orpd4p_HQtMKGxfqPmoPI,4570
|
47
|
+
universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
|
48
|
+
universal_mcp/utils/common.py,sha256=3aJK3AnBkmYf-dbsFLaEu_dGuXQ0Qi2HuqYTueLVhXQ,10968
|
49
|
+
universal_mcp/utils/installation.py,sha256=PU_GfHPqzkumKk-xG4L9CkBzSmABxmchwblZkx-zY-I,7204
|
50
|
+
universal_mcp/utils/prompts.py,sha256=FJhqE0gPXDzYHS8gOjAVrdqVxc9X12ESnpd4C3jDSMI,27547
|
51
|
+
universal_mcp/utils/singleton.py,sha256=RoOiKxBOAhp0TK1QaMDYi-8GjRcy2Vh-bAOuIAcYan0,775
|
52
|
+
universal_mcp/utils/testing.py,sha256=J857Xt5K-hMxTc8UNJWlkzLbca1zObjwNhNXzYGxBHI,8009
|
53
|
+
universal_mcp/utils/openapi/__inti__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
+
universal_mcp/utils/openapi/api_generator.py,sha256=892AWoOCzzvFHVfSpEBg1m4LFRnWMmOSeY0DgLDW0fU,6960
|
55
|
+
universal_mcp/utils/openapi/api_splitter.py,sha256=io7fV-E8hUIR4NxFlakqydbgrQF6aBAnZHPMlpxw-wc,20967
|
56
|
+
universal_mcp/utils/openapi/cli.py,sha256=az5ObS74R-MmDCOZ1PHTJVKZJrHnsBOAweOUa7A-GqA,25918
|
57
|
+
universal_mcp/utils/openapi/docgen.py,sha256=DNmwlhg_-TRrHa74epyErMTRjV2nutfCQ7seb_Rq5hE,21366
|
58
|
+
universal_mcp/utils/openapi/filters.py,sha256=96FajO5nLbvjNPy2A1HvSS9jqpzMDHd4q_QTP-DIsPI,3842
|
59
|
+
universal_mcp/utils/openapi/openapi.py,sha256=0Pn_ugkEwL0eMtctjc0XDMPZdB3SBOqza_J6BK8i_SY,62165
|
60
|
+
universal_mcp/utils/openapi/postprocessor.py,sha256=NKvpXi73INRXxj1KOu8Ph3loWGICx2Uyy2Q8uOOqBoc,12177
|
61
|
+
universal_mcp/utils/openapi/preprocessor.py,sha256=r4n0WQI__OzPL8FTza7jxiM4EYeZwa-3tvEJaJYZC44,63081
|
62
|
+
universal_mcp/utils/openapi/readme.py,sha256=R2Jp7DUXYNsXPDV6eFTkLiy7MXbSULUj1vHh4O_nB4c,2974
|
63
|
+
universal_mcp/utils/openapi/test_generator.py,sha256=h44gQXEXmrw4pD3-XNHKB7T9c2lDomqrJxVO6oszCqM,12186
|
64
|
+
universal_mcp/utils/templates/README.md.j2,sha256=Mrm181YX-o_-WEfKs01Bi2RJy43rBiq2j6fTtbWgbTA,401
|
65
|
+
universal_mcp/utils/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
|
66
|
+
universal_mcp-0.1.24rc3.dist-info/METADATA,sha256=rJ2CEZw0gwZkc3O39e1u_6VMcCr1dAhNBviG0YZXwto,3116
|
67
|
+
universal_mcp-0.1.24rc3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
68
|
+
universal_mcp-0.1.24rc3.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
|
69
|
+
universal_mcp-0.1.24rc3.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
|
70
|
+
universal_mcp-0.1.24rc3.dist-info/RECORD,,
|
@@ -1,122 +0,0 @@
|
|
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
|
universal_mcp/client/__main__.py
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
|
5
|
-
from loguru import logger
|
6
|
-
from pydantic import ValidationError
|
7
|
-
|
8
|
-
from universal_mcp.client.agent import ChatSession
|
9
|
-
from universal_mcp.client.client import MultiClientServer
|
10
|
-
from universal_mcp.config import ClientConfig
|
11
|
-
|
12
|
-
|
13
|
-
async def main() -> None:
|
14
|
-
"""Initialize and run the chat session."""
|
15
|
-
# Load settings and config using Pydantic BaseSettings
|
16
|
-
|
17
|
-
config_path = os.getenv("MCP_CONFIG_PATH", "servers.json")
|
18
|
-
try:
|
19
|
-
app_config = ClientConfig.load_json_config(config_path)
|
20
|
-
except (FileNotFoundError, ValidationError) as e:
|
21
|
-
logger.error(f"Error loading config: {e}")
|
22
|
-
sys.exit(1)
|
23
|
-
|
24
|
-
async with MultiClientServer(app_config.mcpServers) as mcp_server:
|
25
|
-
chat_session = ChatSession(mcp_server, app_config.llm)
|
26
|
-
await chat_session.interactive_loop()
|
27
|
-
|
28
|
-
|
29
|
-
if __name__ == "__main__":
|
30
|
-
asyncio.run(main())
|
universal_mcp/client/agent.py
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
|
3
|
-
from loguru import logger
|
4
|
-
from mcp.server import Server as MCPServer
|
5
|
-
from openai import AsyncOpenAI
|
6
|
-
|
7
|
-
from universal_mcp.config import LLMConfig
|
8
|
-
|
9
|
-
|
10
|
-
class ChatSession:
|
11
|
-
"""Orchestrates the interaction between user, LLM, and tools."""
|
12
|
-
|
13
|
-
def __init__(self, mcp_server: MCPServer, llm: LLMConfig | None) -> None:
|
14
|
-
self.mcp_server: MCPServer = mcp_server
|
15
|
-
self.llm: AsyncOpenAI | None = AsyncOpenAI(api_key=llm.api_key, base_url=llm.base_url) if llm else None
|
16
|
-
self.model = llm.model if llm else None
|
17
|
-
|
18
|
-
async def run(self, messages, tools) -> None:
|
19
|
-
"""Run the chat session."""
|
20
|
-
llm_response = await self.llm.chat.completions.create(
|
21
|
-
model=self.model,
|
22
|
-
messages=messages,
|
23
|
-
tools=tools,
|
24
|
-
tool_choice="auto",
|
25
|
-
)
|
26
|
-
|
27
|
-
tool_calls = llm_response.choices[0].message.tool_calls
|
28
|
-
if tool_calls:
|
29
|
-
for tool_call in tool_calls:
|
30
|
-
result = await self.mcp_server.call_tool(
|
31
|
-
tool_name=tool_call.function.name,
|
32
|
-
arguments=json.loads(tool_call.function.arguments) if tool_call.function.arguments else {},
|
33
|
-
)
|
34
|
-
result_content = [rc.text for rc in result.content] if result.content else "No result"
|
35
|
-
messages.append(
|
36
|
-
{
|
37
|
-
"tool_call_id": tool_call.id,
|
38
|
-
"role": "tool",
|
39
|
-
"name": tool_call.function.name,
|
40
|
-
"content": result_content,
|
41
|
-
}
|
42
|
-
)
|
43
|
-
else:
|
44
|
-
messages.append(llm_response.choices[0].message)
|
45
|
-
return messages
|
46
|
-
|
47
|
-
async def interactive_loop(self) -> None:
|
48
|
-
"""Main chat session handler."""
|
49
|
-
all_openai_tools = await self.mcp_server.list_tools(format="openai")
|
50
|
-
system_message = "You are a helpful assistant"
|
51
|
-
messages = [{"role": "system", "content": system_message}]
|
52
|
-
|
53
|
-
print("\n🎯 Interactive MCP Client")
|
54
|
-
print("Commands:")
|
55
|
-
print(" list - List available tools")
|
56
|
-
print(" call <tool_name> [args] - Call a tool")
|
57
|
-
print(" quit - Exit the client")
|
58
|
-
print()
|
59
|
-
while True:
|
60
|
-
try:
|
61
|
-
user_input = input("You: ").strip()
|
62
|
-
if user_input.lower() in {"quit", "exit"}:
|
63
|
-
logger.info("\nExiting...")
|
64
|
-
break
|
65
|
-
elif user_input.lower() == "list":
|
66
|
-
tools = await self.mcp_server.list_tools()
|
67
|
-
print("\nAvailable tools:")
|
68
|
-
for tool in tools:
|
69
|
-
print(f" {tool.name}")
|
70
|
-
continue
|
71
|
-
elif user_input.startswith("call "):
|
72
|
-
parts = user_input.split(maxsplit=2)
|
73
|
-
tool_name = parts[1] if len(parts) > 1 else ""
|
74
|
-
|
75
|
-
if not tool_name:
|
76
|
-
print("❌ Please specify a tool name")
|
77
|
-
continue
|
78
|
-
|
79
|
-
# Parse arguments (simple JSON-like format)
|
80
|
-
arguments = {}
|
81
|
-
if len(parts) > 2:
|
82
|
-
try:
|
83
|
-
arguments = json.loads(parts[2])
|
84
|
-
except json.JSONDecodeError:
|
85
|
-
print("❌ Invalid arguments format (expected JSON)")
|
86
|
-
continue
|
87
|
-
await self.mcp_server.call_tool(tool_name, arguments)
|
88
|
-
|
89
|
-
messages.append({"role": "user", "content": user_input})
|
90
|
-
|
91
|
-
messages = await self.run(messages, all_openai_tools)
|
92
|
-
print("\nAssistant: ", messages[-1]["content"])
|
93
|
-
|
94
|
-
except KeyboardInterrupt:
|
95
|
-
print("\nExiting...")
|
96
|
-
break
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# Integrations
|
2
|
-
|
3
|
-
This package provides integration classes for handling authentication and authorization with external services.
|
4
|
-
|
5
|
-
## Overview
|
6
|
-
|
7
|
-
An Integration defines how an application authenticates and authorizes with a service provider. The base `Integration` class provides an interface that all integrations must implement.
|
8
|
-
|
9
|
-
## Supported Integrations
|
10
|
-
|
11
|
-
### AgentR Integration
|
12
|
-
The `AgentRIntegration` class handles OAuth-based authentication flow with the AgentR API. It requires an API key which can be obtained from [agentr.dev](https://agentr.dev).
|
13
|
-
|
14
|
-
### API Key Integration
|
15
|
-
The `ApiKeyIntegration` class provides a simple API key based authentication mechanism. API keys are configured via environment variables.
|
16
|
-
|
17
|
-
## Usage
|
18
|
-
|
19
|
-
Each integration implements three key methods:
|
20
|
-
|
21
|
-
- `authorize()` - Initiates the authorization flow
|
22
|
-
- `get_credentials()` - Retrieves stored credentials
|
23
|
-
- `set_credentials()` - Stores new credentials
|
24
|
-
|
25
|
-
See the individual integration classes for specific usage details.
|