universal-mcp 0.1.24rc14__tar.gz → 0.1.24rc17__tar.gz

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.
Files changed (121) hide show
  1. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/PKG-INFO +2 -1
  2. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/pyproject.toml +2 -1
  3. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_api_integration.py +2 -4
  4. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/agentr/registry.py +4 -4
  5. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/applications/application.py +0 -2
  6. universal_mcp-0.1.24rc17/src/universal_mcp/applications/utils.py +52 -0
  7. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/servers/server.py +4 -3
  8. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/manager.py +0 -3
  9. universal_mcp-0.1.24rc17/src/universal_mcp/types.py +18 -0
  10. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/prompts.py +0 -2
  11. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/testing.py +1 -1
  12. universal_mcp-0.1.24rc14/src/evals/dataset.py +0 -24
  13. universal_mcp-0.1.24rc14/src/evals/datasets/creative_writing.jsonl +0 -3
  14. universal_mcp-0.1.24rc14/src/evals/datasets/exact.jsonl +0 -6
  15. universal_mcp-0.1.24rc14/src/evals/datasets/tasks.jsonl +0 -22
  16. universal_mcp-0.1.24rc14/src/evals/evaluators.py +0 -40
  17. universal_mcp-0.1.24rc14/src/evals/run.py +0 -149
  18. universal_mcp-0.1.24rc14/src/evals/test.py +0 -41
  19. universal_mcp-0.1.24rc14/src/evals/utils.py +0 -114
  20. universal_mcp-0.1.24rc14/src/tests/__init__.py +0 -0
  21. universal_mcp-0.1.24rc14/src/tests/test_agents.py +0 -318
  22. universal_mcp-0.1.24rc14/src/tests/test_applications.py +0 -76
  23. universal_mcp-0.1.24rc14/src/universal_mcp/__init__.py +0 -0
  24. universal_mcp-0.1.24rc14/src/universal_mcp/agents/__init__.py +0 -10
  25. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/__init__.py +0 -30
  26. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/__main__.py +0 -25
  27. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/context.py +0 -26
  28. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/graph.py +0 -151
  29. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/prompts.py +0 -9
  30. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/state.py +0 -27
  31. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/studio.py +0 -25
  32. universal_mcp-0.1.24rc14/src/universal_mcp/agents/autoagent/utils.py +0 -13
  33. universal_mcp-0.1.24rc14/src/universal_mcp/agents/base.py +0 -129
  34. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool/__init__.py +0 -54
  35. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool/__main__.py +0 -24
  36. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool/context.py +0 -24
  37. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool/graph.py +0 -166
  38. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool/prompts.py +0 -31
  39. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool/state.py +0 -27
  40. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/__init__.py +0 -53
  41. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/__main__.py +0 -24
  42. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/agent.py +0 -11
  43. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/context.py +0 -33
  44. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/graph.py +0 -169
  45. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/prompts.py +0 -12
  46. universal_mcp-0.1.24rc14/src/universal_mcp/agents/bigtool2/state.py +0 -27
  47. universal_mcp-0.1.24rc14/src/universal_mcp/agents/builder.py +0 -80
  48. universal_mcp-0.1.24rc14/src/universal_mcp/agents/cli.py +0 -27
  49. universal_mcp-0.1.24rc14/src/universal_mcp/agents/codeact/__init__.py +0 -243
  50. universal_mcp-0.1.24rc14/src/universal_mcp/agents/codeact/sandbox.py +0 -27
  51. universal_mcp-0.1.24rc14/src/universal_mcp/agents/codeact/test.py +0 -15
  52. universal_mcp-0.1.24rc14/src/universal_mcp/agents/codeact/utils.py +0 -61
  53. universal_mcp-0.1.24rc14/src/universal_mcp/agents/hil.py +0 -104
  54. universal_mcp-0.1.24rc14/src/universal_mcp/agents/llm.py +0 -45
  55. universal_mcp-0.1.24rc14/src/universal_mcp/agents/planner/__init__.py +0 -37
  56. universal_mcp-0.1.24rc14/src/universal_mcp/agents/planner/__main__.py +0 -24
  57. universal_mcp-0.1.24rc14/src/universal_mcp/agents/planner/graph.py +0 -82
  58. universal_mcp-0.1.24rc14/src/universal_mcp/agents/planner/prompts.py +0 -1
  59. universal_mcp-0.1.24rc14/src/universal_mcp/agents/planner/state.py +0 -12
  60. universal_mcp-0.1.24rc14/src/universal_mcp/agents/react.py +0 -84
  61. universal_mcp-0.1.24rc14/src/universal_mcp/agents/shared/agent_node.py +0 -34
  62. universal_mcp-0.1.24rc14/src/universal_mcp/agents/shared/tool_node.py +0 -235
  63. universal_mcp-0.1.24rc14/src/universal_mcp/agents/simple.py +0 -40
  64. universal_mcp-0.1.24rc14/src/universal_mcp/agents/tools.py +0 -35
  65. universal_mcp-0.1.24rc14/src/universal_mcp/agents/utils.py +0 -111
  66. universal_mcp-0.1.24rc14/src/universal_mcp/analytics.py +0 -111
  67. universal_mcp-0.1.24rc14/src/universal_mcp/applications/__init__.py +0 -70
  68. universal_mcp-0.1.24rc14/src/universal_mcp/types.py +0 -38
  69. universal_mcp-0.1.24rc14/src/universal_mcp/utils/common.py +0 -278
  70. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/.gitignore +0 -0
  71. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/LICENSE +0 -0
  72. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/README.md +0 -0
  73. {universal_mcp-0.1.24rc14/src/evals → universal_mcp-0.1.24rc17/src/tests}/__init__.py +0 -0
  74. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/conftest.py +0 -0
  75. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_api_generator.py +0 -0
  76. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_localserver.py +0 -0
  77. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_stores.py +0 -0
  78. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_tool.py +0 -0
  79. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_tool_manager.py +0 -0
  80. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/tests/test_zenquotes.py +0 -0
  81. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/agentr/README.md +0 -0
  82. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/agentr/__init__.py +0 -0
  83. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/agentr/client.py +0 -0
  84. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/agentr/integration.py +0 -0
  85. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/agentr/server.py +0 -0
  86. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/applications/sample/app.py +0 -0
  87. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/cli.py +0 -0
  88. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/client/oauth.py +0 -0
  89. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/client/token_store.py +0 -0
  90. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/client/transport.py +0 -0
  91. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/config.py +0 -0
  92. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/exceptions.py +0 -0
  93. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/integrations/__init__.py +0 -0
  94. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/integrations/integration.py +0 -0
  95. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/logger.py +0 -0
  96. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/py.typed +0 -0
  97. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/servers/__init__.py +0 -0
  98. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/stores/__init__.py +0 -0
  99. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/stores/store.py +0 -0
  100. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/__init__.py +0 -0
  101. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/adapters.py +0 -0
  102. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/docstring_parser.py +0 -0
  103. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/func_metadata.py +0 -0
  104. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/registry.py +0 -0
  105. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/tools/tools.py +0 -0
  106. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/__init__.py +0 -0
  107. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/installation.py +0 -0
  108. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/__inti__.py +0 -0
  109. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/api_generator.py +0 -0
  110. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/api_splitter.py +0 -0
  111. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/cli.py +0 -0
  112. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/docgen.py +0 -0
  113. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/filters.py +0 -0
  114. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/openapi.py +0 -0
  115. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/postprocessor.py +0 -0
  116. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/preprocessor.py +0 -0
  117. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/readme.py +0 -0
  118. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/openapi/test_generator.py +0 -0
  119. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/singleton.py +0 -0
  120. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/templates/README.md.j2 +0 -0
  121. {universal_mcp-0.1.24rc14 → universal_mcp-0.1.24rc17}/src/universal_mcp/utils/templates/api_client.py.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.24rc14
