nvidia-nat 1.3.0a20250828__py3-none-any.whl → 1.3.0a20250830__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.
Files changed (82) hide show
  1. nat/agent/base.py +6 -1
  2. nat/agent/react_agent/agent.py +46 -38
  3. nat/agent/react_agent/register.py +7 -2
  4. nat/agent/rewoo_agent/agent.py +16 -30
  5. nat/agent/rewoo_agent/register.py +3 -3
  6. nat/agent/tool_calling_agent/agent.py +9 -19
  7. nat/agent/tool_calling_agent/register.py +2 -2
  8. nat/builder/eval_builder.py +2 -2
  9. nat/builder/function.py +8 -8
  10. nat/builder/workflow.py +6 -2
  11. nat/builder/workflow_builder.py +21 -24
  12. nat/cli/cli_utils/config_override.py +1 -1
  13. nat/cli/commands/info/list_channels.py +1 -1
  14. nat/cli/commands/info/list_mcp.py +183 -47
  15. nat/cli/commands/registry/publish.py +2 -2
  16. nat/cli/commands/registry/pull.py +2 -2
  17. nat/cli/commands/registry/remove.py +2 -2
  18. nat/cli/commands/registry/search.py +1 -1
  19. nat/cli/commands/start.py +15 -3
  20. nat/cli/commands/uninstall.py +1 -1
  21. nat/cli/commands/workflow/workflow_commands.py +4 -4
  22. nat/data_models/discovery_metadata.py +4 -4
  23. nat/data_models/thinking_mixin.py +27 -8
  24. nat/eval/evaluate.py +6 -6
  25. nat/eval/intermediate_step_adapter.py +1 -1
  26. nat/eval/rag_evaluator/evaluate.py +2 -2
  27. nat/eval/rag_evaluator/register.py +1 -1
  28. nat/eval/remote_workflow.py +3 -3
  29. nat/eval/swe_bench_evaluator/evaluate.py +5 -5
  30. nat/eval/trajectory_evaluator/evaluate.py +1 -1
  31. nat/eval/tunable_rag_evaluator/evaluate.py +3 -3
  32. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +2 -2
  33. nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
  34. nat/front_ends/fastapi/fastapi_front_end_plugin.py +1 -1
  35. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +3 -3
  36. nat/front_ends/fastapi/message_handler.py +2 -2
  37. nat/front_ends/fastapi/message_validator.py +8 -10
  38. nat/front_ends/fastapi/response_helpers.py +4 -4
  39. nat/front_ends/fastapi/step_adaptor.py +1 -1
  40. nat/front_ends/mcp/mcp_front_end_config.py +5 -0
  41. nat/front_ends/mcp/mcp_front_end_plugin.py +8 -2
  42. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +2 -2
  43. nat/front_ends/mcp/tool_converter.py +40 -13
  44. nat/observability/exporter/base_exporter.py +1 -1
  45. nat/observability/exporter/processing_exporter.py +8 -9
  46. nat/observability/exporter_manager.py +5 -5
  47. nat/observability/mixin/file_mixin.py +7 -7
  48. nat/observability/processor/batching_processor.py +4 -6
  49. nat/observability/register.py +3 -1
  50. nat/profiler/calc/calc_runner.py +3 -4
  51. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  52. nat/profiler/callbacks/langchain_callback_handler.py +5 -5
  53. nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
  54. nat/profiler/callbacks/semantic_kernel_callback_handler.py +2 -2
  55. nat/profiler/profile_runner.py +1 -1
  56. nat/profiler/utils.py +1 -1
  57. nat/registry_handlers/local/local_handler.py +2 -2
  58. nat/registry_handlers/package_utils.py +1 -1
  59. nat/registry_handlers/pypi/pypi_handler.py +3 -3
  60. nat/registry_handlers/rest/rest_handler.py +4 -4
  61. nat/retriever/milvus/retriever.py +1 -1
  62. nat/retriever/nemo_retriever/retriever.py +1 -1
  63. nat/runtime/loader.py +1 -1
  64. nat/runtime/runner.py +2 -2
  65. nat/settings/global_settings.py +1 -1
  66. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +1 -1
  67. nat/tool/mcp/{mcp_client.py → mcp_client_base.py} +197 -46
  68. nat/tool/mcp/mcp_client_impl.py +229 -0
  69. nat/tool/mcp/mcp_tool.py +79 -42
  70. nat/tool/nvidia_rag.py +1 -1
  71. nat/tool/register.py +1 -0
  72. nat/tool/retriever.py +3 -2
  73. nat/utils/io/yaml_tools.py +1 -1
  74. nat/utils/reactive/observer.py +2 -2
  75. nat/utils/settings/global_settings.py +2 -2
  76. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/METADATA +3 -3
  77. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/RECORD +82 -81
  78. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/WHEEL +0 -0
  79. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/entry_points.txt +0 -0
  80. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  81. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/licenses/LICENSE.md +0 -0
  82. {nvidia_nat-1.3.0a20250828.dist-info → nvidia_nat-1.3.0a20250830.dist-info}/top_level.txt +0 -0
