universal-mcp 0.1.24rc9__py3-none-any.whl → 0.1.24rc11__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/registry.py +2 -2
- universal_mcp/agents/auto.py +5 -5
- universal_mcp/agents/autoagent/__main__.py +5 -4
- universal_mcp/agents/autoagent/graph.py +20 -27
- universal_mcp/agents/autoagent/state.py +0 -1
- universal_mcp/agents/base.py +28 -1
- universal_mcp/agents/llm.py +4 -2
- universal_mcp/agents/react.py +3 -3
- universal_mcp/client/transport.py +2 -2
- universal_mcp/tools/manager.py +2 -2
- universal_mcp/utils/openapi/api_generator.py +4 -1
- universal_mcp/utils/openapi/openapi.py +29 -9
- universal_mcp/utils/openapi/postprocessor.py +27 -24
- universal_mcp/utils/openapi/test_generator.py +4 -2
- {universal_mcp-0.1.24rc9.dist-info → universal_mcp-0.1.24rc11.dist-info}/METADATA +1 -1
- {universal_mcp-0.1.24rc9.dist-info → universal_mcp-0.1.24rc11.dist-info}/RECORD +19 -19
- {universal_mcp-0.1.24rc9.dist-info → universal_mcp-0.1.24rc11.dist-info}/WHEEL +0 -0
- {universal_mcp-0.1.24rc9.dist-info → universal_mcp-0.1.24rc11.dist-info}/entry_points.txt +0 -0
- {universal_mcp-0.1.24rc9.dist-info → universal_mcp-0.1.24rc11.dist-info}/licenses/LICENSE +0 -0
universal_mcp/agentr/registry.py
CHANGED
@@ -132,10 +132,10 @@ class AgentrRegistry(ToolRegistry):
|
|
132
132
|
# Clear tools from tool manager before loading new tools
|
133
133
|
self.tool_manager.clear_tools()
|
134
134
|
if isinstance(tools, ToolConfig):
|
135
|
-
|
135
|
+
logger.info("Loading tools from tool config")
|
136
136
|
self._load_tools_from_tool_config(tools, self.tool_manager)
|
137
137
|
else:
|
138
|
-
|
138
|
+
logger.info("Loading tools from list")
|
139
139
|
self._load_agentr_tools_from_list(tools, self.tool_manager)
|
140
140
|
loaded_tools = self.tool_manager.list_tools(format=format)
|
141
141
|
logger.info(f"Exporting {len(loaded_tools)} tools to {format} format")
|
universal_mcp/agents/auto.py
CHANGED
@@ -521,7 +521,7 @@ Be friendly and concise, but list each set of apps clearly. Do not return any ot
|
|
521
521
|
return result
|
522
522
|
|
523
523
|
# Get all available apps from platform manager
|
524
|
-
available_apps =
|
524
|
+
available_apps = self.app_registry.list_apps()
|
525
525
|
|
526
526
|
logger.info(f"Found {len(available_apps)} available apps")
|
527
527
|
|
@@ -567,9 +567,9 @@ if __name__ == "__main__":
|
|
567
567
|
|
568
568
|
agent = AutoAgent("Auto Agent", instructions, "azure/gpt-4.1", app_registry=app_registry)
|
569
569
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
570
|
+
logger.info("AutoAgent created successfully!")
|
571
|
+
logger.info(f"Agent name: {agent.name}")
|
572
|
+
logger.info(f"Agent instructions: {agent.instructions}")
|
573
|
+
logger.info(f"Agent model: {agent.model}")
|
574
574
|
|
575
575
|
asyncio.run(agent.run_interactive())
|
@@ -8,13 +8,14 @@ async def main():
|
|
8
8
|
agent = AutoAgent(
|
9
9
|
name="autoagent",
|
10
10
|
instructions="You are a helpful assistant that can use tools to help the user.",
|
11
|
-
model="
|
11
|
+
model="azure/gpt-4.1",
|
12
12
|
tool_registry=AgentrRegistry(),
|
13
13
|
)
|
14
|
-
|
15
|
-
user_input="
|
14
|
+
await agent.invoke(
|
15
|
+
user_input="Please send the email from google-mail to manoj@agentr.dev, with subject hello and body hello from auto",
|
16
|
+
thread_id="12345",
|
16
17
|
)
|
17
|
-
|
18
|
+
# from loguru import logger; logger.debug(result)
|
18
19
|
|
19
20
|
|
20
21
|
if __name__ == "__main__":
|
@@ -33,43 +33,40 @@ async def build_graph(tool_registry: ToolRegistry, instructions: str = ""):
|
|
33
33
|
"""Ask the user a question. Use this tool to ask the user for any missing information for performing a task, or when you have multiple apps to choose from for performing a task."""
|
34
34
|
full_question = question
|
35
35
|
return f"ASKING_USER: {full_question}"
|
36
|
-
|
36
|
+
|
37
37
|
@tool()
|
38
38
|
async def load_tools(tools: list[str]) -> list[str]:
|
39
39
|
"""Choose the tools you want to use by passing their tool ids. Loads the tools for the chosen tools and returns the tool ids."""
|
40
40
|
return tools
|
41
|
-
|
42
41
|
|
43
42
|
async def call_model(
|
44
43
|
state: State,
|
45
44
|
runtime: Runtime[Context],
|
46
45
|
):
|
47
|
-
system_prompt =
|
46
|
+
system_prompt = SYSTEM_PROMPT
|
48
47
|
app_ids = await tool_registry.list_all_apps()
|
49
48
|
connections = tool_registry.client.list_my_connections()
|
50
49
|
connection_ids = set([connection["app_id"] for connection in connections])
|
51
|
-
connected_apps = [app[
|
52
|
-
unconnected_apps = [app[
|
53
|
-
app_id_descriptions = "These are the apps connected to the user's account:\n" + "\n".join(
|
50
|
+
connected_apps = [app["id"] for app in app_ids if app["id"] in connection_ids]
|
51
|
+
unconnected_apps = [app["id"] for app in app_ids if app["id"] not in connection_ids]
|
52
|
+
app_id_descriptions = "These are the apps connected to the user's account:\n" + "\n".join(
|
53
|
+
[f"{app}" for app in connected_apps]
|
54
|
+
)
|
54
55
|
if unconnected_apps:
|
55
|
-
app_id_descriptions += "\n\nOther (not connected) apps: " + "\n".join(
|
56
|
-
|
56
|
+
app_id_descriptions += "\n\nOther (not connected) apps: " + "\n".join(
|
57
|
+
[f"{app}" for app in unconnected_apps]
|
58
|
+
)
|
59
|
+
|
57
60
|
system_prompt = system_prompt.format(system_time=datetime.now(tz=UTC).isoformat(), app_ids=app_id_descriptions)
|
58
|
-
|
61
|
+
|
59
62
|
messages = [{"role": "system", "content": system_prompt + "\n" + instructions}, *state["messages"]]
|
60
63
|
model = load_chat_model(runtime.context.model)
|
61
64
|
# Load tools from tool registry
|
62
65
|
loaded_tools = await tool_registry.export_tools(tools=state["selected_tool_ids"], format=ToolFormat.LANGCHAIN)
|
63
66
|
model_with_tools = model.bind_tools([search_tools, ask_user, load_tools, *loaded_tools], tool_choice="auto")
|
64
67
|
response_raw = model_with_tools.invoke(messages)
|
65
|
-
|
66
|
-
|
67
|
-
if key in token_usage:
|
68
|
-
token_usage[key] += response_raw.usage_metadata[key]
|
69
|
-
else:
|
70
|
-
token_usage[key] = response_raw.usage_metadata[key]
|
71
|
-
response = cast(AIMessage, response_raw)
|
72
|
-
return {"messages": [response], "token_usage": token_usage}
|
68
|
+
response = cast(AIMessage, response_raw)
|
69
|
+
return {"messages": [response]}
|
73
70
|
|
74
71
|
# Define the conditional edge that determines whether to continue or not
|
75
72
|
def should_continue(state: State):
|
@@ -108,15 +105,15 @@ async def build_graph(tool_registry: ToolRegistry, instructions: str = ""):
|
|
108
105
|
tools = await search_tools.ainvoke(tool_call["args"])
|
109
106
|
outputs.append(
|
110
107
|
ToolMessage(
|
111
|
-
content=json.dumps(tools)+"\n\nUse the load_tools tool to load the tools you want to use.",
|
108
|
+
content=json.dumps(tools) + "\n\nUse the load_tools tool to load the tools you want to use.",
|
112
109
|
name=tool_call["name"],
|
113
110
|
tool_call_id=tool_call["id"],
|
114
111
|
)
|
115
112
|
)
|
116
|
-
|
113
|
+
|
117
114
|
elif tool_call["name"] == load_tools.name:
|
118
115
|
tool_ids = await load_tools.ainvoke(tool_call["args"])
|
119
|
-
|
116
|
+
|
120
117
|
outputs.append(
|
121
118
|
ToolMessage(
|
122
119
|
content=json.dumps(tool_ids),
|
@@ -132,23 +129,19 @@ async def build_graph(tool_registry: ToolRegistry, instructions: str = ""):
|
|
132
129
|
ToolMessage(
|
133
130
|
content=json.dumps(tool_result),
|
134
131
|
name=tool_call["name"],
|
135
|
-
|
136
|
-
)
|
132
|
+
tool_call_id=tool_call["id"],
|
137
133
|
)
|
134
|
+
)
|
138
135
|
except Exception as e:
|
139
136
|
outputs.append(
|
140
137
|
ToolMessage(
|
141
|
-
content=json.dumps("Error: "+str(e)),
|
138
|
+
content=json.dumps("Error: " + str(e)),
|
142
139
|
name=tool_call["name"],
|
143
140
|
tool_call_id=tool_call["id"],
|
144
141
|
)
|
145
142
|
)
|
146
143
|
return {"messages": outputs, "selected_tool_ids": tool_ids}
|
147
144
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
145
|
builder = StateGraph(State, context_schema=Context)
|
153
146
|
|
154
147
|
builder.add_node("agent", call_model)
|
universal_mcp/agents/base.py
CHANGED
@@ -30,13 +30,37 @@ class BaseAgent:
|
|
30
30
|
|
31
31
|
async def stream(self, thread_id: str, user_input: str):
|
32
32
|
await self.ainit()
|
33
|
-
async for event,
|
33
|
+
async for event, metadata in self._graph.astream(
|
34
34
|
{"messages": [{"role": "user", "content": user_input}]},
|
35
35
|
config={"configurable": {"thread_id": thread_id}},
|
36
|
+
context={"system_prompt": self.instructions, "model": self.model},
|
36
37
|
stream_mode="messages",
|
38
|
+
stream_usage=True,
|
37
39
|
):
|
40
|
+
# Only forward assistant token chunks that are not tool-related.
|
38
41
|
event = cast(AIMessageChunk, event)
|
42
|
+
if "finish_reason" in event.response_metadata:
|
43
|
+
# Got LLM finish reason ignore it
|
44
|
+
# logger.debug(f"Finish event: {event}")
|
45
|
+
continue
|
46
|
+
|
47
|
+
# Skip chunks that correspond to tool calls or tool execution phases
|
48
|
+
# - tool_call_chunks present => model is emitting tool call(s)
|
49
|
+
# - metadata tags or node names may indicate tool/quiet phases
|
50
|
+
tags = metadata.get("tags", []) if isinstance(metadata, dict) else []
|
51
|
+
|
52
|
+
is_quiet = isinstance(tags, list) and ("quiet" in tags)
|
53
|
+
|
54
|
+
if is_quiet:
|
55
|
+
continue
|
56
|
+
|
57
|
+
# Emit only the token chunks for the final assistant message.
|
58
|
+
# logger.debug(f"Event: {event}, Metadata: {metadata}")
|
39
59
|
yield event
|
60
|
+
# Send a final finished message
|
61
|
+
# The last event would be finish
|
62
|
+
event = cast(AIMessageChunk, event)
|
63
|
+
yield event
|
40
64
|
|
41
65
|
async def stream_interactive(self, thread_id: str, user_input: str):
|
42
66
|
await self.ainit()
|
@@ -101,4 +125,7 @@ class BaseAgent:
|
|
101
125
|
self.cli.display_info("\nGoodbye! 👋")
|
102
126
|
break
|
103
127
|
except Exception as e:
|
128
|
+
import traceback
|
129
|
+
|
130
|
+
traceback.print_exc()
|
104
131
|
self.cli.display_error(f"An error occurred: {str(e)}")
|
universal_mcp/agents/llm.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from langchain_anthropic import ChatAnthropic
|
2
2
|
from langchain_core.language_models import BaseChatModel
|
3
|
-
from langchain_google_vertexai.model_garden import ChatAnthropicVertex
|
4
3
|
from langchain_google_vertexai import ChatVertexAI
|
4
|
+
from langchain_google_vertexai.model_garden import ChatAnthropicVertex
|
5
5
|
from langchain_openai import AzureChatOpenAI
|
6
6
|
|
7
7
|
|
@@ -26,5 +26,7 @@ def load_chat_model(fully_specified_name: str, tags: list[str] | None = None) ->
|
|
26
26
|
|
27
27
|
|
28
28
|
if __name__ == "__main__":
|
29
|
+
from loguru import logger
|
30
|
+
|
29
31
|
llm = load_chat_model("azure/gpt-4.1")
|
30
|
-
|
32
|
+
logger.info(llm.invoke("Hello, world!"))
|
universal_mcp/agents/react.py
CHANGED
@@ -38,9 +38,9 @@ class ReactAgent(BaseAgent):
|
|
38
38
|
if config.get("agentrServers")
|
39
39
|
else []
|
40
40
|
)
|
41
|
-
|
41
|
+
logger.debug(agentr_tools)
|
42
42
|
mcp_tools = await load_mcp_tools(config["mcpServers"]) if config.get("mcpServers") else []
|
43
|
-
|
43
|
+
logger.debug(mcp_tools)
|
44
44
|
tools = agentr_tools + mcp_tools
|
45
45
|
else:
|
46
46
|
tools = []
|
@@ -81,4 +81,4 @@ if __name__ == "__main__":
|
|
81
81
|
result = asyncio.run(
|
82
82
|
agent.invoke(user_input="Send an email with the subject 'testing react agent' to manoj@agentr.dev")
|
83
83
|
)
|
84
|
-
|
84
|
+
logger.info(result["messages"][-1].content)
|
@@ -66,7 +66,7 @@ class ClientTransport:
|
|
66
66
|
|
67
67
|
async def _callback_handler(self) -> tuple[str, str | None]:
|
68
68
|
"""Handles the OAuth callback by waiting for and returning auth details."""
|
69
|
-
|
69
|
+
logger.info("⏳ Waiting for authorization callback...")
|
70
70
|
try:
|
71
71
|
auth_code = self.callback_server.wait_for_callback(timeout=300)
|
72
72
|
return auth_code, self.callback_server.get_state()
|
@@ -86,7 +86,7 @@ class ClientTransport:
|
|
86
86
|
|
87
87
|
async def _default_redirect_handler(self, authorization_url: str) -> None:
|
88
88
|
"""Default handler for OAuth redirects; opens URL in a web browser."""
|
89
|
-
|
89
|
+
logger.info(f"Opening browser for authorization: {authorization_url}")
|
90
90
|
webbrowser.open(authorization_url)
|
91
91
|
|
92
92
|
async def initialize(self, exit_stack: AsyncExitStack) -> None:
|
universal_mcp/tools/manager.py
CHANGED
@@ -43,7 +43,7 @@ def _filter_by_name(tools: list[Tool], tool_names: list[str] | None) -> list[Too
|
|
43
43
|
"""
|
44
44
|
if not tool_names:
|
45
45
|
return tools
|
46
|
-
|
46
|
+
logger.debug(f"All tools: {[tool.name for tool in tools]}")
|
47
47
|
logger.debug(f"Filtering tools by names: {tool_names}")
|
48
48
|
tool_names_set = set(_sanitize_tool_names(tool_names))
|
49
49
|
logger.debug(f"Tool names set: {tool_names_set}")
|
@@ -259,7 +259,7 @@ class ToolManager:
|
|
259
259
|
except Exception as e:
|
260
260
|
tool_name = getattr(function, "__name__", "unknown")
|
261
261
|
logger.error(f"Failed to create Tool from '{tool_name}' in {app.name}: {e}")
|
262
|
-
|
262
|
+
# logger.debug([tool.name for tool in tools])
|
263
263
|
if tags:
|
264
264
|
tools = _filter_by_tags(tools, tags)
|
265
265
|
|
@@ -11,7 +11,10 @@ from universal_mcp.utils.openapi.openapi import generate_api_client, generate_sc
|
|
11
11
|
|
12
12
|
def echo(message: str, err: bool = False) -> None:
|
13
13
|
"""Echo a message to the console, with optional error flag."""
|
14
|
-
|
14
|
+
if err:
|
15
|
+
logger.error(message)
|
16
|
+
else:
|
17
|
+
logger.info(message)
|
15
18
|
|
16
19
|
|
17
20
|
def validate_and_load_schema(schema_path: Path) -> dict:
|
@@ -179,7 +179,9 @@ def _get_or_create_response_model(
|
|
179
179
|
|
180
180
|
except Exception as e:
|
181
181
|
# If model generation fails, log and continue with fallback
|
182
|
-
|
182
|
+
from loguru import logger
|
183
|
+
|
184
|
+
logger.warning(f"Could not generate model for {method.upper()} {path}: {e}")
|
183
185
|
|
184
186
|
return None
|
185
187
|
|
@@ -486,7 +488,9 @@ def _generate_path_params(path: str) -> list[Parameters]:
|
|
486
488
|
)
|
487
489
|
)
|
488
490
|
except Exception as e:
|
489
|
-
|
491
|
+
from loguru import logger
|
492
|
+
|
493
|
+
logger.error(f"Error generating path parameters {param_name}: {e}")
|
490
494
|
raise e
|
491
495
|
return parameters
|
492
496
|
|
@@ -919,11 +923,11 @@ def _generate_method_code(path, method, operation):
|
|
919
923
|
|
920
924
|
# Combine required and optional arguments FOR DOCSTRING (as before, without types)
|
921
925
|
args = required_args + optional_args
|
922
|
-
|
926
|
+
# from loguru import logger; logger.debug(f"Final combined args for DOCSTRING: {args}") # DEBUG
|
923
927
|
|
924
928
|
# Combine required and optional arguments FOR SIGNATURE (with types)
|
925
929
|
signature_args_combined_typed = signature_required_args_typed + signature_optional_args_typed
|
926
|
-
|
930
|
+
# from loguru import logger; logger.debug(f"Final combined args for SIGNATURE: {signature_args_combined_typed}") # DEBUG
|
927
931
|
|
928
932
|
# ----- Build Docstring -----
|
929
933
|
# This section constructs the entire docstring for the generated method,
|
@@ -1288,7 +1292,11 @@ def generate_api_client(schema, class_name: str | None = None, filter_config_pat
|
|
1288
1292
|
filter_config = None
|
1289
1293
|
if filter_config_path:
|
1290
1294
|
filter_config = load_filter_config(filter_config_path)
|
1291
|
-
|
1295
|
+
from loguru import logger
|
1296
|
+
|
1297
|
+
logger.info(
|
1298
|
+
f"Loaded filter configuration from {filter_config_path} with {len(filter_config)} path specifications"
|
1299
|
+
)
|
1292
1300
|
|
1293
1301
|
methods = []
|
1294
1302
|
method_names = []
|
@@ -1345,19 +1353,29 @@ def generate_api_client(schema, class_name: str | None = None, filter_config_pat
|
|
1345
1353
|
if method in ["get", "post", "put", "delete", "patch", "options", "head"]:
|
1346
1354
|
# Apply filter configuration
|
1347
1355
|
if not should_process_operation(path, method, filter_config):
|
1348
|
-
|
1356
|
+
from loguru import logger
|
1357
|
+
|
1358
|
+
logger.info(
|
1359
|
+
f"Skipping method generation for '{method.upper()} {path}' due to filter configuration."
|
1360
|
+
)
|
1349
1361
|
skipped_count += 1
|
1350
1362
|
continue
|
1351
1363
|
|
1352
1364
|
operation = path_info[method]
|
1353
|
-
|
1365
|
+
from loguru import logger
|
1366
|
+
|
1367
|
+
logger.info(f"Generating method for: {method.upper()} {path}")
|
1354
1368
|
method_code, func_name = _generate_method_code(path, method, operation)
|
1355
1369
|
methods.append(method_code)
|
1356
1370
|
method_names.append(func_name)
|
1357
1371
|
processed_count += 1
|
1358
1372
|
|
1359
1373
|
if filter_config is not None:
|
1360
|
-
|
1374
|
+
from loguru import logger
|
1375
|
+
|
1376
|
+
logger.info(
|
1377
|
+
f"Selective generation complete: {processed_count} methods generated, {skipped_count} methods skipped."
|
1378
|
+
)
|
1361
1379
|
|
1362
1380
|
# Generate list_tools method with all the function names
|
1363
1381
|
tools_list = ",\n ".join([f"self.{name}" for name in method_names])
|
@@ -1453,4 +1471,6 @@ if __name__ == "__main__":
|
|
1453
1471
|
|
1454
1472
|
schema = load_schema("openapi.yaml")
|
1455
1473
|
code = generate_api_client(schema)
|
1456
|
-
|
1474
|
+
from loguru import logger
|
1475
|
+
|
1476
|
+
logger.info(code)
|
@@ -55,26 +55,28 @@ def add_hint_tags_to_docstrings(input_path: str, output_path: str):
|
|
55
55
|
llm_failures
|
56
56
|
|
57
57
|
total_functions += 1
|
58
|
-
|
58
|
+
from loguru import logger
|
59
|
+
|
60
|
+
logger.info(f"Processing function: {node.name} ({total_functions})")
|
59
61
|
|
60
62
|
http_method = self._find_http_method(node)
|
61
63
|
tag_to_add = None
|
62
64
|
|
63
65
|
if http_method:
|
64
66
|
functions_with_http_methods += 1
|
65
|
-
|
67
|
+
logger.debug(f"Found HTTP method: {http_method.upper()}")
|
66
68
|
|
67
69
|
# Use simple agent to decide tag
|
68
|
-
|
70
|
+
logger.debug("Calling LLM to determine tag...")
|
69
71
|
tag_to_add = self._get_tag_suggestion_from_agent(node, http_method)
|
70
72
|
|
71
73
|
if tag_to_add:
|
72
74
|
functions_processed_by_llm += 1
|
73
|
-
|
75
|
+
logger.info(f"LLM suggested tags: {tag_to_add}")
|
74
76
|
else:
|
75
|
-
|
77
|
+
logger.warning("LLM failed or returned invalid response")
|
76
78
|
else:
|
77
|
-
|
79
|
+
logger.debug("No HTTP method found - skipping")
|
78
80
|
|
79
81
|
if tag_to_add:
|
80
82
|
docstring = ast.get_docstring(node, clean=False)
|
@@ -98,13 +100,13 @@ def add_hint_tags_to_docstrings(input_path: str, output_path: str):
|
|
98
100
|
if isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Constant):
|
99
101
|
node.body[0].value.value = new_docstring
|
100
102
|
functions_tagged += 1
|
101
|
-
|
103
|
+
logger.info(f"Tags '{', '.join(tags_to_add)}' added successfully")
|
102
104
|
else:
|
103
|
-
|
105
|
+
logger.warning(f"All tags '{tag_to_add}' already exist - skipping")
|
104
106
|
else:
|
105
|
-
|
107
|
+
logger.warning("No 'Tags:' section found in docstring - skipping")
|
106
108
|
else:
|
107
|
-
|
109
|
+
logger.warning("No docstring found - skipping")
|
108
110
|
return node
|
109
111
|
|
110
112
|
def _get_tag_suggestion_from_agent(self, node, http_method):
|
@@ -232,7 +234,9 @@ Your answer (comma-separated tags or 'none'):"""
|
|
232
234
|
except Exception as e:
|
233
235
|
nonlocal llm_failures
|
234
236
|
llm_failures += 1
|
235
|
-
|
237
|
+
from loguru import logger
|
238
|
+
|
239
|
+
logger.error(f"LLM failed for function {function_name}: {e}")
|
236
240
|
# If LLM fails, return None (no tag added)
|
237
241
|
return None
|
238
242
|
|
@@ -241,19 +245,18 @@ Your answer (comma-separated tags or 'none'):"""
|
|
241
245
|
new_source = ast.unparse(new_tree)
|
242
246
|
|
243
247
|
# Print summary statistics
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
248
|
+
from loguru import logger
|
249
|
+
|
250
|
+
logger.info("📊 PROCESSING SUMMARY")
|
251
|
+
logger.info(f"Total functions processed: {total_functions}")
|
252
|
+
logger.info(f"Functions with HTTP methods: {functions_with_http_methods}")
|
253
|
+
logger.info(f"Functions processed by LLM: {functions_processed_by_llm}")
|
254
|
+
logger.info(f"Functions successfully tagged: {functions_tagged}")
|
255
|
+
logger.info(f"LLM failures: {llm_failures}")
|
252
256
|
if functions_with_http_methods > 0:
|
253
|
-
|
257
|
+
logger.info(
|
254
258
|
f"LLM success rate: {(functions_processed_by_llm / functions_with_http_methods * 100):.1f}% of HTTP functions"
|
255
259
|
)
|
256
|
-
print(f"{'=' * 60}")
|
257
260
|
|
258
261
|
# Format with Black in memory
|
259
262
|
try:
|
@@ -262,14 +265,14 @@ Your answer (comma-separated tags or 'none'):"""
|
|
262
265
|
formatted_content = black.format_file_contents(new_source, fast=False, mode=black.FileMode())
|
263
266
|
with open(output_path, "w", encoding="utf-8") as f:
|
264
267
|
f.write(formatted_content)
|
265
|
-
|
268
|
+
logger.info(f"Black formatting applied successfully to: {output_path}")
|
266
269
|
except ImportError:
|
267
|
-
|
270
|
+
logger.warning(f"Black not installed. Skipping formatting for: {output_path}")
|
268
271
|
# Write unformatted version if Black is not available
|
269
272
|
with open(output_path, "w", encoding="utf-8") as f:
|
270
273
|
f.write(new_source)
|
271
274
|
except Exception as e:
|
272
|
-
|
275
|
+
logger.error(f"Black formatting failed for {output_path}: {e}")
|
273
276
|
# Write unformatted version if Black formatting fails
|
274
277
|
with open(output_path, "w", encoding="utf-8") as f:
|
275
278
|
f.write(new_source)
|
@@ -282,6 +282,8 @@ async def test_{app_name}_test_case_{i}({app_name}_test_case_{i}):
|
|
282
282
|
with open(output_file, "w") as f:
|
283
283
|
f.write(file_content)
|
284
284
|
|
285
|
-
|
285
|
+
from loguru import logger
|
286
|
+
|
287
|
+
logger.info(f"✅ Generated {output_file} with {len(multi_test_case.test_cases)} test cases for {app_name}")
|
286
288
|
for i, test_case in enumerate(multi_test_case.test_cases, 1):
|
287
|
-
|
289
|
+
logger.info(f" Test Case {i}: {len(test_case.tools)} tools, {len(test_case.tasks)} tasks")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: universal-mcp
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.24rc11
|
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
|
@@ -10,24 +10,24 @@ universal_mcp/agentr/README.md,sha256=t15pVgkCwZM5wzgLgrf0Zv6hVL7dPmKXvAeTf8CiXP
|
|
10
10
|
universal_mcp/agentr/__init__.py,sha256=fv1ZnOCduIUiJ9oN4e6Ya_hA2oWQvcEuDU3Ek1vEufI,180
|
11
11
|
universal_mcp/agentr/client.py,sha256=TQgwrNc7dEMXuprELf0Q-fdYdrH92Ppd7PUDZoD-KcY,7429
|
12
12
|
universal_mcp/agentr/integration.py,sha256=V5GjqocqS02tRoI8MeV9PL6m-BzejwBzgJhOHo4MxAE,4212
|
13
|
-
universal_mcp/agentr/registry.py,sha256=
|
13
|
+
universal_mcp/agentr/registry.py,sha256=GOCltEqVegBca_r-8ljxQqEJnNAlx3MF0DAw-cAUhbQ,6595
|
14
14
|
universal_mcp/agentr/server.py,sha256=bIPmHMiKKwnUYnxmfZVRh1thcn7Rytm_-bNiXTfANzc,2098
|
15
15
|
universal_mcp/agents/__init__.py,sha256=AMBDQs3p5PePjzdoHYNoPYEsUK_PLHGNVPGxK7yrKVo,270
|
16
|
-
universal_mcp/agents/auto.py,sha256=
|
17
|
-
universal_mcp/agents/base.py,sha256=
|
16
|
+
universal_mcp/agents/auto.py,sha256=zQHtWTL0L88j9zFN44xHbGhKzYZ_TEuzHMTVc0TzTRU,25433
|
17
|
+
universal_mcp/agents/base.py,sha256=Ta9fOw-ncP4S2j2lXeQzYNPBaWbThcEC_PvJwjzaHzE,5307
|
18
18
|
universal_mcp/agents/cli.py,sha256=7GdRBpu9rhZPiC2vaNQXWI7K-0yCnvdlmE0IFpvy2Gk,539
|
19
19
|
universal_mcp/agents/hil.py,sha256=6xi0hhK5g-rhCrAMcGbjcKMReLWPC8rnFZMBOF3N_cY,3687
|
20
|
-
universal_mcp/agents/llm.py,sha256=
|
21
|
-
universal_mcp/agents/react.py,sha256=
|
20
|
+
universal_mcp/agents/llm.py,sha256=GncfbrM7FbMQJ305jCq78PVN4Jh4hOBskLcdgBd69sY,1419
|
21
|
+
universal_mcp/agents/react.py,sha256=bjTq1SzNUSeCCDMrrfXsUBu_F_mGzow_jRx5KrQ-HVg,3032
|
22
22
|
universal_mcp/agents/simple.py,sha256=JL8TFyXlA1F4zcArgKhlqVIbLWXetwM05z4MPDJgFeI,1367
|
23
23
|
universal_mcp/agents/tools.py,sha256=7Vdw0VZYxXVAzAYSpRKWHzVl9Ll6NOnVRlc4cTXguUQ,1335
|
24
24
|
universal_mcp/agents/utils.py,sha256=7kwFpD0Rv6JqHG-LlNCVwSu_xRX-N119mUmiBroHJL4,4109
|
25
25
|
universal_mcp/agents/autoagent/__init__.py,sha256=E_vMnFz8Z120qdqaKXPNP_--4Tes4jImen7m_iZvtVo,913
|
26
|
-
universal_mcp/agents/autoagent/__main__.py,sha256=
|
26
|
+
universal_mcp/agents/autoagent/__main__.py,sha256=cnR5VuuTgcF4bE2XdpJGXVQtA7x_c5M1d-exAff4Mvw,651
|
27
27
|
universal_mcp/agents/autoagent/context.py,sha256=RgjW1uCslucxYJpdmi4govd-0V1_9e6Y_kjWl3FpLrE,847
|
28
|
-
universal_mcp/agents/autoagent/graph.py,sha256=
|
28
|
+
universal_mcp/agents/autoagent/graph.py,sha256=IWJLV3PL-0dmPvDckrtjJkgO6Zgt47kRk8Yprnn52dY,6768
|
29
29
|
universal_mcp/agents/autoagent/prompts.py,sha256=ptnXyOarigq96nVW-P1ceT2WRilKvh7NPfE_Cy0NTz4,719
|
30
|
-
universal_mcp/agents/autoagent/state.py,sha256=
|
30
|
+
universal_mcp/agents/autoagent/state.py,sha256=TQeGZD99okclkoCh5oz-VYIlEsC9yLQyDpnBnm7QCN8,759
|
31
31
|
universal_mcp/agents/autoagent/studio.py,sha256=nfVRzPXwBjDORHA0wln2k3Nz-zQXNKgZMvgeqBvkdtM,644
|
32
32
|
universal_mcp/agents/autoagent/utils.py,sha256=AFq-8scw_WlSZxDnTzxSNrOSiGYsIlqkqtQLDWf_rMU,431
|
33
33
|
universal_mcp/agents/codeact/__init__.py,sha256=5D_I3lI_3tWjZERRoFav_bPe9UDaJ53pDzZYtyixg3E,10097
|
@@ -39,7 +39,7 @@ universal_mcp/applications/application.py,sha256=pGF9Rb2D6qzlaSwlcfZ-dNqPtsLkQTq
|
|
39
39
|
universal_mcp/applications/sample/app.py,sha256=E0JwaWD7qytwawb_iWc1pBnJ-Te7MMtab4MxOOebLdc,8972
|
40
40
|
universal_mcp/client/oauth.py,sha256=O00zOUfQxINaruFU2zt-64DIR1_mAqrY8ykLQo-teJU,8679
|
41
41
|
universal_mcp/client/token_store.py,sha256=6VAzjzJG49wYvmEDqksFvb-fVqdjHIKWv7yYyh_AuF8,3912
|
42
|
-
universal_mcp/client/transport.py,sha256=
|
42
|
+
universal_mcp/client/transport.py,sha256=qqtb0ky6yvLBxsaA9-oFU0v9MwfcQb4eomq-O2fmwtQ,11850
|
43
43
|
universal_mcp/integrations/__init__.py,sha256=tfzLyPEPly5tfIcT8K6-oKCr_MEFGxOROHy_NeVy0KM,200
|
44
44
|
universal_mcp/integrations/integration.py,sha256=H-hOoDHqk78A4Fi_TGN7OOFS7PDfqXK_nedH8iSz-6A,16459
|
45
45
|
universal_mcp/servers/__init__.py,sha256=speBb_E94UJa4A6Fv8RHFeoJ7cR-q2bCMtKV7R21P5w,142
|
@@ -50,7 +50,7 @@ universal_mcp/tools/__init__.py,sha256=jC8hsqfTdtn32yU57AVFUXiU3ZmUOCfCERSCaNEIH
|
|
50
50
|
universal_mcp/tools/adapters.py,sha256=YJ2oqgc8JgmtsdRRtvO-PO0Q0bKqTJ4Y3J6yxlESoTo,3947
|
51
51
|
universal_mcp/tools/docstring_parser.py,sha256=efEOE-ME7G5Jbbzpn7pN2xNuyu2M5zfZ1Tqu1lRB0Gk,8392
|
52
52
|
universal_mcp/tools/func_metadata.py,sha256=F4jd--hoZWKPBbZihVtluYKUsIdXdq4a0VWRgMl5k-Q,10838
|
53
|
-
universal_mcp/tools/manager.py,sha256=
|
53
|
+
universal_mcp/tools/manager.py,sha256=U2-OQY4FGTDKS4IEOZTLVLcdqMC2vVghS0p_iLXX2Gc,10507
|
54
54
|
universal_mcp/tools/registry.py,sha256=etluwUwf2EfiGBoqQFZ1nf2xcPWtrJn0N4Qhcg7UGCU,2440
|
55
55
|
universal_mcp/tools/tools.py,sha256=Lk-wUO3rfhwdxaRANtC7lQr5fXi7nclf0oHzxNAb79Q,4927
|
56
56
|
universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
|
@@ -60,20 +60,20 @@ universal_mcp/utils/prompts.py,sha256=FJhqE0gPXDzYHS8gOjAVrdqVxc9X12ESnpd4C3jDSM
|
|
60
60
|
universal_mcp/utils/singleton.py,sha256=RoOiKxBOAhp0TK1QaMDYi-8GjRcy2Vh-bAOuIAcYan0,775
|
61
61
|
universal_mcp/utils/testing.py,sha256=J857Xt5K-hMxTc8UNJWlkzLbca1zObjwNhNXzYGxBHI,8009
|
62
62
|
universal_mcp/utils/openapi/__inti__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
|
-
universal_mcp/utils/openapi/api_generator.py,sha256=
|
63
|
+
universal_mcp/utils/openapi/api_generator.py,sha256=sygUE8Sropq3Pz09N1kSwGelgnTCAz_lOGspTaYCWEs,6985
|
64
64
|
universal_mcp/utils/openapi/api_splitter.py,sha256=io7fV-E8hUIR4NxFlakqydbgrQF6aBAnZHPMlpxw-wc,20967
|
65
65
|
universal_mcp/utils/openapi/cli.py,sha256=az5ObS74R-MmDCOZ1PHTJVKZJrHnsBOAweOUa7A-GqA,25918
|
66
66
|
universal_mcp/utils/openapi/docgen.py,sha256=DNmwlhg_-TRrHa74epyErMTRjV2nutfCQ7seb_Rq5hE,21366
|
67
67
|
universal_mcp/utils/openapi/filters.py,sha256=96FajO5nLbvjNPy2A1HvSS9jqpzMDHd4q_QTP-DIsPI,3842
|
68
|
-
universal_mcp/utils/openapi/openapi.py,sha256=
|
69
|
-
universal_mcp/utils/openapi/postprocessor.py,sha256=
|
68
|
+
universal_mcp/utils/openapi/openapi.py,sha256=mcmahLJrR-CS-r2RDaYOgMV1Eq7ZuCHMk7_Qrijadh4,62613
|
69
|
+
universal_mcp/utils/openapi/postprocessor.py,sha256=j_OZ5u2VCb44rfvnJZdKcxQRyGgrEVMr8ue9oN0Ed7A,12235
|
70
70
|
universal_mcp/utils/openapi/preprocessor.py,sha256=r4n0WQI__OzPL8FTza7jxiM4EYeZwa-3tvEJaJYZC44,63081
|
71
71
|
universal_mcp/utils/openapi/readme.py,sha256=R2Jp7DUXYNsXPDV6eFTkLiy7MXbSULUj1vHh4O_nB4c,2974
|
72
|
-
universal_mcp/utils/openapi/test_generator.py,sha256=
|
72
|
+
universal_mcp/utils/openapi/test_generator.py,sha256=vucBh9klWmQOUA740TFwfM9ry2nkwKWQiNRcsiZ9HbY,12229
|
73
73
|
universal_mcp/utils/templates/README.md.j2,sha256=Mrm181YX-o_-WEfKs01Bi2RJy43rBiq2j6fTtbWgbTA,401
|
74
74
|
universal_mcp/utils/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
|
75
|
-
universal_mcp-0.1.
|
76
|
-
universal_mcp-0.1.
|
77
|
-
universal_mcp-0.1.
|
78
|
-
universal_mcp-0.1.
|
79
|
-
universal_mcp-0.1.
|
75
|
+
universal_mcp-0.1.24rc11.dist-info/METADATA,sha256=wptNoLSs4TFv7_nDGfXDIHpQSuFcNCcAMx_dRNUWra0,3144
|
76
|
+
universal_mcp-0.1.24rc11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
77
|
+
universal_mcp-0.1.24rc11.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
|
78
|
+
universal_mcp-0.1.24rc11.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
|
79
|
+
universal_mcp-0.1.24rc11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|