universal-mcp 0.1.24rc4__py3-none-any.whl → 0.1.24rc6__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.
@@ -3,26 +3,31 @@ from typing import cast
3
3
  from uuid import uuid4
4
4
 
5
5
  from langchain_core.messages import AIMessageChunk
6
+ from langgraph.checkpoint.base import BaseCheckpointSaver
6
7
  from langgraph.checkpoint.memory import MemorySaver
7
8
  from langgraph.types import Command
8
9
 
10
+ from .llm import get_llm
9
11
  from .utils import RichCLI
10
12
 
11
13
 
12
14
  class BaseAgent:
13
- def __init__(self, name: str, instructions: str, model: str):
15
+ def __init__(self, name: str, instructions: str, model: str, memory: BaseCheckpointSaver | None = None, **kwargs):
14
16
  self.name = name
15
17
  self.instructions = instructions
16
18
  self.model = model
17
- self.memory = MemorySaver()
19
+ self.memory = memory or MemorySaver()
20
+ self._graph = None
21
+ self.llm = get_llm(model)
18
22
  self.cli = RichCLI()
19
23
 
20
- @property
21
- def graph(self):
24
+ async def _build_graph(self):
22
25
  raise NotImplementedError("Subclasses must implement this method")
23
26
 
24
27
  async def stream(self, thread_id: str, user_input: str):