3
+ Version: 0.1.24rc17
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
@@ -32,6 +32,7 @@ Requires-Dist: pyyaml>=6.0.2
32
32
  Requires-Dist: rich>=14.0.0
33
33
  Requires-Dist: streamlit>=1.46.1
34
34
  Requires-Dist: typer>=0.15.2
35
+ Requires-Dist: universal-mcp-applications>=0.1.2
35
36
  Provides-Extra: dev
36
37
  Requires-Dist: litellm>=1.30.7; extra == 'dev'
37
38
  Requires-Dist: pre-commit>=4.2.0; extra == 'dev'
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "universal-mcp"
7
- version = "0.1.24-rc14"
7
+ version = "0.1.24-rc17"
8
8
  description = "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."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -39,6 +39,7 @@ dependencies = [
39
39
  "rich>=14.0.0",
40
40
  "streamlit>=1.46.1",
41
41
  "typer>=0.15.2",
42
+ "universal-mcp-applications>=0.1.2",
42
43
  ]
43
44
 
44
45
  [project.optional-dependencies]
@@ -1,7 +1,6 @@
1
1
  import pytest
2
2
 
3
- from universal_mcp.applications import app_from_config
4
- from universal_mcp.config import AppConfig # <-- Import the AppConfig model
3
+ from universal_mcp.applications.utils import app_from_slug
5
4
  from universal_mcp.exceptions import NotAuthorizedError
6
5
  from universal_mcp.integrations import ApiKeyIntegration
7
6
  from universal_mcp.stores import MemoryStore
@@ -11,8 +10,7 @@ def test_perplexity_api_no_key():
11
10
  store = MemoryStore()
12
11
 
13
12
  integration = ApiKeyIntegration("PERPLEXITY", store=store)
14
- perplexity_app_config = AppConfig(name="perplexity")
15
- PerplexityApp = app_from_config(perplexity_app_config)
13
+ PerplexityApp = app_from_slug("perplexity")
16
14
 
17
15
  app = PerplexityApp(integration=integration)
18
16
 