nat/tool/mcp/mcp_tool.py CHANGED
@@ -14,10 +14,12 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import logging
17
+ from typing import Literal
17
18
 
18
19
  from pydantic import BaseModel
19
20
  from pydantic import Field
20
21
  from pydantic import HttpUrl
22
+ from pydantic import model_validator
21
23
 
22
24
  from nat.builder.builder import Builder
23
25
  from nat.builder.function_info import FunctionInfo
@@ -33,8 +35,16 @@ class MCPToolConfig(FunctionBaseConfig, name="mcp_tool_wrapper"):
33
35
  function.
34
36
  """
35
37
  # Add your custom configuration parameters here
36
- url: HttpUrl = Field(description="The URL of the MCP server")
38
+ url: HttpUrl | None = Field(default=None,
39
+ description="The URL of the MCP server (for streamable-http or sse modes)")
37
40
  mcp_tool_name: str = Field(description="The name of the tool served by the MCP Server that you want to use")
41
+ transport: Literal["sse", "stdio", "streamable-http"] = Field(
42
+ default="streamable-http",
43
+ description="The type of transport to use (default: streamable-http, backwards compatible with sse)")
44
+ command: str | None = Field(default=None,
45
+ description="The command to run for stdio mode (e.g. 'docker' or 'python')")
46
+ args: list[str] | None = Field(default=None, description="Additional arguments for the stdio command")
47
+ env: dict[str, str] | None = Field(default=None, description="Environment variables to set for the stdio process")
38
48
  description: str | None = Field(default=None,
39
49
  description="""
40
50
  Description for the tool that will override the description provided by the MCP server. Should only be used if
@@ -46,51 +56,78 @@ class MCPToolConfig(FunctionBaseConfig, name="mcp_tool_wrapper"):
46
56
  If false, raise the exception.
47
57
  """)
48
58
 
59
+ @model_validator(mode="after")
60
+ def validate_model(self):
61
+ """Validate that stdio and SSE/Streamable HTTP properties are mutually exclusive."""
62
+ if self.transport == 'stdio':
63
+ if self.url is not None:
64
+ raise ValueError("url should not be set when using stdio client type")
65
+ if not self.command:
66
+ raise ValueError("command is required when using stdio client type")
67
+ elif self.transport in ['streamable-http', 'sse']:
68
+ if self.command is not None or self.args is not None or self.env is not None:
69
+ raise ValueError(
70
+ "command, args, and env should not be set when using streamable-http or sse client type")
71
+ if not self.url:
72
+ raise ValueError("url is required when using streamable-http or sse client type")
73
+ return self
74
+
49
75
 
50
76
  @register_function(config_type=MCPToolConfig)
51
77
  async def mcp_tool(config: MCPToolConfig, builder: Builder):
52
78
  """