25
- async for event, _ in self.graph.astream(
28
+ if self._graph is None:
29
+ self._graph = await self._build_graph()
30
+ async for event, _ in self._graph.astream(
26
31
  {"messages": [{"role": "user", "content": user_input}]},
27
32
  config={"configurable": {"thread_id": thread_id}},
28
33
  stream_mode="messages",
@@ -32,25 +37,33 @@ class BaseAgent:
32
37
 
33
38
  async def stream_interactive(self, thread_id: str, user_input: str):
34
39
  with self.cli.display_agent_response_streaming(self.name) as stream_updater:
35
- async for event in self.stream(thread_id, user_input):
40
+ async for event in self.astream(thread_id, user_input):
36
41
  stream_updater.update(event.content)
37
42
 
38
- async def process_command(self, command: str) -> bool | None:
39
- """Process a command from the user"""
43
+ async def run(self, user_input: str, thread_id: str = str(uuid4())):
44
+ """Run the agent"""
45
+ if not self._graph:
46
+ self._graph = await self._build_graph()
47
+ return await self._graph.ainvoke(
48
+ {"messages": [{"role": "user", "content": user_input}]},
49
+ config={"configurable": {"thread_id": thread_id}},
50
+ )
40
51
 
41
52
  async def run_interactive(self, thread_id: str = str(uuid4())):
42
53
  """Main application loop"""
43
54
 
55
+ if not self._graph:
56
+ self._graph = await self._build_graph()
44
57
  # Display welcome
45
58
  self.cli.display_welcome(self.name)
46
59
 
47
60
  # Main loop
48
61
  while True:
49
62
  try:
50
- state = self.graph.get_state(config={"configurable": {"thread_id": thread_id}})
63
+ state = self._graph.get_state(config={"configurable": {"thread_id": thread_id}})
51
64
  if state.interrupts:
52
65
  value = self.cli.handle_interrupt(state.interrupts[0])
53
- self.graph.invoke(Command(resume=value), config={"configurable": {"thread_id": thread_id}})
66
+ self._graph.invoke(Command(resume=value), config={"configurable": {"thread_id": thread_id}})
54
67
  continue
55
68
 
56
69
  user_input = self.cli.get_user_input()
@@ -1,34 +1,36 @@
1
+ from langgraph.checkpoint.base import BaseCheckpointSaver
1
2
  from langgraph.prebuilt import create_react_agent
2
3
  from loguru import logger
3
4
 
4
- from universal_mcp.agentr.registry import AgentrRegistry
5
- from universal_mcp.tools.adapters import ToolFormat
6
- from universal_mcp.tools.manager import ToolManager
7
-
8
- from .base import BaseAgent
9
- from .llm import get_llm
5
+ from universal_mcp.agents.base import BaseAgent
6
+ from universal_mcp.agents.tools import load_agentr_tools, load_mcp_tools
7
+ from universal_mcp.types import ToolConfig
10
8
 
11
9
 
12
10
  class ReactAgent(BaseAgent):
13
11
  def __init__(
14
- self, name: str, instructions: str, model: str, tools: list[str] | None = None, max_iterations: int = 10
12
+ self,
13
+ name: str,
14
+ instructions: str,
15
+ model: str,
16
+ memory: BaseCheckpointSaver | None = None,
17
+ tools: ToolConfig | None = None,
18
+ max_iterations: int = 10,
19
+ **kwargs,
15
20
  ):
16
- super().__init__(name, instructions, model)
17
- self.llm = get_llm(model)
21
+ super().__init__(name, instructions, model, memory, **kwargs)
22
+ self.tools = tools
18
23
  self.max_iterations = max_iterations
19
- self.tool_manager = ToolManager()
20
- registry = AgentrRegistry()
21
- if tools:
22
- registry.load_tools(tools, self.tool_manager)
23
- logger.debug(f"Initialized ReactAgent: name={name}, model={model}")
24
- self._graph = self._build_graph()
25
-
26
- @property
27
- def graph(self):
28
- return self._graph
29
-
30
- def _build_graph(self):
31
- tools = self.tool_manager.list_tools(format=ToolFormat.LANGCHAIN) if self.tool_manager else []
24
+
25
+ async def _build_graph(self):
26
+ if self.tools:
27
+ config = self.tools.model_dump(exclude_none=True)
28
+ agentr_tools = await load_agentr_tools(config["agentrServers"]) if config.get("agentrServers") else []
29
+ mcp_tools = await load_mcp_tools(config["mcpServers"]) if config.get("mcpServers") else []
30
+ tools = agentr_tools + mcp_tools
31
+ else:
32
+ tools = []
33
+ logger.debug(f"Initialized ReactAgent: name={self.name}, model={self.model}")
32
34
  return create_react_agent(
33
35
  self.llm,
34
36
  tools,
@@ -53,6 +55,10 @@ if __name__ == "__main__":
53
55
  import asyncio
54
56
 
55
57
  agent = ReactAgent(
56
- "Universal React Agent", "You are a helpful assistant", "gpt-4.1", tools=["google-mail_send_email"]
58
+ "Universal React Agent",
59
+ instructions="",
60
+ model="gpt-4o",
61
+ tools=ToolConfig(agentrServers={"google-mail": {"tools": ["send_email"]}}),
57
62
  )
58
- asyncio.run(agent.run_interactive())
63
+ result = asyncio.run(agent.run(user_input="Send an email with the subject 'Hello' to john.doe@example.com"))
64
+ print(result["messages"][-1].content)
@@ -0,0 +1,35 @@
1
+ import json
2
+
3
+ from langchain_mcp_adapters.client import MultiServerMCPClient
4
+
5
+ from universal_mcp.agentr.integration import AgentrIntegration
6
+ from universal_mcp.applications import app_from_slug
7
+ from universal_mcp.tools.adapters import ToolFormat
8
+ from universal_mcp.tools.manager import ToolManager
9
+ from universal_mcp.types import ToolConfig
10
+
11
+
12
+ async def load_agentr_tools(agentr_servers: dict):
13
+ tool_manager = ToolManager()
14
+ for app_name, tool_names in agentr_servers.items():
15
+ app = app_from_slug(app_name)
16
+ integration = AgentrIntegration(name=app_name)
17
+ app_instance = app(integration=integration)
18
+ tool_manager.register_tools_from_app(app_instance, tool_names=tool_names["tools"])
19
+ tools = tool_manager.list_tools(format=ToolFormat.LANGCHAIN)
20
+ return tools
21
+
22
+
23
+ async def load_mcp_tools(mcp_servers: dict):
24
+ client = MultiServerMCPClient(mcp_servers)
25
+ tools = await client.get_tools()
26
+ return tools
27
+
28
+
29
+ async def load_tools(path: str) -> ToolConfig:
30
+ with open(path) as f:
31
+ data = json.load(f)
32
+ config = ToolConfig.model_validate(data)
33
+ agentr_tools = await load_agentr_tools(config.model_dump(exclude_none=True)["agentrServers"])
34
+ mcp_tools = await load_mcp_tools(config.model_dump(exclude_none=True)["mcpServers"])
35
+ return agentr_tools + mcp_tools
universal_mcp/config.py CHANGED
@@ -176,96 +176,3 @@ class ServerConfig(BaseSettings):
176
176
  with open(path) as f:
177
177
  data = json.load(f)
178
178
  return cls.model_validate(data)
179
-
180
-
181
- class ClientTransportConfig(BaseModel):
182
- """Configuration for how an MCP client connects to an MCP server.
183
-
184
- Specifies the transport protocol and its associated parameters, such as
185
- the command for stdio, URL for HTTP-based transports (SSE, streamable_http),
186
- and any necessary headers or environment variables.
187
- """
188
-
189
- transport: str | None = Field(
190
- default=None,
191
- description="The transport protocol (e.g., 'stdio', 'sse', 'streamable_http'). Auto-detected in model_validate if not set.",
192
- )
193
- command: str | None = Field(
194
- default=None, description="The command to execute for 'stdio' transport (e.g., 'python -m mcp_server.run')."
195
- )
196
- args: list[str] = Field(default=[], description="List of arguments for the 'stdio' command.")
197
- env: dict[str, str] = Field(default={}, description="Environment variables to set for the 'stdio' command.")
198
- url: str | None = Field(default=None, description="The URL for 'sse' or 'streamable_http' transport.")
199
- headers: dict[str, str] = Field(
200
- default={}, description="HTTP headers to include for 'sse' or 'streamable_http' transport."
201
- )
202
-
203
- @model_validator(mode="after")
204
- def determine_transport_if_not_set(self) -> Self:
205
- """Determines and sets the transport type if not explicitly provided.
206
-
207
- - If `command` is present, transport is set to 'stdio'.
208
- - If `url` is present, transport is 'streamable_http' if URL ends with '/mcp',
209
- otherwise 'sse' if URL ends with '/sse'.
210
- - Raises ValueError if transport cannot be determined or if neither
211
- `command` nor `url` is provided.
212
- """
213
- if self.command:
214
- self.transport = "stdio"
215
- elif self.url:
216
- # Remove search params from url
217
- url = self.url.split("?")[0]
218
- if url.rstrip("/").endswith("mcp"):
219
- self.transport = "streamable_http"
220
- elif url.rstrip("/").endswith("sse"):
221
- self.transport = "sse"
222
- else:
223
- raise ValueError(f"Unknown transport: {self.url}")
224
- else:
225
- raise ValueError("Either command or url must be provided")
226
- return self
227
-
228
-
229
- class ClientConfig(BaseSettings):
230
- """Configuration for a client application that interacts with MCP servers and an LLM.
231
-
232
- Defines connections to one or more MCP servers (via `mcpServers`) and
233
- optionally, settings for an LLM to be used by the client (e.g., by an agent).
234
- """
235
-
236
- mcpServers: dict[str, ClientTransportConfig] = Field(
237
- ...,
238
- description="Dictionary of MCP server connections. Keys are descriptive names for the server, values are `ClientTransportConfig` objects defining how to connect to each server.",
239
- )
240
- apps: list[AppConfig] = Field(
241
- default=[],
242
- description="List of application configurations to load",
243
- )
244
- store: StoreConfig | None = Field(
245
- default=None,
246
- description="Default credential store configuration for applications that do not define their own specific store.",
247
- )
248
- model: str = Field(
249
- default="openrouter/auto",
250
- description="The model to use for the LLM.",
251
- )
252
-
253
- @classmethod
254
- def load_json_config(cls, path: Path) -> Self:
255
- """Loads client configuration from a JSON file.
256
-
257
- Args:
258
- path (str, optional): The path to the JSON configuration file.
259
- Defaults to "client_config.json".
260
-
261
- Returns:
262
- ClientConfig: An instance of ClientConfig populated with data
263
- from the JSON file.
264
- """
265
- with open(path) as f:
266
- data = json.load(f)
267
- return cls.model_validate(data)
268
-
269
- def save_json_config(self, path: str) -> None:
270
- with open(path, "w") as f:
271
- json.dump(self.model_dump(), f, indent=4)
@@ -12,12 +12,7 @@ from universal_mcp.tools.adapters import (
12
12
  convert_tool_to_openai_tool,
13
13
  )
14
14
  from universal_mcp.tools.tools import Tool
15
- from universal_mcp.types import ToolFormat
16
-
17
- # Constants
18
- DEFAULT_IMPORTANT_TAG = "important"
19
- TOOL_NAME_SEPARATOR = "_"
20
- DEFAULT_APP_NAME = "common"
15
+ from universal_mcp.types import DEFAULT_APP_NAME, DEFAULT_IMPORTANT_TAG, TOOL_NAME_SEPARATOR, ToolFormat
21
16
 
22
17
 
23
18
  def _get_app_and_tool_name(tool_name: str) -> tuple[str, str]:
@@ -31,8 +26,13 @@ def _get_app_and_tool_name(tool_name: str) -> tuple[str, str]:
31
26
  return app_name, tool_name_without_app_name
32
27
 
33
28
 
29
+ def _sanitize_tool_names(tool_names: list[str]) -> list[str]:
30
+ """Sanitize tool names by removing empty strings and converting to lowercase."""
31
+ return [_get_app_and_tool_name(name)[1].lower() for name in tool_names if name]
32
+
33
+
34
34
  def _filter_by_name(tools: list[Tool], tool_names: list[str] | None) -> list[Tool]:
35
- """Filter tools by name using simple string matching.
35
+ """Filter tools by name using set comparison for efficient matching.
36
36
 
37
37
  Args:
38
38
  tools: List of tools to filter.
@@ -45,16 +45,14 @@ def _filter_by_name(tools: list[Tool], tool_names: list[str] | None) -> list[Too
45
45
  return tools
46
46
 
47
47
  logger.debug(f"Filtering tools by names: {tool_names}")
48
- # Convert names to lowercase for case-insensitive matching
49
- tool_names = [name.lower() for name in tool_names]
48
+ tool_names_set = set(_sanitize_tool_names(tool_names))
49
+ logger.debug(f"Tool names set: {tool_names_set}")
50
50
  filtered_tools = []
51
51
  for tool in tools:
52
- for tool_name in tool_names:
53
- if tool_name in tool.name.lower():
54
- filtered_tools.append(tool)
55
- logger.debug(f"Tool '{tool.name}' matched name filter")
56
- break
57
-
52
+ if tool.tool_name.lower() in tool_names_set:
53
+ filtered_tools.append(tool)
54
+ logger.debug(f"Tool '{tool.name}' matched name filter")
55
+ logger.debug(f"Filtered tools: {[tool.name for tool in filtered_tools]}")
58
56
  return filtered_tools
59
57
 
60
58
 
@@ -200,11 +198,6 @@ class ToolManager:
200
198
  app_name: Application name to group the tools under.
201
199
  """
202
200
  for tool in tools:
203
- app_name, tool_name = _get_app_and_tool_name(tool.name)
204
-
205
- # Add prefix to tool name, if not already present
206
- tool.name = f"{app_name}{TOOL_NAME_SEPARATOR}{tool_name}"
207
- tool.tags.append(app_name)
208
201
  self.add_tool(tool)
209
202
 
210
203
  def remove_tool(self, name: str) -> bool:
@@ -259,14 +252,14 @@ class ToolManager:
259
252
 
260
253
  try:
261
254
  tool_instance = Tool.from_function(function)
262
- tool_instance.name = f"{app.name}{TOOL_NAME_SEPARATOR}{tool_instance.name}"
255
+ tool_instance.app_name = app.name
263
256
  if app.name not in tool_instance.tags:
264
257
  tool_instance.tags.append(app.name)
265
258
  tools.append(tool_instance)
266
259
  except Exception as e:
267
260
  tool_name = getattr(function, "__name__", "unknown")
268
261
  logger.error(f"Failed to create Tool from '{tool_name}' in {app.name}: {e}")
269
-
262
+ print([tool.name for tool in tools])
270
263
  if tags:
271
264
  tools = _filter_by_tags(tools, tags)
272
265
 
@@ -7,6 +7,7 @@ from pydantic import BaseModel, Field, create_model
7
7
 
8
8
  from universal_mcp.exceptions import NotAuthorizedError, ToolError
9
9
  from universal_mcp.tools.docstring_parser import parse_docstring
10
+ from universal_mcp.types import TOOL_NAME_SEPARATOR
10
11
 
11
12
  from .func_metadata import FuncMetadata
12
13
 
@@ -31,8 +32,9 @@ class Tool(BaseModel):
31
32
  """Internal tool registration info."""
32
33
 
33
34
  fn: Callable[..., Any] = Field(exclude=True)
34
- name: str = Field(description="Name of the tool")
35
- description: str = Field(description="Summary line from the tool's docstring")
35
+ app_name: str | None = Field(default=None, description="Name of the app that the tool belongs to")
36
+ tool_name: str = Field(description="Name of the tool")
37
+ description: str | None = Field(default=None, description="Summary line from the tool's docstring")
36
38
  args_description: dict[str, str] = Field(
37
39
  default_factory=dict, description="Descriptions of arguments from the docstring"
38
40
  )
@@ -44,11 +46,15 @@ class Tool(BaseModel):
44
46
  tags: list[str] = Field(default_factory=list, description="Tags for categorizing the tool")
45
47
  parameters: dict[str, Any] = Field(description="JSON schema for tool parameters")
46
48
  output_schema: dict[str, Any] | None = Field(default=None, description="JSON schema for tool output")
47
- fn_metadata: FuncMetadata = Field(
48
- description="Metadata about the function including a pydantic model for tool arguments"
49
+ fn_metadata: FuncMetadata | None = Field(
50
+ default=None, description="Metadata about the function including a pydantic model for tool arguments"
49
51
  )
50
52
  is_async: bool = Field(description="Whether the tool is async")
51
53
 
54
+ @property
55
+ def name(self) -> str:
56
+ return f"{self.app_name}{TOOL_NAME_SEPARATOR}{self.tool_name}" if self.app_name else self.tool_name
57
+
52
58
  @classmethod
53
59
  def from_function(
54
60
  cls,
@@ -81,7 +87,7 @@ class Tool(BaseModel):
81
87
 
82
88
  return cls(
83
89
  fn=fn,
84
- name=func_name,
90
+ tool_name=func_name,
85
91
  description=parsed_doc["summary"],
86
92
  args_description=simple_args_descriptions,
87
93
  returns_description=parsed_doc["returns"],
universal_mcp/types.py CHANGED
@@ -1,4 +1,12 @@
1
1
  from enum import Enum
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel
5
+
6
+ # Constants
7
+ DEFAULT_IMPORTANT_TAG = "important"
8
+ TOOL_NAME_SEPARATOR = "__"
9
+ DEFAULT_APP_NAME = "common"
2
10
 
3
11
 
4
12
  class ToolFormat(str, Enum):
@@ -8,3 +16,20 @@ class ToolFormat(str, Enum):
8
16
  MCP = "mcp"
9
17
  LANGCHAIN = "langchain"
10
18
  OPENAI = "openai"
19
+
20
+
21
+ class AgentrConnection(BaseModel):
22
+ tools: list[str]
23
+
24
+
25
+ class MCPConnection(BaseModel):
26
+ transport: Literal["stdio", "sse", "streamable-http"]
27
+ command: str | None = None
28
+ args: list[str] | None = None
29
+ url: str | None = None
30
+ headers: dict[str, str] | None = None
31
+
32
+
33
+ class ToolConfig(BaseModel):
34
+ mcpServers: dict[str, MCPConnection] | None = None
35
+ agentrServers: dict[str, AgentrConnection] | None = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.24rc4
3
+ Version: 0.1.24rc6
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
@@ -8,7 +8,7 @@ License-File: LICENSE
8
8
  Requires-Python: >=3.11
9
9
  Requires-Dist: black>=25.1.0
10
10
  Requires-Dist: cookiecutter>=2.6.0
11
- Requires-Dist: gql[all]==4.0.0
11
+ Requires-Dist: gql>=4.0.0
12
12
  Requires-Dist: jinja2>=3.1.3
13
13
  Requires-Dist: jsonref>=1.1.0
14
14
  Requires-Dist: keyring>=25.6.0
@@ -19,24 +19,21 @@ Requires-Dist: langgraph>=0.5.2
19
19
  Requires-Dist: langsmith>=0.4.5
20
20
  Requires-Dist: loguru>=0.7.3
21
21
  Requires-Dist: mcp>=1.10.0
22
- Requires-Dist: mkdocs-material>=9.6.15
23
- Requires-Dist: mkdocs>=1.6.1
24
22
  Requires-Dist: posthog>=3.24.0
25
23
  Requires-Dist: pydantic-settings>=2.8.1
26
24
  Requires-Dist: pydantic>=2.11.1
27
25
  Requires-Dist: pyyaml>=6.0.2
28
26
  Requires-Dist: rich>=14.0.0
29
27
  Requires-Dist: streamlit>=1.46.1
30
- Requires-Dist: ty>=0.0.1a17
31
28
  Requires-Dist: typer>=0.15.2
32
29
  Provides-Extra: dev
33
30
  Requires-Dist: litellm>=1.30.7; extra == 'dev'
34
- Requires-Dist: mypy>=1.16.0; extra == 'dev'
35
31
  Requires-Dist: pre-commit>=4.2.0; extra == 'dev'
36
32
  Requires-Dist: pyright>=1.1.398; extra == 'dev'
37
33
  Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
38
34
  Requires-Dist: pytest>=8.3.5; extra == 'dev'
39
35
  Requires-Dist: ruff>=0.11.4; extra == 'dev'
36
+ Requires-Dist: ty>=0.0.1a17; extra == 'dev'
40
37
  Provides-Extra: docs
41
38
  Requires-Dist: mkdocs-glightbox>=0.4.0; extra == 'docs'
42
39
  Requires-Dist: mkdocs-material[imaging]>=9.5.45; extra == 'docs'
@@ -1,11 +1,11 @@
1
1
  universal_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  universal_mcp/analytics.py,sha256=RzS88HSvJRGMjdJeLHnOgWzfKSb1jVnvOcD7NHqfERw,3733
3
3
  universal_mcp/cli.py,sha256=pPnIWLhSrLV9ukI8YAg2znehCR3VovhEkmh8XkRT3MU,2505
4
- universal_mcp/config.py,sha256=pkKs0gST65umzmNEvjHiOAtmiBaaICi45WG4Z0My0ak,11983
4
+ universal_mcp/config.py,sha256=lOlDAgQMT7f6VymmsuCP9sYLlxGKj0hDF3hFcJ2nzS4,8135
5
5
  universal_mcp/exceptions.py,sha256=Uen8UFgLHGlSwXgRUyF-nhqTwdiBuL3okgBVRV2AgtA,2150
6
6
  universal_mcp/logger.py,sha256=VmH_83efpErLEDTJqz55Dp0dioTXfGvMBLZUx5smOLc,2116
7
7
  universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- universal_mcp/types.py,sha256=dVK7uSMuhvx5Xk6L7GGdjeaAIKiEwQskTmaVFwIS8LQ,176
8
+ universal_mcp/types.py,sha256=jeUEkUnwdGWo3T_qSRSF83u0fYpuydaWzdKlCYBlCQA,770
9
9
  universal_mcp/agentr/README.md,sha256=xXM8JzPyrM2__pGhxHrGEUn9uP2y2bdF00wwcQtBUCI,6441
10
10
  universal_mcp/agentr/__init__.py,sha256=ogOhH_OJwkoUZu_2nQJc7-vEGmYQxEjOE511-6ubrX0,217
11
11
  universal_mcp/agentr/agentr.py,sha256=JfawuREfXAyeNUE7o58DzTPhmQXuwsB_Da7c1Gf3Qxw,1059
@@ -15,12 +15,13 @@ universal_mcp/agentr/registry.py,sha256=b9sr5JyT3HLj3e7GFpdXpT7ofGwLQc--y8k2DqF5
15
15
  universal_mcp/agentr/server.py,sha256=bIPmHMiKKwnUYnxmfZVRh1thcn7Rytm_-bNiXTfANzc,2098
16
16
  universal_mcp/agents/__init__.py,sha256=vgixOLTCcCmSweENV7GSAuOPyHXlE4XAbvOXyr4MrRA,185
17
17
  universal_mcp/agents/auto.py,sha256=o__71BCOHSfaj7Xt0PhsamVXdeP4o7irhtmu1q6-3Fo,25336
18
- universal_mcp/agents/base.py,sha256=U5JtpOopmUi73qcxtY9T2qJpYD7e6c62iVlIr3m5Chc,3430
18
+ universal_mcp/agents/base.py,sha256=aplcZ-OKva3hFMB5uzoAPCB0ZDh3BL3FlJV39sJYYZ8,4057
19
19
  universal_mcp/agents/cli.py,sha256=7GdRBpu9rhZPiC2vaNQXWI7K-0yCnvdlmE0IFpvy2Gk,539
20
20
  universal_mcp/agents/hil.py,sha256=CTgX7CoFEyTFIaNaL-id2WALOPd0VBb79pHkQK8quM8,3671
21
21
  universal_mcp/agents/llm.py,sha256=YNxN43bVhGfdYs09yPkdkGCKJkj-2UNqkB1EFmtnUS4,309
22
- universal_mcp/agents/react.py,sha256=6L--LcuU5Ityi2UiZSYJWgp-lXGkxvpsx8mjvpoNRBQ,2021
22
+ universal_mcp/agents/react.py,sha256=cpE4wzySnyEdhz-c1T1FDA3w68nRByz7yWFt8FefUBo,2361
23
23
  universal_mcp/agents/simple.py,sha256=UfmQIIff--_Y0DQ6oivRciHqSZvRqy_qwQn_UYVzYy8,1146
24
+ universal_mcp/agents/tools.py,sha256=7Vdw0VZYxXVAzAYSpRKWHzVl9Ll6NOnVRlc4cTXguUQ,1335
24
25
  universal_mcp/agents/utils.py,sha256=7kwFpD0Rv6JqHG-LlNCVwSu_xRX-N119mUmiBroHJL4,4109
25
26
  universal_mcp/agents/codeact/__init__.py,sha256=5D_I3lI_3tWjZERRoFav_bPe9UDaJ53pDzZYtyixg3E,10097
26
27
  universal_mcp/agents/codeact/sandbox.py,sha256=lGRzhuXTHCB1qauuOI3bH1-fPTsyL6Lf9EmMIz4C2xQ,1039
@@ -42,9 +43,9 @@ universal_mcp/tools/__init__.py,sha256=jC8hsqfTdtn32yU57AVFUXiU3ZmUOCfCERSCaNEIH
42
43
  universal_mcp/tools/adapters.py,sha256=YJ2oqgc8JgmtsdRRtvO-PO0Q0bKqTJ4Y3J6yxlESoTo,3947
43
44
  universal_mcp/tools/docstring_parser.py,sha256=efEOE-ME7G5Jbbzpn7pN2xNuyu2M5zfZ1Tqu1lRB0Gk,8392
44
45
  universal_mcp/tools/func_metadata.py,sha256=F4jd--hoZWKPBbZihVtluYKUsIdXdq4a0VWRgMl5k-Q,10838
45
- universal_mcp/tools/manager.py,sha256=MajVskIptgXv1uZzwnSRycj1TSi7nhn4ebNSRkSSEDs,10455
46
+ universal_mcp/tools/manager.py,sha256=24Rkn5Uvv_AuYAtjeMq986bJ7uzTaGE1290uB9eDtRE,10435
46
47
  universal_mcp/tools/registry.py,sha256=XsmVZL1rY5XgIBPTmvKKBWFLAvB3d9LfYMb11b4wSPI,1169
47
- universal_mcp/tools/tools.py,sha256=1Q8bKiqj1E_-swvjmNHv16Orpd4p_HQtMKGxfqPmoPI,4570
48
+ universal_mcp/tools/tools.py,sha256=Lk-wUO3rfhwdxaRANtC7lQr5fXi7nclf0oHzxNAb79Q,4927
48
49
  universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
49
50
  universal_mcp/utils/common.py,sha256=3aJK3AnBkmYf-dbsFLaEu_dGuXQ0Qi2HuqYTueLVhXQ,10968
50
51
  universal_mcp/utils/installation.py,sha256=PU_GfHPqzkumKk-xG4L9CkBzSmABxmchwblZkx-zY-I,7204
@@ -64,8 +65,8 @@ universal_mcp/utils/openapi/readme.py,sha256=R2Jp7DUXYNsXPDV6eFTkLiy7MXbSULUj1vH
64
65
  universal_mcp/utils/openapi/test_generator.py,sha256=h44gQXEXmrw4pD3-XNHKB7T9c2lDomqrJxVO6oszCqM,12186
65
66
  universal_mcp/utils/templates/README.md.j2,sha256=Mrm181YX-o_-WEfKs01Bi2RJy43rBiq2j6fTtbWgbTA,401
66
67
  universal_mcp/utils/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
67
- universal_mcp-0.1.24rc4.dist-info/METADATA,sha256=q3DM_RctCJdHxfeY2CwcRiH5VuESYHr7sb_GB-c4vRw,3116
68
- universal_mcp-0.1.24rc4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
- universal_mcp-0.1.24rc4.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
70
- universal_mcp-0.1.24rc4.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
71
- universal_mcp-0.1.24rc4.dist-info/RECORD,,
68
+ universal_mcp-0.1.24rc6.dist-info/METADATA,sha256=5KFnUe9auDhLti6G6uogy93a3RuJIBc9un01LNoVBgU,3015
69
+ universal_mcp-0.1.24rc6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
70
+ universal_mcp-0.1.24rc6.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
71
+ universal_mcp-0.1.24rc6.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
72
+ universal_mcp-0.1.24rc6.dist-info/RECORD,,