@@ -3,7 +3,7 @@ from typing import Any
3
3
  from loguru import logger
4
4
 
5
5
  from universal_mcp.agentr.client import AgentrClient
6
- from universal_mcp.applications import app_from_slug
6
+ from universal_mcp.applications.utils import app_from_slug
7
7
  from universal_mcp.tools.manager import ToolManager, _get_app_and_tool_name
8
8
  from universal_mcp.tools.registry import ToolRegistry
9
9
  from universal_mcp.types import ToolConfig, ToolFormat
@@ -133,7 +133,7 @@ class AgentrRegistry(ToolRegistry):
133
133
  try:
134
134
  # Clear tools from tool manager before loading new tools
135
135
  self.tool_manager.clear_tools()
136
- if isinstance(tools, ToolConfig):
136
+ if isinstance(tools, dict):
137
137
  logger.info("Loading tools from tool config")
138
138
  self._load_tools_from_tool_config(tools, self.tool_manager)
139
139
  else:
@@ -176,8 +176,8 @@ class AgentrRegistry(ToolRegistry):
176
176
  tool_config: The tool configuration containing app names and tools
177
177
  tool_manager: The tool manager to register tools with
178
178
  """
179
- for app_name, tool_data in tool_config.agentrServers.items():
180
- self._load_tools(app_name, tool_data.tools, tool_manager)
179
+ for app_name, tool_names in tool_config.items():
180
+ self._load_tools(app_name, tool_names, tool_manager)
181
181
 
182
182
  async def call_tool(self, tool_name: str, tool_args: dict[str, Any]) -> dict[str, Any]:
183
183
  """Call a tool with the given name and arguments."""
@@ -9,7 +9,6 @@ from gql.transport.requests import RequestsHTTPTransport
9
9
  from graphql import DocumentNode
10
10
  from loguru import logger
11
11
 
12
- from universal_mcp.analytics import analytics
13
12
  from universal_mcp.integrations.integration import Integration
14
13
 
15
14
 
@@ -37,7 +36,6 @@ class BaseApplication(ABC):
37
36
  """
38
37
  self.name = name
39
38
  logger.debug(f"Initializing Application '{name}' with kwargs: {kwargs}")
40
- analytics.track_app_loaded(name) # Track app loading
41
39
 
42
40
  @abstractmethod
43
41
  def list_tools(self) -> list[Callable]:
@@ -0,0 +1,52 @@
1
+ import importlib
2
+
3
+ from loguru import logger
4
+
5
+ from universal_mcp.applications.application import BaseApplication
6
+
7
+ # --- Default Name Generators ---
8
+
9
+
10
+ def get_default_package_name(slug: str) -> str:
11
+ """
12
+ Convert a repository slug to a package name.
13
+ """
14
+ slug = slug.strip().lower().replace("-", "_")
15
+ package_name = f"universal_mcp.applications.{slug}"
16
+ return package_name
17
+
18
+
19
+ def get_default_module_path(slug: str) -> str:
20
+ """
21
+ Convert a repository slug to a module path.
22
+ """
23
+ package_name = get_default_package_name(slug)
24
+ module_path = f"{package_name}.app"
25
+ return module_path
26
+
27
+
28
+ def get_default_class_name(slug: str) -> str:
29
+ """
30
+ Convert a repository slug to a class name.
31
+ """
32
+ slug = slug.strip().lower()
33
+ parts = slug.replace("-", "_").split("_")
34
+ class_name = "".join(part.capitalize() for part in parts) + "App"
35
+ return class_name
36
+
37
+
38
+ # --- Application Loaders ---
39
+
40
+
41
+ def app_from_slug(slug: str) -> type[BaseApplication]:
42
+ """Loads an application from a slug."""
43
+ return load_app_from_package(slug)
44
+
45
+
46
+ def load_app_from_package(slug: str) -> type[BaseApplication]:
47
+ """Loads an application from a pip-installable package."""
48
+ logger.debug(f"Loading '{slug}' as a package.")
49
+ module_path_str = get_default_module_path(slug)
50
+ class_name_str = get_default_class_name(slug)
51
+ module = importlib.import_module(module_path_str)
52
+ return getattr(module, class_name_str)
@@ -5,7 +5,8 @@ from loguru import logger
5
5
  from mcp.server.fastmcp import FastMCP
6
6
  from mcp.types import TextContent
7
7
 
8
- from universal_mcp.applications import BaseApplication, app_from_config
8
+ from universal_mcp.applications.application import BaseApplication
9
+ from universal_mcp.applications.utils import app_from_slug
9
10
  from universal_mcp.config import ServerConfig
10
11
  from universal_mcp.exceptions import ConfigurationError, ToolError
11
12
  from universal_mcp.integrations.integration import ApiKeyIntegration, OAuthIntegration
@@ -44,8 +45,8 @@ def load_from_local_config(config: ServerConfig, tool_manager: ToolManager) -> N
44
45
  integration = OAuthIntegration(config.name, store=store, **app_config.integration.credentials)