53
- Generate a NAT Function that wraps a tool provided by the MCP server.
79
+ Generate a NeMo Agent Toolkit Function that wraps a tool provided by the MCP server.
54
80
  """
55
81
 
56
- from nat.tool.mcp.mcp_client import MCPBuilder
57
- from nat.tool.mcp.mcp_client import MCPToolClient
58
-
59
- client = MCPBuilder(url=str(config.url))
60
-
61
- tool: MCPToolClient = await client.get_tool(config.mcp_tool_name)
62
- if config.description:
63
- tool.set_description(description=config.description)
64
-
65
- logger.info("Configured to use tool: %s from MCP server at %s", tool.name, str(config.url))
66
-
67
- def _convert_from_str(input_str: str) -> tool.input_schema:
68
- return tool.input_schema.model_validate_json(input_str)
69
-
70
- async def _response_fn(tool_input: BaseModel | None = None, **kwargs) -> str:
71
- # Run the tool, catching any errors and sending to agent for correction
72
- try:
73
- if tool_input:
74
- args = tool_input.model_dump()
75
- return await tool.acall(args)
76
-
77
- _ = tool.input_schema.model_validate(kwargs)
78
- filtered_kwargs = {k: v for k, v in kwargs.items() if v is not None}
79
- return await tool.acall(filtered_kwargs)
80
- except Exception as e:
81
- if config.return_exception:
82
+ from nat.tool.mcp.mcp_client_base import MCPSSEClient
83
+ from nat.tool.mcp.mcp_client_base import MCPStdioClient
84
+ from nat.tool.mcp.mcp_client_base import MCPStreamableHTTPClient
85
+ from nat.tool.mcp.mcp_client_base import MCPToolClient
86
+
87
+ # Initialize the client
88
+ if config.transport == 'stdio':
89
+ client = MCPStdioClient(command=config.command, args=config.args, env=config.env)
90
+ elif config.transport == 'streamable-http':
91
+ client = MCPStreamableHTTPClient(url=str(config.url))
92
+ elif config.transport == 'sse':
93
+ client = MCPSSEClient(url=str(config.url))
94
+ else:
95
+ raise ValueError(f"Invalid transport type: {config.transport}")
96
+
97
+ async with client:
98
+ # If the tool is found create a MCPToolClient object and set the description if provided
99
+ tool: MCPToolClient = await client.get_tool(config.mcp_tool_name)
100
+ if config.description:
101
+ tool.set_description(description=config.description)
102
+
103
+ logger.info("Configured to use tool: %s from MCP server at %s", tool.name, client.server_name)
104
+
105
+ def _convert_from_str(input_str: str) -> tool.input_schema:
106
+ return tool.input_schema.model_validate_json(input_str)
107
+
108
+ async def _response_fn(tool_input: BaseModel | None = None, **kwargs) -> str:
109
+ # Run the tool, catching any errors and sending to agent for correction
110
+ try:
82
111
  if tool_input:
83
- logger.warning("Error calling tool %s with serialized input: %s",
84
- tool.name,
85
- tool_input.model_dump(),
86
- exc_info=True)
87
- else:
88
- logger.warning("Error calling tool %s with input: %s", tool.name, kwargs, exc_info=True)
89
- return str(e)
90
- # If the tool call fails, raise the exception.
91
- raise
92
-
93
- yield FunctionInfo.create(single_fn=_response_fn,
94
- description=tool.description,
95
- input_schema=tool.input_schema,
96
- converters=[_convert_from_str])
112
+ args = tool_input.model_dump()
113
+ return await tool.acall(args)
114
+
115
+ _ = tool.input_schema.model_validate(kwargs)
116
+ return await tool.acall(kwargs)
117
+ except Exception as e:
118
+ if config.return_exception:
119
+ if tool_input:
120
+ logger.warning("Error calling tool %s with serialized input: %s",
121
+ tool.name,
122
+ tool_input.model_dump(),
123
+ exc_info=True)
124
+ else:
125
+ logger.warning("Error calling tool %s with input: %s", tool.name, kwargs, exc_info=True)
126
+ return str(e)
127
+ # If the tool call fails, raise the exception.
128
+ raise
129
+
130
+ yield FunctionInfo.create(single_fn=_response_fn,
131
+ description=tool.description,
132
+ input_schema=tool.input_schema,
133
+ converters=[_convert_from_str])
nat/tool/nvidia_rag.py CHANGED
@@ -86,7 +86,7 @@ async def nvidia_rag_tool(config: NVIDIARAGToolConfig, builder: Builder):
86
86
  [await aformat_document(doc, document_prompt) for doc in docs])
87
87
  return parsed_output
88
88
  except Exception as e:
89
- logger.exception("Error while running the tool", exc_info=True)
89
+ logger.exception("Error while running the tool")
90
90
  return f"Error while running the tool: {e}"
91
91
 
92
92
  yield FunctionInfo.from_fn(
nat/tool/register.py CHANGED
@@ -31,6 +31,7 @@ from .github_tools import get_github_file
31
31
  from .github_tools import get_github_issue
32
32
  from .github_tools import get_github_pr
33
33
  from .github_tools import update_github_issue
34
+ from .mcp import mcp_client_impl
34
35
  from .mcp import mcp_tool
35
36
  from .memory_tools import add_memory_tool
36
37
  from .memory_tools import delete_memory_tool
nat/tool/retriever.py CHANGED
@@ -78,8 +78,9 @@ async def retriever_tool(config: RetrieverConfig, builder: Builder):
78
78
 
79
79
  except RetrieverError as e:
80
80
  if config.raise_errors:
81
- raise e
82
- logger.warning("Retriever threw an error: %s. Returning an empty response.", e)
81
+ logger.error("Retriever threw an error: %s.", e)
82
+ raise
83
+ logger.exception("Retriever threw an error: %s. Returning an empty response.", e)
83
84
  return RetrieverOutput(results=[])
84
85
 
85
86
  yield FunctionInfo.from_fn(
@@ -85,7 +85,7 @@ def yaml_loads(config: str) -> dict:
85
85
  try:
86
86
  config_data = yaml.safe_load(stream)
87
87
  except yaml.YAMLError as e:
88
- logger.error("Error loading YAML: %s", interpolated_config_str, exc_info=True)
88
+ logger.error("Error loading YAML: %s", interpolated_config_str)
89
89
  raise ValueError(f"Error loading YAML: {e}") from e
90
90
 
91
91
  assert isinstance(config_data, dict)
@@ -64,7 +64,7 @@ class Observer(ObserverBase[_T_in_contra]):
64
64
  try:
65
65
  self._on_error(exc)
66
66
  except Exception as e:
67
- logger.exception("Error in on_error callback: %s", e, exc_info=True)
67
+ logger.exception("Error in on_error callback: %s", e)
68
68
 
69
69
  def on_complete(self) -> None:
70
70
  if not self._stopped:
@@ -73,4 +73,4 @@ class Observer(ObserverBase[_T_in_contra]):
73
73
  try:
74
74
  self._on_complete()
75
75
  except Exception as e:
76
- logger.exception("Error in on_complete callback: %s", e, exc_info=True)
76
+ logger.exception("Error in on_complete callback: %s", e)
@@ -55,7 +55,7 @@ def configure_registry_channel(config_type: RegistryHandlerBaseConfig, channel_n
55
55
  channel_registry_pre[field] = getattr(validated_field_model, field)
56
56
  break
57
57
  except Exception as e:
58
- logger.exception(e, exc_info=True)
58
+ logger.exception(e)
59
59
  logger.warning("Invalid '%s' input, input must be of type %s.", field, info.annotation)
60
60
 
61
61
  validated_model = config_type(**channel_registry_pre)
@@ -78,7 +78,7 @@ def add_channel_interative(channel_type: str) -> None:
78
78
  try:
79
79
  ChannelConfigType = registry.get_registered_channel_info_by_channel_type(channel_type=channel_type).config_type
80
80
  except Exception as e:
81
- logger.exception("Invalid channel type: %s", e, exc_info=True)
81
+ logger.exception("Invalid channel type: %s", e)
82
82
  return
83
83
 
84
84
  while (True):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nvidia-nat
3
- Version: 1.3.0a20250828
3
+ Version: 1.3.0a20250830
4
4
  Summary: NVIDIA NeMo Agent toolkit
5
5
  Author: NVIDIA Corporation
6
6
  Maintainer: NVIDIA Corporation
@@ -221,7 +221,7 @@ Requires-Dist: fastapi~=0.115.5
221
221
  Requires-Dist: httpx~=0.27
222
222
  Requires-Dist: jinja2~=3.1
223
223
  Requires-Dist: jsonpath-ng~=1.7
224
- Requires-Dist: mcp~=1.10
224
+ Requires-Dist: mcp~=1.13
225
225
  Requires-Dist: networkx~=3.4
226
226
  Requires-Dist: numpy~=1.26
227
227
  Requires-Dist: openinference-semantic-conventions~=0.1.14
@@ -229,7 +229,7 @@ Requires-Dist: openpyxl~=3.1
229
229
  Requires-Dist: pkce==1.0.3
230
230
  Requires-Dist: pkginfo~=1.12
231
231
  Requires-Dist: platformdirs~=4.3
232
- Requires-Dist: pydantic==2.10.*
232
+ Requires-Dist: pydantic~=2.11
233
233
  Requires-Dist: pymilvus~=2.4
234
234
  Requires-Dist: PyYAML~=6.0
235
235
  Requires-Dist: ragas~=0.2.14