universal-mcp 0.1.18rc4__py3-none-any.whl → 0.1.20__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/cli.py CHANGED
@@ -59,8 +59,8 @@ def generate(
59
59
  else:
60
60
  # Handle the error case from api_generator if validation fails
61
61
  if isinstance(app_file_data, dict) and "error" in app_file_data:
62
- console.print(f"[red]{app_file_data['error']}[/red]")
63
- raise typer.Exit(1)
62
+ console.print(f"[red]{app_file_data['error']}[/red]")
63
+ raise typer.Exit(1)
64
64
  else:
65
65
  console.print("[red]Unexpected return value from API generator.[/red]")
66
66
  raise typer.Exit(1)
@@ -119,9 +119,8 @@ def run(
119
119
  from universal_mcp.logger import setup_logger
120
120
  from universal_mcp.servers import server_from_config
121
121
 
122
- setup_logger()
123
-
124
122
  config = ServerConfig.model_validate_json(config_path.read_text()) if config_path else ServerConfig()
123
+ setup_logger(level=config.log_level)
125
124
  server = server_from_config(config)
126
125
  server.run(transport=config.transport)
127
126
 
@@ -291,7 +290,7 @@ def split_api(
291
290
  console.print(f"[green]Successfully split {input_app_file} into {output_dir}[/green]")
292
291
  except Exception as e:
293
292
  console.print(f"[red]Error splitting API client: {e}[/red]")
294
-
293
+
295
294
  raise typer.Exit(1) from e
296
295
 
297
296
 
universal_mcp/config.py CHANGED
@@ -46,7 +46,7 @@ class ServerConfig(BaseSettings):
46
46
 
47
47
  name: str = Field(default="Universal MCP", description="Name of the MCP server")
48
48
  description: str = Field(default="Universal MCP", description="Description of the MCP server")
49
- api_key: SecretStr | None = Field(default=None, description="API key for authentication")
49
+ api_key: SecretStr | None = Field(default=None, description="API key for authentication", alias="AGENTR_API_KEY")
50
50
  type: Literal["local", "agentr"] = Field(default="agentr", description="Type of server deployment")
51
51
  transport: Literal["stdio", "sse", "streamable-http"] = Field(
52
52
  default="stdio", description="Transport protocol to use"
@@ -217,8 +217,9 @@ class AgentRServer(BaseServer):
217
217
  **kwargs: Additional keyword arguments passed to FastMCP
218
218
  """
219
219
 
220
- def __init__(self, config: ServerConfig, api_key: str | None = None, **kwargs):
221
- self.api_key = api_key or str(config.api_key)
220
+ def __init__(self, config: ServerConfig, **kwargs):
221
+ self.api_key = config.api_key.get_secret_value() if config.api_key else None
222
+ logger.info(f"Initializing AgentR server with API key: {self.api_key}")
222
223
  self.client = AgentrClient(api_key=self.api_key)
223
224
  super().__init__(config, **kwargs)
224
225
  self.integration = AgentRIntegration(name="agentr", api_key=self.client.api_key)
@@ -328,4 +329,4 @@ class SingleMCPServer(BaseServer):
328
329
  description=f"Minimal MCP server for the local {app_instance.name} application.",
329
330
  )
330
331
  super().__init__(config, **kwargs)
331
- self._tool_manager.register_tools_from_app(app_instance)
332
+ self._tool_manager.register_tools_from_app(app_instance, tags="all")
@@ -17,7 +17,7 @@ def convert_tool_to_mcp_tool(
17
17
  from mcp.server.fastmcp.server import MCPTool
18
18
 
19
19
  return MCPTool(
20
- name=tool.name,
20
+ name=tool.name[:63],
21
21
  description=tool.description or "",
22
22
  inputSchema=tool.parameters,
23
23
  )
@@ -33,6 +33,10 @@ def _filter_by_name(tools: list[Tool], tool_names: list[str] | None) -> list[Too
33
33
 
34
34
 
35
35
  def _filter_by_tags(tools: list[Tool], tags: list[str] | None) -> list[Tool]:
36
+ logger.debug(f"Filtering tools by tags: {tags}")
37
+ if "all" in tags:
38
+ return tools
39
+
36
40
  if not tags:
37
41
  return tools
38
42
  return [tool for tool in tools if any(tag in tool.tags for tag in tags)]
@@ -197,11 +201,9 @@ class ToolManager:
197
201
 
198
202
  if tool_names:
199
203
  tools = _filter_by_name(tools, tool_names)
200
-
201
204
  # If no tool names or tags are provided, use the default important tag
202
205
  if not tool_names and not tags:
203
206
  tools = _filter_by_tags(tools, [DEFAULT_IMPORTANT_TAG])
204
-
205
207
  self.register_tools(tools)
206
208
  return
207
209
 
@@ -55,7 +55,7 @@ class Tool(BaseModel):
55
55
 
56
56
  return cls(
57
57
  fn=fn,
58
- name=func_name,
58
+ name=func_name[:48],
59
59
  description=parsed_doc["summary"],
60
60
  args_description=parsed_doc["args"],
61
61
  returns_description=parsed_doc["returns"],
@@ -18,9 +18,12 @@ class AgentrClient:
18
18
  base_url (str, optional): Base URL for AgentR API. Defaults to https://api.agentr.dev
19
19
  """
20
20
 
21
- def __init__(self, api_key: str = None, base_url: str = None):
22
- self.api_key = api_key or os.getenv("AGENTR_API_KEY")
23
- if not self.api_key:
21
+ def __init__(self, api_key: str | None = None, base_url: str | None = None):
22
+ if api_key:
23
+ self.api_key = api_key
24
+ elif os.getenv("AGENTR_API_KEY"):
25
+ self.api_key = os.getenv("AGENTR_API_KEY")
26
+ else:
24
27
  logger.error(
25
28
  "API key for AgentR is missing. Please visit https://agentr.dev to create an API key, then set it as AGENTR_API_KEY environment variable."
26
29
  )
@@ -643,43 +643,28 @@ def _generate_method_code(path, method, operation):
643
643
  param_details[param_obj.name] = param_obj
644
644
 
645
645
  # Fetch request body example
646
- request_body_example_str = None
646
+ example_data = None # Initialize example_data here for wider scope
647
+
647
648
  if has_body:
648
649
  try:
649
650
  json_content = operation["requestBody"]["content"]["application/json"]
650
- example_data = None
651
+ #From direct content definition
651
652
  if "example" in json_content:
652
653
  example_data = json_content["example"]
653
654
  elif "examples" in json_content and json_content["examples"]:
654
655
  first_example_key = list(json_content["examples"].keys())[0]
655
656
  example_data = json_content["examples"][first_example_key].get("value")
656
-
657
- if example_data is not None:
658
- try:
659
- example_json = json.dumps(example_data, indent=2)
660
- indented_example = textwrap.indent(example_json, " " * 8) # 8 spaces
661
- request_body_example_str = f"\n Example:\n ```json\n{indented_example}\n ```"
662
- except TypeError:
663
- request_body_example_str = f"\n Example: {example_data}"
657
+ #If not found directly, try from resolved body schema (for nested/referenced examples)
658
+ if example_data is None and body_schema_to_use and "example" in body_schema_to_use:
659
+ example_data = body_schema_to_use["example"]
664
660
  except KeyError:
665
- pass # No example found
666
-
667
- # Identify the last argument related to the request body
668
- last_body_arg_name = None
669
- # request_body_params contains the names as they appear in the signature
670
- if final_request_body_arg_names_for_signature: # Use the new list with final aliased names
671
- # Find which of these appears last in the combined args list
672
- body_args_in_signature = [
673
- a.split("=")[0] for a in args if a.split("=")[0] in final_request_body_arg_names_for_signature
674
- ]
675
- if body_args_in_signature:
676
- last_body_arg_name = body_args_in_signature[-1]
661
+ pass # No example found or application/json content not present
677
662
 
678
663
  if signature_arg_names:
679
664
  args_doc_lines.append("Args:")
680
665
  for arg_signature_str in args:
681
666
  arg_name = arg_signature_str.split("=")[0]
682
- example_str = None # Initialize example_str here
667
+ example_str = None # Initialize example_str here for each argument
683
668
  detail = param_details.get(arg_name)
684
669
  if detail:
685
670
  desc = detail.description or "No description provided."
@@ -692,26 +677,22 @@ def _generate_method_code(path, method, operation):
692
677
  if detail.example and not detail.is_file: # Don't show schema example for file inputs
693
678
  example_str = repr(detail.example)
694
679
  arg_line += f" Example: {example_str}."
695
-
696
- # Append the full body example after the last body-related argument
697
- if arg_name == last_body_arg_name and request_body_example_str:
698
- # Remove the simple Example: if it exists before adding the detailed one
699
- if example_str and (
700
- f" Example: {example_str}." in arg_line or f" Example: {example_str} ." in arg_line
701
- ):
702
- arg_line = arg_line.replace(
703
- f" Example: {example_str}.", ""
704
- ) # Remove with or without trailing period
705
- arg_line += request_body_example_str # Append the formatted JSON example
680
+ # Fallback for body parameters if no direct example was found
681
+ elif not example_str and detail.where == "body" and example_data and isinstance(example_data, dict) and detail.identifier in example_data:
682
+ current_body_param_example = example_data[detail.identifier]
683
+ if current_body_param_example is not None: # Ensure the extracted part is not None
684
+ try:
685
+ arg_line += f" Example: {repr(current_body_param_example)}."
686
+ except Exception: # Fallback if repr fails
687
+ arg_line += " Example: [Could not represent example]."
706
688
 
707
689
  args_doc_lines.append(arg_line)
708
- elif arg_name == final_empty_body_param_name and has_empty_body: # Use potentially suffixed name
690
+
691
+ elif arg_name == final_empty_body_param_name and has_empty_body:
709
692
  args_doc_lines.append(
710
693
  f" {arg_name} (dict | None): Optional dictionary for an empty JSON request body (e.g., {{}})."
711
694
  )
712
- if ( arg_name == last_body_arg_name and request_body_example_str ):
713
- args_doc_lines[-1] += request_body_example_str
714
- elif arg_name == raw_body_param_name: # Docstring for raw body parameter
695
+ elif arg_name == raw_body_param_name:
715
696
  raw_body_type_hint = "bytes"
716
697
  raw_body_desc = "Raw binary content for the request body."
717
698
  if selected_content_type and "text" in selected_content_type:
@@ -720,13 +701,9 @@ def _generate_method_code(path, method, operation):
720
701
  elif selected_content_type and selected_content_type.startswith("image/"):
721
702
  raw_body_type_hint = "bytes (image data)"
722
703
  raw_body_desc = f"Raw image content ({selected_content_type}) for the request body."
723
-
724
704
  args_doc_lines.append(
725
705
  f" {arg_name} ({raw_body_type_hint} | None): {raw_body_desc}"
726
706
  )
727
- # Example for raw body is harder to give generically, but if present in spec, could be added.
728
- if ( arg_name == last_body_arg_name and request_body_example_str ):
729
- args_doc_lines[-1] += request_body_example_str
730
707
 
731
708
  if args_doc_lines:
732
709
  docstring_parts.append("\n".join(args_doc_lines))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp
3
- Version: 0.1.18rc4
3
+ Version: 0.1.20
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
@@ -14,7 +14,7 @@ Requires-Dist: keyring>=25.6.0
14
14
  Requires-Dist: langchain-mcp-adapters>=0.0.3
15
15
  Requires-Dist: litellm>=1.30.7
16
16
  Requires-Dist: loguru>=0.7.3
17
- Requires-Dist: mcp>=1.8.1
17
+ Requires-Dist: mcp>=1.9.0
18
18
  Requires-Dist: posthog>=3.24.0
19
19
  Requires-Dist: pydantic-settings>=2.8.1
20
20
  Requires-Dist: pydantic>=2.11.1
@@ -1,7 +1,7 @@
1
1
  universal_mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  universal_mcp/analytics.py,sha256=Dkv8mkc_2T2t5NxLSZzcr3BlmOispj1RKtbB86V1i4M,2306
3
- universal_mcp/cli.py,sha256=Xcdm4UMIlwxK6f0FHvmu5b6lZ8q14S3vZexRM32zAK4,10298
4
- universal_mcp/config.py,sha256=mjvrVK7eGmMq8CKBgZOMD1wVOAVzd7gzTUxW5sgIfWE,3297
3
+ universal_mcp/cli.py,sha256=-MVcqDEL_0AefmEgjq3ZCa2tySxXwi-Tfoat2kboX_U,10311
4
+ universal_mcp/config.py,sha256=HaAZvf-XzQZpqGGWUuT5zojWloO8GL5Acfa5_0sDs_Q,3321
5
5
  universal_mcp/exceptions.py,sha256=-pbeZhpNieJfnSd2-WM80pU8W8mK8VHXcSjky0BHwdk,665
6
6
  universal_mcp/logger.py,sha256=VmH_83efpErLEDTJqz55Dp0dioTXfGvMBLZUx5smOLc,2116
7
7
  universal_mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,18 +13,18 @@ universal_mcp/integrations/__init__.py,sha256=X8iEzs02IlXfeafp6GMm-cOkg70QdjnlTR
13
13
  universal_mcp/integrations/integration.py,sha256=QvZlq3G5OU4tHPv9uq9Nv5NFe30NdUsJU-Av474n0_o,13154
14
14
  universal_mcp/servers/README.md,sha256=ytFlgp8-LO0oogMrHkMOp8SvFTwgsKgv7XhBVZGNTbM,2284
15
15
  universal_mcp/servers/__init__.py,sha256=eBZCsaZjiEv6ZlRRslPKgurQxmpHLQyiXv2fTBygHnM,532
16
- universal_mcp/servers/server.py,sha256=ZyI--B2Lho5BFhfcAzbOsnlFN-3spRbb-FrhhCc2G7M,12727
16
+ universal_mcp/servers/server.py,sha256=6xRUxTJHiX0AqEWbAZ6eb6jEkvuBX5Qpkb53hmKcfc0,12822
17
17
  universal_mcp/stores/README.md,sha256=jrPh_ow4ESH4BDGaSafilhOVaN8oQ9IFlFW-j5Z5hLA,2465
18
18
  universal_mcp/stores/__init__.py,sha256=quvuwhZnpiSLuojf0NfmBx2xpaCulv3fbKtKaSCEmuM,603
19
19
  universal_mcp/stores/store.py,sha256=mxnmOVlDNrr8OKhENWDtCIfK7YeCBQcGdS6I2ogRCsU,6756
20
20
  universal_mcp/tools/README.md,sha256=RuxliOFqV1ZEyeBdj3m8UKfkxAsfrxXh-b6V4ZGAk8I,2468
21
21
  universal_mcp/tools/__init__.py,sha256=Fatza_R0qYWmNF1WQSfUZZKQFu5qf-16JhZzdmyx3KY,333
22
- universal_mcp/tools/adapters.py,sha256=gz_sNDc_bseMHWmpQmqhOq65veE-DuK_kJYXGIx0Wi8,1427
22
+ universal_mcp/tools/adapters.py,sha256=OCZuWxLscys6mw1Q5ctQFshv9q3szlikwHhn4j5PMnE,1432
23
23
  universal_mcp/tools/func_metadata.py,sha256=zIDXgIBvu5Gh8aNlg-Q7cZZos9Iky75MS0Me0BraXeM,8086
24
- universal_mcp/tools/manager.py,sha256=eNYEGCeTvtSsyPkPWo4ciuJEqNw4ux-XCr0r5OyBejg,7981
25
- universal_mcp/tools/tools.py,sha256=8YBTaJCM38Nhan9Al6Vlq4FtSULrKlxg1q_o8OL1_FM,3322
24
+ universal_mcp/tools/manager.py,sha256=iwywaTjVGvBCJJasfwDWrASUleYqxenm4S-0txdhCF0,8076
25
+ universal_mcp/tools/tools.py,sha256=qiuuLe0mCWtxXp6E5ISDDaNojCrMLfV1r5L8peFoJfg,3327
26
26
  universal_mcp/utils/__init__.py,sha256=8wi4PGWu-SrFjNJ8U7fr2iFJ1ktqlDmSKj1xYd7KSDc,41
27
- universal_mcp/utils/agentr.py,sha256=xXCrWh_GL70eYXjuYz-1YkPzKLDqwg0S98TBS3Osqg8,3271
27
+ universal_mcp/utils/agentr.py,sha256=YmX6hvFW84keJKTZqP3jPz4rP9DM5wHZ0vTMHfxO6AI,3360
28
28
  universal_mcp/utils/common.py,sha256=HEZC2Mhilb8DrGXQG2tboAIw1r4veGilGWjfnPF1lyA,888
29
29
  universal_mcp/utils/docstring_parser.py,sha256=6oIeCjOUirFdVpXdGZt5zDKje6jmCY42-GeYOc_7r2I,11317
30
30
  universal_mcp/utils/installation.py,sha256=ItOfBFhKOh4DLz237jgAz_Fn0uOMdrKXw0n5BaUZZNs,7286
@@ -33,13 +33,13 @@ universal_mcp/utils/openapi/__inti__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
33
33
  universal_mcp/utils/openapi/api_generator.py,sha256=FjtvbnWuI1P8W8wXuKLCirUtsqQ4HI_TuQrhpA4SqTs,4749
34
34
  universal_mcp/utils/openapi/api_splitter.py,sha256=6O2y7fcCo2k3ixLr6_9-aAZx2kas3UAxQhqJy1esNkE,18829
35
35
  universal_mcp/utils/openapi/docgen.py,sha256=DNmwlhg_-TRrHa74epyErMTRjV2nutfCQ7seb_Rq5hE,21366
36
- universal_mcp/utils/openapi/openapi.py,sha256=8XCIkJuwTN0UUcrBslQxitvz4y0NItBtuIgxdvb-Gdg,46857
36
+ universal_mcp/utils/openapi/openapi.py,sha256=EQME-VC5ccXpukIF0h96YsQOZj6oWip8W7a0bRN3s_k,45677
37
37
  universal_mcp/utils/openapi/preprocessor.py,sha256=qLYv4ekors5B2OU_YUvXICYQ7XYhAOEPyAnKtnBvNpM,46699
38
38
  universal_mcp/utils/openapi/readme.py,sha256=R2Jp7DUXYNsXPDV6eFTkLiy7MXbSULUj1vHh4O_nB4c,2974
39
39
  universal_mcp/utils/templates/README.md.j2,sha256=Mrm181YX-o_-WEfKs01Bi2RJy43rBiq2j6fTtbWgbTA,401
40
40
  universal_mcp/utils/templates/api_client.py.j2,sha256=972Im7LNUAq3yZTfwDcgivnb-b8u6_JLKWXwoIwXXXQ,908
41
- universal_mcp-0.1.18rc4.dist-info/METADATA,sha256=MPY4E-Q_CWOtHzhCb1lCeHwZo_840SMgxh1Xuz1s_sc,12125
42
- universal_mcp-0.1.18rc4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
- universal_mcp-0.1.18rc4.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
44
- universal_mcp-0.1.18rc4.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
45
- universal_mcp-0.1.18rc4.dist-info/RECORD,,
41
+ universal_mcp-0.1.20.dist-info/METADATA,sha256=Ue8JfKp1O-fFRZ6WjcH30hcGhbS4_V61s3HEWk9Nx3o,12122
42
+ universal_mcp-0.1.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
43
+ universal_mcp-0.1.20.dist-info/entry_points.txt,sha256=QlBrVKmA2jIM0q-C-3TQMNJTTWOsOFQvgedBq2rZTS8,56
44
+ universal_mcp-0.1.20.dist-info/licenses/LICENSE,sha256=NweDZVPslBAZFzlgByF158b85GR0f5_tLQgq1NS48To,1063
45
+ universal_mcp-0.1.20.dist-info/RECORD,,