45
46
  else:
46
47
  raise ValueError(f"Unsupported integration type: {app_config.integration.type}")
47
- app = app_from_config(app_config)(integration=integration)
48
- tool_manager.register_tools_from_app(app, app_config.actions)
48
+ app = app_from_slug(app_config.name)(integration=integration)
49
+ tool_manager.register_tools_from_app(app, tool_names=app_config.actions)
49
50
  logger.info(f"Loaded app: {app_config.name}")
50
51
  except Exception as e:
51
52
  logger.error(f"Failed to load app {app_config.name}: {e}", exc_info=True)
@@ -3,7 +3,6 @@ from typing import Any
3
3
 
4
4
  from loguru import logger
5
5
 
6
- from universal_mcp.analytics import analytics
7
6
  from universal_mcp.applications.application import BaseApplication
8
7
  from universal_mcp.exceptions import ToolNotFoundError
9
8
  from universal_mcp.tools.adapters import (
@@ -298,8 +297,6 @@ class ToolManager:
298
297
  raise ToolNotFoundError(f"Unknown tool: {name}")
299
298
  try:
300
299
  result = await tool.run(arguments, context)
301
- analytics.track_tool_called(name, app_name, "success")
302
300
  return result
303
301
  except Exception as e:
304
- analytics.track_tool_called(name, app_name, "error", str(e))
305
302
  raise e
@@ -0,0 +1,18 @@
1
+ from enum import Enum
2
+
3
+ # Constants
4
+ DEFAULT_IMPORTANT_TAG = "important"
5
+ TOOL_NAME_SEPARATOR = "__"
6
+ DEFAULT_APP_NAME = "common"
7
+
8
+
9
+ class ToolFormat(str, Enum):
10
+ """Supported tool formats."""
11
+
12
+ NATIVE = "native"
13
+ MCP = "mcp"
14
+ LANGCHAIN = "langchain"
15
+ OPENAI = "openai"
16
+
17
+
18
+ ToolConfig = dict[str, list[str]]
@@ -229,7 +229,6 @@ from loguru import logger
229
229
 
230
230
  import httpx
231
231
 
232
- from universal_mcp.analytics import analytics
233
232
  from universal_mcp.integrations import Integration
234
233
 
235
234
  class BaseApplication(ABC):
@@ -256,7 +255,6 @@ class BaseApplication(ABC):
256
255
  """
257
256
  self.name = name
258
257
  logger.debug(f"Initializing Application '{name}' with kwargs: {kwargs}")
259
- analytics.track_app_loaded(name) # Track app loading
260
258
 
261
259
  @abstractmethod
262
260
  def list_tools(self) -> list[Callable]:
@@ -8,7 +8,7 @@ from pydantic import BaseModel, SecretStr
8
8
 
9
9
  from universal_mcp.agentr import AgentrIntegration
10
10
  from universal_mcp.agentr.client import AgentrClient
11
- from universal_mcp.applications import APIApplication, BaseApplication
11
+ from universal_mcp.applications.application import APIApplication, BaseApplication
12
12
  from universal_mcp.tools import Tool, ToolManager
13
13
  from universal_mcp.types import ToolFormat
14
14
 
@@ -1,24 +0,0 @@
1
- import csv
2
- import json
3
- from typing import Any
4
-
5
-
6
- def load_dataset(file_path: str) -> list[dict[str, Any]]:
7
- """
8
- Loads a dataset from a CSV or JSONL file.
9
-
10
- Args:
11
- file_path: The path to the dataset file.
12
-
13
- Returns:
14
- A list of dictionaries, where each dictionary represents an example.
15
- """
16
- if file_path.endswith(".csv"):
17
- with open(file_path, encoding="utf-8") as f:
18
- reader = csv.DictReader(f)
19
- return list(reader)
20
- elif file_path.endswith(".jsonl"):
21
- with open(file_path, encoding="utf-8") as f:
22
- return [json.loads(line) for line in f]
23
- else:
24
- raise ValueError("Unsupported file format. Please use CSV or JSONL.")
@@ -1,3 +0,0 @@
1
- {"user_input": "Write a haiku about a rainy day."}
2
- {"user_input": "Tell me a short story about a friendly ghost."}
3
- {"user_input": "Write a poem about the changing seasons."}
@@ -1,6 +0,0 @@
1
- {"user_input": "What is 2 + 2?", "expected_output": "4"}
2
- {"user_input": "What is the opposite of 'hot'?", "expected_output": "cold"}
3
- {"user_input": "Who is the first president of the United States?", "expected_output": "George Washington"}
4
- {"user_input": "What is the capital of France?", "expected_output": "Paris"}
5
- {"user_input": "Who wrote 'To Kill a Mockingbird'?", "expected_output": "Harper Lee"}
6
- {"user_input": "What is the boiling point of water at sea level in Celsius?", "expected_output": "100"}
@@ -1,22 +0,0 @@
1
- {"user_input": "Send an email to manoj@agentr.dev from my Gmail account", "difficulty": 1, "expected_tools": {"agentrServers": {"google-mail": {"tools": ["send_email"]}}}
2
- {"user_input": "Show me events from today's Google Calendar.", "difficulty": 1, "expected_tools": {"agentrServers": {"google-calendar": {"tools": ["list_events"]}}}}
3
- {"user_input": "Create a Google Doc summarizing the last 5 merged pull requests in my GitHub repo- universal-mcp/universal-mcp, including links and commit highlights.", "difficulty": 4, "expected_tools": {"agentrServers": {"github": {"tools": ["get_pull_request"]}, "google-docs": {"tools": ["create_document"]}}}}
4
- {"user_input": "Summarize the key insights from all marketing emails received this week from my Gmail and add a section in a Google Doc with action points.", "difficulty": 4, "expected_tools": {"agentrServers": {"google-gemini": {"tools": ["prompt_document"]}, "google-docs": {"tools": ["style_text"]}}}}
5
- {"user_input": "Create a Google Sheet of the best cafes and restaurants near IIT Bombay", "difficulty": 3, "expected_tools": {"agentrServers": {"google-sheet": {"tools": ["create_spreadsheet"]}, "serpapi": {"tools": ["google_maps_search"]}}}}
6
- {"user_input": "Track the top posts in r/startups over the past 7 days using Reddit and create a trend report on what's being discussed most (e.g., hiring, funding, MVPs) in a Google Doc.", "difficulty": 5, "expected_tools": {"agentrServers": {"reddit": {"tools": ["search"]}, "google-docs": {"tools": ["add_content", "create_footer"]}}}}
7
- {"user_input": "Find the best restaurants in Goa using perplexity web search", "difficulty": 2, "expected_tools": {"agentrServers": {}}}
8
- {"user_input": "List the unread emails from the last 24 hours from my Gmail, sorted by sender.", "difficulty": 2, "expected_tools": {"agentrServers": {"google-mail": {"tools": ["list_messages"]}}}}
9
- {"user_input": "Tell me how many meetings I have tomorrow and when they start from my Google Calendar.", "difficulty": 1, "expected_tools": {"agentrServers": {"google-calendar": {"tools": ["get_today_events"]}}}}
10
- {"user_input": "Create a meeting with aditakarsh@example.com on the topic of the latest trends in AI at 8PM today using Google Calendar.", "difficulty": 2, "expected_tools": {"agentrServers": {"google-calendar": {"tools": ["add_an_event"]}}}}
11
- {"user_input": "What are the topics of my meetings today from Google Calendar and who are the attendees? Give a 1-line context for each attendee using LinkedIn or web search.", "difficulty": 4, "expected_tools": {"agentrServers": {"google-calendar": {"tools": ["list_events"]}, "serpapi": {"tools": ["search"]}, "perplexity": {"tools": ["chat"]}, "tavily": {"tools": ["search"]}}}}
12
- {"user_input": "Fetch my last inbox mail from Microsoft Outlook", "difficulty": 1, "expected_tools": {"agentrServers": {"outlook": {"tools": ["user_get_mail_folder"]}}}}
13
- {"user_input": "Fetch unsubscribe links from my Gmail inbox for promo emails I have received in the last 7 days", "difficulty": 3, "expected_tools": {"agentrServers": {"google-mail": {"tools": ["delete_filters", "list_filters"]}}}}
14
- {"user_input": "Fetch all unread emails from Gmail and new tickets from ClickUp for me from last night", "difficulty": 4, "expected_tools": {"agentrServers": {"clickup": {"tools": ["tasks_filter_team_tasks"]}}}}
15
- {"user_input": "Give me a report on the earnings of Oklo using web search, and projections for the company revenue, stock price", "difficulty": 4, "expected_tools": {"agentrServers": {"serpapi": {"tools": ["search"]}, "perplexity": {"tools": ["chat"]}, "tavily": {"tools": ["search"]}}}}
16
- {"user_input": "Create a weekly expense report from my credit card transactions and categorize spending by type (food, transport, entertainment, etc.) in a Google Sheet", "difficulty": 3, "expected_tools": {"agentrServers": {"google-sheet": {"tools": ["create_spreadsheet"]}}}}
17
- {"user_input": "Generate a comparison table of SaaS tools for project management using web search, including pricing, features, and user ratings in a Google Sheet", "difficulty": 4, "expected_tools": {"agentrServers": {"serpapi": {"tools": ["search"]}, "google-sheet": {"tools": ["add_table", "list_tables"]}}}}
18
- {"user_input": "Research the top 10 Y Combinator startups from the latest batch using web search and create a report on their industries and funding status in Google Docs", "difficulty": 5, "expected_tools": {"agentrServers": {"google-docs": {"tools": ["create_footer", "create_header"]}}}}
19
- {"user_input": "Find and summarize the key takeaways from the latest earnings calls of FAANG companies using web search and create a report in Google Docs", "difficulty": 5, "expected_tools": {"agentrServers": {"serpapi": {"tools": ["search"]}, "google-docs": {"tools": ["style_text", "create_footer"]}}}}
20
- {"user_input": "Draft personalized LinkedIn outreach messages for 10 potential collaborators in the fintech space based on their recent posts using LinkedIn data in a Google Sheet", "difficulty": 5, "expected_tools": {"agentrServers": {}}}
21
- {"user_input": "Monitor my Twitter mentions and DMs from the past 48 hours and create a response priority list in Google Sheets", "difficulty": 4, "expected_tools": {"agentrServers": {"twitter": {"tools": ["search_stream"]}, "google-sheet": {"tools": ["add_sheet", "add_table"]}}}}
22
- {"user_input": "Create a content calendar for next month with trending AI/ML topics using web search and optimal posting times based on my audience analytics in Google Sheets", "difficulty": 5, "expected_tools": {"agentrServers": {"google-calendar": {"tools": ["add_an_event"]}, "google-sheet": {"tools": ["append_values"]}, "serpapi": {"tools": ["search"]}, "perplexity": {"tools": ["chat"]}, "tavily": {"tools": ["search"]}}}}
@@ -1,40 +0,0 @@
1
- from langsmith.evaluation import EvaluationResult, run_evaluator
2
- from langsmith.schemas import Example, Run
3
- from openevals.llm import create_llm_as_judge
4
- from openevals.prompts import CORRECTNESS_PROMPT
5
-
6
-
7
- @run_evaluator
8
- def exact_match_evaluator(run: Run, example: Example | None = None) -> EvaluationResult:
9
- """
10
- A simple evaluator that checks for exact match between the agent's output
11
- and the expected output from the dataset.
12
- """
13
- if example is None or "expected_output" not in example.outputs:
14
- return EvaluationResult(key="exact_match", score=0, comment="No expected output provided.")
15
-
16
- # The agent's response might be in a list of messages
17
- agent_response_raw = run.outputs.get("output", "")
18
- if isinstance(agent_response_raw, list):
19
- # Extract text from the last dictionary in the list
20
- agent_response = agent_response_raw[-1].get("text", "").strip() if agent_response_raw else ""
21
- else:
22
- agent_response = str(agent_response_raw).strip()
23
-
24
- expected_output = example.outputs["expected_output"].strip()
25
-
26
- if agent_response == expected_output:
27
- score = 1
28
- comment = "Exact match."
29
- else:
30
- score = 0
31
- comment = f"Mismatch: Expected '{expected_output}', but got '{agent_response}'."
32
-
33
- return EvaluationResult(key="exact_match", score=score, comment=comment)
34
-
35
-
36
- correctness_evaluator = create_llm_as_judge(
37
- prompt=CORRECTNESS_PROMPT,
38
- feedback_key="correctness",
39
- model="anthropic:claude-4-sonnet-20250514",
40
- )
@@ -1,149 +0,0 @@
1
- import argparse
2
- import asyncio
3
- from typing import Any
4
-
5
- from dotenv import load_dotenv
6
- from langsmith import Client, aevaluate
7
- from langsmith.evaluation import RunEvaluator
8
-
9
- from evals.dataset import load_dataset
10
- from evals.evaluators import correctness_evaluator, exact_match_evaluator
11
- from universal_mcp.agentr.registry import AgentrRegistry
12
- from universal_mcp.agents.auto import AutoAgent
13
- from universal_mcp.agents.base import BaseAgent
14
- from universal_mcp.agents.react import ReactAgent
15
- from universal_mcp.agents.simple import SimpleAgent
16
-
17
- load_dotenv()
18
-
19
-
20
- # 1. Agent Factory
21
- def get_agent(agent_name: str) -> BaseAgent:
22
- """
23
- Factory function to get an agent instance by name.
24
- """
25
- common_params = {
26
- "instructions": "You are a helpful assistant.",
27
- "model": "anthropic/claude-4-sonnet-20250514",
28
- "registry": AgentrRegistry() if agent_name != "simple" else None,
29
- }
30
- if agent_name == "simple":
31
- return SimpleAgent(name="simple-agent", **common_params)
32
- elif agent_name == "react":
33
- return ReactAgent(name="react-agent", **common_params)
34
- elif agent_name == "auto":
35
- return AutoAgent(name="auto-agent", **common_params)
36
- else:
37
- raise ValueError(f"Unknown agent: {agent_name}. Available agents: simple, react, auto")
38
-
39
-
40
- # 2. Evaluator Registry
41
- EVALUATORS: dict[str, Any] = {
42
- "llm_as_judge": correctness_evaluator,
43
- "exact_match": exact_match_evaluator,
44
- }
45
-
46
-
47
- def get_evaluator(evaluator_name: str) -> RunEvaluator:
48
- """
49
- Retrieves an evaluator from the registry.
50
- """
51
- evaluator = EVALUATORS.get(evaluator_name)
52
- if evaluator is None:
53
- raise ValueError(f"Unknown evaluator: {evaluator_name}. Available evaluators: {', '.join(EVALUATORS.keys())}")
54
- return evaluator
55
-
56
-
57
- # Wrapper to run the agent and format the output consistently
58
- async def agent_runner(agent: BaseAgent, inputs: dict):
59
- """
60
- Runs the agent and returns a dictionary with the final output.
61
- """
62
- result = await agent.invoke(user_input=inputs["user_input"])
63
- # Extract the last message content as the final response
64
- if isinstance(result, dict) and "messages" in result and result["messages"]:
65
- content = result["messages"][-1].content
66
- if isinstance(content, str):
67
- final_response = content
68
- elif isinstance(content, list):
69
- # Handle list of content blocks (e.g., from Anthropic)
70
- text_parts = []
71
- for item in content:
72
- if isinstance(item, dict) and item.get("type") == "text":
73
- text_parts.append(item.get("text", ""))
74
- final_response = "\n".join(text_parts)
75
- else:
76
- final_response = str(content)
77
- else:
78
- final_response = str(result)
79
- return {"output": final_response}
80
-
81
-
82
- async def main(agent_name: str, dataset_path: str, evaluator_name: str):
83
- """
84
- The main function for the evaluation CLI.
85
- """
86
- print(f"Starting evaluation with agent='{agent_name}', dataset='{dataset_path}', evaluator='{evaluator_name}'")
87
-
88
- # 1. Get the agent and evaluator
89
- agent = get_agent(agent_name)
90
- evaluator = get_evaluator(evaluator_name)
91
-
92
- # Create a callable for aevaluate
93
- async def target_func(inputs: dict):
94
- return await agent_runner(agent, inputs)
95
-
96
- # 2. Load the dataset from file
97
- dataset_examples = load_dataset(dataset_path)
98
-
99
- # 3. Upload dataset to LangSmith for the evaluation run
100
- client = Client()
101
- dataset_name = dataset_path.split("/")[-1].split(".")[0]
102
- # dataset_name = f"{agent_name}-{evaluator_name}-eval-dataset"
103
- try:
104
- # If dataset with same name and examples exists, read it.
105
- # Otherwise, a new one is created.
106
- dataset = client.create_dataset(
107
- dataset_name, description=f"Dataset for {agent_name} evaluation with {evaluator_name}."
108
- )
109
- for example in dataset_examples:
110
- client.create_example(
111
- inputs={"user_input": example["user_input"]},
112
- outputs={"output": example.get("expected_output")} if "expected_output" in example else None,
113
- dataset_id=dataset.id,
114
- )
115
- print(f"Created and populated dataset '{dataset_name}' for this run.")
116
- except Exception:
117
- print(f"Using existing dataset '{dataset_name}'.")
118
-
119
- # 4. Run the evaluation
120
- await aevaluate(
121
- target_func,
122
- data=dataset_name, # Pass the dataset name
123
- evaluators=[evaluator],
124
- experiment_prefix=f"{agent_name}-{evaluator_name}-eval",
125
- )
126
-
127
-
128
- if __name__ == "__main__":
129
- parser = argparse.ArgumentParser(description="Run evaluations on different agents.")
130
- parser.add_argument(
131
- "agent",
132
- type=str,
133
- choices=["simple", "react", "auto"],
134
- help="The name of the agent to evaluate.",
135
- )
136
- parser.add_argument(
137
- "dataset",
138
- type=str,
139
- help="Path to the dataset file (e.g., src/evals/example_dataset.jsonl).",
140
- )
141
- parser.add_argument(
142
- "evaluator",
143
- type=str,
144
- choices=list(EVALUATORS.keys()),
145
- help="The name of the evaluator to use.",
146
- )
147
- args = parser.parse_args()
148
-
149
- asyncio.run(main(agent_name=args.agent, dataset_path=args.dataset, evaluator_name=args.evaluator))
@@ -1,41 +0,0 @@
1
- import asyncio
2
-
3
- from dotenv import load_dotenv
4
- from langsmith import Client, aevaluate
5
-
6
- from universal_mcp.agentr.registry import AgentrRegistry
7
- from universal_mcp.agents.autoagent import AutoAgent
8
-
9
- load_dotenv()
10
-
11
-
12
- async def target_function1(inputs: dict):
13
- agent = AutoAgent(
14
- name="autoagent",
15
- instructions="You are a helpful assistant that can use tools to help the user.",
16
- model="anthropic/claude-4-sonnet-20250514",
17
- tool_registry=AgentrRegistry(),
18
- )
19
- result = await agent.invoke(inputs["user_input"])
20
- return result
21
-
22
-
23
- if __name__ == "__main__":
24
- client = Client()
25
- dataset_name = "autoagent-actual-runs"
26
- experiment = asyncio.run(
27
- aevaluate(
28
- target_function1,
29
- data=client.list_examples(dataset_name=dataset_name, splits=["choose-app"]),
30
- evaluators=[],
31
- experiment_prefix="test-1",
32
- )
33
- )
34
- asyncio.run(
35
- aevaluate(
36
- target_function1,
37
- data=client.list_examples(dataset_name=dataset_name),
38
- evaluators=[],
39
- experiment=experiment.experiment_name,
40
- )
41
- )
@@ -1,114 +0,0 @@
1
- import argparse
2
-
3
- from dotenv import load_dotenv
4
- from langsmith import Client
5
-
6
- from evals.dataset import load_dataset
7
-
8
- load_dotenv()
9
-
10
-
11
- def upload_runs_to_dataset(project_name: str, dataset_name: str, dataset_description: str = ""):
12
- """
13
- Uploads runs from a LangSmith project to a dataset.
14
-
15
- Args:
16
- project_name: The name of the LangSmith project containing the runs.
17
- dataset_name: The name of the dataset to create or add to.
18
- dataset_description: A description for the new dataset.
19
- """
20
- client = Client()
21
- try:
22
- dataset = client.create_dataset(dataset_name, description=dataset_description)
23
- print(f"Created new dataset: '{dataset_name}'")
24
- except Exception:
25
- dataset = client.read_dataset(dataset_name=dataset_name)
26
- print(f"Using existing dataset: '{dataset_name}'")
27
-
28
- runs = client.list_runs(project_name=project_name)
29
-
30
- example_count = 0
31
- for run in runs:
32
- client.create_example(
33
- inputs=run.inputs,
34
- outputs=run.outputs,
35
- dataset_id=dataset.id,
36
- )
37
- example_count += 1
38
-
39
- print(f"✅ Successfully uploaded {example_count} runs from project '{project_name}' to dataset '{dataset_name}'.")
40
-
41
-
42
- def upload_dataset_from_file(
43
- file_path: str,
44
- dataset_name: str,
45
- dataset_description: str,
46
- input_keys: list[str],
47
- output_keys: list[str],
48
- ):
49
- """
50
- Uploads a dataset from a local file (CSV or JSONL) to LangSmith.
51
-
52
- Args:
53
- file_path: The path to the local dataset file.
54
- dataset_name: The name for the new dataset in LangSmith.
55
- dataset_description: A description for the new dataset.
56
- input_keys: A list of column names to be used as inputs.
57
- output_keys: A list of column names to be used as outputs.
58
- """
59
- client = Client()
60
- examples = load_dataset(file_path)
61
-
62
- try:
63
- dataset = client.create_dataset(dataset_name, description=dataset_description)
64
- print(f"Created new dataset: '{dataset_name}'")
65
- except Exception:
66
- dataset = client.read_dataset(dataset_name=dataset_name)
67
- print(f"Using existing dataset: '{dataset_name}'")
68
-
69
- for example in examples:
70
- inputs = {key: example[key] for key in input_keys if key in example}
71
- outputs = {key: example[key] for key in output_keys if key in example}
72
- client.create_example(inputs=inputs, outputs=outputs, dataset_id=dataset.id)
73
-
74
- print(f"✅ Successfully uploaded {len(examples)} examples from '{file_path}' to dataset '{dataset_name}'.")
75
-
76
-
77
- if __name__ == "__main__":
78
- parser = argparse.ArgumentParser(description="LangSmith Dataset Utilities.")
79
- subparsers = parser.add_subparsers(dest="command", required=True)
80
-
81
- # Sub-parser for uploading runs from a project
82
- parser_runs = subparsers.add_parser("upload-runs", help="Upload runs from a project to a dataset.")
83
- parser_runs.add_argument("--project-name", required=True, help="The LangSmith project name.")
84
- parser_runs.add_argument("--dataset-name", required=True, help="The target dataset name.")
85
- parser_runs.add_argument(
86
- "--dataset-description", default="Dataset from project runs.", help="Description for the dataset."
87
- )
88
-
89
- # Sub-parser for uploading a dataset from a file
90
- parser_file = subparsers.add_parser("upload-file", help="Upload a dataset from a local file.")
91
- parser_file.add_argument("--file-path", required=True, help="Path to the local dataset file (CSV or JSONL).")
92
- parser_file.add_argument("--dataset-name", required=True, help="The name for the dataset in LangSmith.")
93
- parser_file.add_argument(
94
- "--dataset-description", default="Dataset uploaded from file.", help="Description for the dataset."
95
- )
96
- parser_file.add_argument("--input-keys", required=True, help="Comma-separated list of input column names.")
97
- parser_file.add_argument("--output-keys", required=True, help="Comma-separated list of output column names.")
98
-
99
- args = parser.parse_args()
100
-
101
- if args.command == "upload-runs":
102
- upload_runs_to_dataset(
103
- project_name=args.project_name,
104
- dataset_name=args.dataset_name,
105
- dataset_description=args.dataset_description,
106
- )
107
- elif args.command == "upload-file":
108
- upload_dataset_from_file(
109
- file_path=args.file_path,
110
- dataset_name=args.dataset_name,
111
- dataset_description=args.dataset_description,
112
- input_keys=args.input_keys.split(","),
113
- output_keys=args.output_keys.split(","),
114
- )
File without changes