nvidia-nat-mcp 1.3.0a20250917__py3-none-any.whl → 1.3.0a20250923__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.
- nat/plugins/mcp/auth/__init__.py +14 -0
- nat/plugins/mcp/auth/auth_provider.py +367 -0
- nat/plugins/mcp/auth/auth_provider_config.py +76 -0
- nat/plugins/mcp/auth/register.py +25 -0
- nat/plugins/mcp/client_base.py +118 -88
- nat/plugins/mcp/client_impl.py +90 -137
- nat/plugins/mcp/tool.py +41 -35
- nat/plugins/mcp/utils.py +95 -0
- {nvidia_nat_mcp-1.3.0a20250917.dist-info → nvidia_nat_mcp-1.3.0a20250923.dist-info}/METADATA +3 -3
- nvidia_nat_mcp-1.3.0a20250923.dist-info/RECORD +18 -0
- {nvidia_nat_mcp-1.3.0a20250917.dist-info → nvidia_nat_mcp-1.3.0a20250923.dist-info}/entry_points.txt +1 -0
- nvidia_nat_mcp-1.3.0a20250917.dist-info/RECORD +0 -13
- {nvidia_nat_mcp-1.3.0a20250917.dist-info → nvidia_nat_mcp-1.3.0a20250923.dist-info}/WHEEL +0 -0
- {nvidia_nat_mcp-1.3.0a20250917.dist-info → nvidia_nat_mcp-1.3.0a20250923.dist-info}/top_level.txt +0 -0
nat/plugins/mcp/tool.py
CHANGED
@@ -25,6 +25,7 @@ from nat.builder.builder import Builder
|
|
25
25
|
from nat.builder.function_info import FunctionInfo
|
26
26
|
from nat.cli.register_workflow import register_function
|
27
27
|
from nat.data_models.function import FunctionBaseConfig
|
28
|
+
from nat.plugins.mcp.client_base import MCPToolClient
|
28
29
|
|
29
30
|
logger = logging.getLogger(__name__)
|
30
31
|
|
@@ -50,11 +51,6 @@ class MCPToolConfig(FunctionBaseConfig, name="mcp_tool_wrapper"):
|
|
50
51
|
Description for the tool that will override the description provided by the MCP server. Should only be used if
|
51
52
|
the description provided by the server is poor or nonexistent
|
52
53
|
""")
|
53
|
-
return_exception: bool = Field(default=True,
|
54
|
-
description="""
|
55
|
-
If true, the tool will return the exception message if the tool call fails.
|
56
|
-
If false, raise the exception.
|
57
|
-
""")
|
58
54
|
|
59
55
|
@model_validator(mode="after")
|
60
56
|
def validate_model(self):
|
@@ -73,6 +69,45 @@ class MCPToolConfig(FunctionBaseConfig, name="mcp_tool_wrapper"):
|
|
73
69
|
return self
|
74
70
|
|
75
71
|
|
72
|
+
def mcp_tool_function(tool: MCPToolClient) -> FunctionInfo:
|
73
|
+
"""
|
74
|
+
Create a NeMo Agent toolkit function from an MCP tool.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
tool: The MCP tool to wrap
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
The NeMo Agent toolkit function
|
81
|
+
"""
|
82
|
+
|
83
|
+
def _convert_from_str(input_str: str) -> tool.input_schema:
|
84
|
+
return tool.input_schema.model_validate_json(input_str)
|
85
|
+
|
86
|
+
async def _response_fn(tool_input: BaseModel | None = None, **kwargs) -> str:
|
87
|
+
# Run the tool, catching any errors and sending to agent for correction
|
88
|
+
try:
|
89
|
+
if tool_input:
|
90
|
+
args = tool_input.model_dump()
|
91
|
+
return await tool.acall(args)
|
92
|
+
|
93
|
+
_ = tool.input_schema.model_validate(kwargs)
|
94
|
+
return await tool.acall(kwargs)
|
95
|
+
except Exception as e:
|
96
|
+
if tool_input:
|
97
|
+
logger.warning("Error calling tool %s with serialized input: %s",
|
98
|
+
tool.name,
|
99
|
+
tool_input.model_dump(),
|
100
|
+
exc_info=True)
|
101
|
+
else:
|
102
|
+
logger.warning("Error calling tool %s with input: %s", tool.name, kwargs, exc_info=True)
|
103
|
+
return str(e)
|
104
|
+
|
105
|
+
return FunctionInfo.create(single_fn=_response_fn,
|
106
|
+
description=tool.description,
|
107
|
+
input_schema=tool.input_schema,
|
108
|
+
converters=[_convert_from_str])
|
109
|
+
|
110
|
+
|
76
111
|
@register_function(config_type=MCPToolConfig)
|
77
112
|
async def mcp_tool(config: MCPToolConfig, builder: Builder):
|
78
113
|
"""
|
@@ -82,7 +117,6 @@ async def mcp_tool(config: MCPToolConfig, builder: Builder):
|
|
82
117
|
from nat.plugins.mcp.client_base import MCPSSEClient
|
83
118
|
from nat.plugins.mcp.client_base import MCPStdioClient
|
84
119
|
from nat.plugins.mcp.client_base import MCPStreamableHTTPClient
|
85
|
-
from nat.plugins.mcp.client_base import MCPToolClient
|
86
120
|
|
87
121
|
# Initialize the client
|
88
122
|
if config.transport == 'stdio':
|
@@ -102,32 +136,4 @@ async def mcp_tool(config: MCPToolConfig, builder: Builder):
|
|
102
136
|
|
103
137
|
logger.info("Configured to use tool: %s from MCP server at %s", tool.name, client.server_name)
|
104
138
|
|
105
|
-
|
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:
|
111
|
-
if tool_input:
|
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])
|
139
|
+
yield mcp_tool_function(tool)
|
nat/plugins/mcp/utils.py
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from enum import Enum
|
17
|
+
from typing import Any
|
18
|
+
|
19
|
+
from pydantic import BaseModel
|
20
|
+
from pydantic import Field
|
21
|
+
from pydantic import create_model
|
22
|
+
|
23
|
+
|
24
|
+
def model_from_mcp_schema(name: str, mcp_input_schema: dict) -> type[BaseModel]:
|
25
|
+
"""
|
26
|
+
Create a pydantic model from the input schema of the MCP tool
|
27
|
+
"""
|
28
|
+
_type_map = {
|
29
|
+
"string": str,
|
30
|
+
"number": float,
|
31
|
+
"integer": int,
|
32
|
+
"boolean": bool,
|
33
|
+
"array": list,
|
34
|
+
"null": None,
|
35
|
+
"object": dict,
|
36
|
+
}
|
37
|
+
|
38
|
+
properties = mcp_input_schema.get("properties", {})
|
39
|
+
required_fields = set(mcp_input_schema.get("required", []))
|
40
|
+
schema_dict = {}
|
41
|
+
|
42
|
+
def _generate_valid_classname(class_name: str):
|
43
|
+
return class_name.replace('_', ' ').replace('-', ' ').title().replace(' ', '')
|
44
|
+
|
45
|
+
def _generate_field(field_name: str, field_properties: dict[str, Any]) -> tuple:
|
46
|
+
json_type = field_properties.get("type", "string")
|
47
|
+
enum_vals = field_properties.get("enum")
|
48
|
+
|
49
|
+
if enum_vals:
|
50
|
+
enum_name = f"{field_name.capitalize()}Enum"
|
51
|
+
field_type = Enum(enum_name, {item: item for item in enum_vals})
|
52
|
+
|
53
|
+
elif json_type == "object" and "properties" in field_properties:
|
54
|
+
field_type = model_from_mcp_schema(name=field_name, mcp_input_schema=field_properties)
|
55
|
+
elif json_type == "array" and "items" in field_properties:
|
56
|
+
item_properties = field_properties.get("items", {})
|
57
|
+
if item_properties.get("type") == "object":
|
58
|
+
item_type = model_from_mcp_schema(name=field_name, mcp_input_schema=item_properties)
|
59
|
+
else:
|
60
|
+
item_type = _type_map.get(item_properties.get("type", "string"), Any)
|
61
|
+
field_type = list[item_type]
|
62
|
+
elif isinstance(json_type, list):
|
63
|
+
field_type = None
|
64
|
+
for t in json_type:
|
65
|
+
mapped = _type_map.get(t, Any)
|
66
|
+
field_type = mapped if field_type is None else field_type | mapped
|
67
|
+
|
68
|
+
return field_type, Field(
|
69
|
+
default=field_properties.get("default", None if "null" in json_type else ...),
|
70
|
+
description=field_properties.get("description", "")
|
71
|
+
)
|
72
|
+
else:
|
73
|
+
field_type = _type_map.get(json_type, Any)
|
74
|
+
|
75
|
+
# Determine the default value based on whether the field is required
|
76
|
+
if field_name in required_fields:
|
77
|
+
# Field is required - use explicit default if provided, otherwise make it required
|
78
|
+
default_value = field_properties.get("default", ...)
|
79
|
+
else:
|
80
|
+
# Field is optional - use explicit default if provided, otherwise None
|
81
|
+
default_value = field_properties.get("default", None)
|
82
|
+
# Make the type optional if no default was provided
|
83
|
+
if "default" not in field_properties:
|
84
|
+
field_type = field_type | None
|
85
|
+
|
86
|
+
nullable = field_properties.get("nullable", False)
|
87
|
+
description = field_properties.get("description", "")
|
88
|
+
|
89
|
+
field_type = field_type | None if nullable else field_type
|
90
|
+
|
91
|
+
return field_type, Field(default=default_value, description=description)
|
92
|
+
|
93
|
+
for field_name, field_props in properties.items():
|
94
|
+
schema_dict[field_name] = _generate_field(field_name=field_name, field_properties=field_props)
|
95
|
+
return create_model(f"{_generate_valid_classname(name)}InputSchema", **schema_dict)
|
{nvidia_nat_mcp-1.3.0a20250917.dist-info → nvidia_nat_mcp-1.3.0a20250923.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: nvidia-nat-mcp
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.0a20250923
|
4
4
|
Summary: Subpackage for MCP client integration in NeMo Agent toolkit
|
5
5
|
Keywords: ai,rag,agents,mcp
|
6
6
|
Classifier: Programming Language :: Python
|
@@ -9,8 +9,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.13
|
10
10
|
Requires-Python: <3.14,>=3.11
|
11
11
|
Description-Content-Type: text/markdown
|
12
|
-
Requires-Dist: nvidia-nat==v1.3.
|
13
|
-
Requires-Dist: mcp~=1.
|
12
|
+
Requires-Dist: nvidia-nat==v1.3.0a20250923
|
13
|
+
Requires-Dist: mcp~=1.14
|
14
14
|
|
15
15
|
<!--
|
16
16
|
SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
@@ -0,0 +1,18 @@
|
|
1
|
+
nat/meta/pypi.md,sha256=GyV4DI1d9ThgEhnYTQ0vh40Q9hPC8jN-goLnRiFDmZ8,1498
|
2
|
+
nat/plugins/mcp/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
|
3
|
+
nat/plugins/mcp/client_base.py,sha256=eWIHhZIaX8KGJMS7BTQ2szU2Nm4h57zCZ7uR5CmbYiY,15585
|
4
|
+
nat/plugins/mcp/client_impl.py,sha256=A1rSxVz1K29ZlqY-7BRrMNRAkCVZyUg7MS6vU0stYZc,8067
|
5
|
+
nat/plugins/mcp/exception_handler.py,sha256=JdPdZG1NgWpdRnIz7JTGHiJASS5wot9nJiD3SRWV4Kw,7649
|
6
|
+
nat/plugins/mcp/exceptions.py,sha256=EGVOnYlui8xufm8dhJyPL1SUqBLnCGOTvRoeyNcmcWE,5980
|
7
|
+
nat/plugins/mcp/register.py,sha256=HOT2Wl2isGuyFc7BUTi58-BbjI5-EtZMZo7stsv5pN4,831
|
8
|
+
nat/plugins/mcp/tool.py,sha256=v3MFsiaLJy8Ourcfqa6ohtAE2Nn-vqpC6Q6gsCdJ28Q,6165
|
9
|
+
nat/plugins/mcp/utils.py,sha256=3fuzYpC14wrfMOTOGvY2KHWcxZvBWqrxdDZD17lhmC8,4055
|
10
|
+
nat/plugins/mcp/auth/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
|
11
|
+
nat/plugins/mcp/auth/auth_provider.py,sha256=GOmM9vCfVd0QiyD_hBj7zCfkiimHa1WDfTTWWfMsr_k,17466
|
12
|
+
nat/plugins/mcp/auth/auth_provider_config.py,sha256=bE6IKV0yveo98KXr0xynrH5BMwPhRv8xbaMBwYu42YQ,3587
|
13
|
+
nat/plugins/mcp/auth/register.py,sha256=yzphsn1I4a5G39_IacbuX0ZQqGM8fevvTUM_B94UXKE,1211
|
14
|
+
nvidia_nat_mcp-1.3.0a20250923.dist-info/METADATA,sha256=GYws10CYVt4uL9OpBw7EiACV6DdEjLLDuoZexWgZJf8,1997
|
15
|
+
nvidia_nat_mcp-1.3.0a20250923.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
nvidia_nat_mcp-1.3.0a20250923.dist-info/entry_points.txt,sha256=rYvUp4i-klBr3bVNh7zYOPXret704vTjvCk1qd7FooI,97
|
17
|
+
nvidia_nat_mcp-1.3.0a20250923.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
|
18
|
+
nvidia_nat_mcp-1.3.0a20250923.dist-info/RECORD,,
|
@@ -1,13 +0,0 @@
|
|
1
|
-
nat/meta/pypi.md,sha256=GyV4DI1d9ThgEhnYTQ0vh40Q9hPC8jN-goLnRiFDmZ8,1498
|
2
|
-
nat/plugins/mcp/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
|
3
|
-
nat/plugins/mcp/client_base.py,sha256=4vFOBFoSpLpkq7r2iXDMbi6tDj02JBidZo5RiBR167w,13424
|
4
|
-
nat/plugins/mcp/client_impl.py,sha256=6rG3LcCX4TFsiST5O0_C8eOpY9LdnoSMarfAWeR76XA,9724
|
5
|
-
nat/plugins/mcp/exception_handler.py,sha256=JdPdZG1NgWpdRnIz7JTGHiJASS5wot9nJiD3SRWV4Kw,7649
|
6
|
-
nat/plugins/mcp/exceptions.py,sha256=EGVOnYlui8xufm8dhJyPL1SUqBLnCGOTvRoeyNcmcWE,5980
|
7
|
-
nat/plugins/mcp/register.py,sha256=HOT2Wl2isGuyFc7BUTi58-BbjI5-EtZMZo7stsv5pN4,831
|
8
|
-
nat/plugins/mcp/tool.py,sha256=MSRnnr1a6OjfqVkt2SYPkfLi9lU0JqovIZuTOL1cgHQ,6378
|
9
|
-
nvidia_nat_mcp-1.3.0a20250917.dist-info/METADATA,sha256=_u78nMJ-J8BKxiwJI4KZdf25baNEzu2x4guWmyQ6icI,1997
|
10
|
-
nvidia_nat_mcp-1.3.0a20250917.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
-
nvidia_nat_mcp-1.3.0a20250917.dist-info/entry_points.txt,sha256=x7dQTqek3GEdU-y9GslnygxMu0BSbEeUiOOMa2gvaaQ,52
|
12
|
-
nvidia_nat_mcp-1.3.0a20250917.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
|
13
|
-
nvidia_nat_mcp-1.3.0a20250917.dist-info/RECORD,,
|
File without changes
|
{nvidia_nat_mcp-1.3.0a20250917.dist-info → nvidia_nat_mcp-1.3.0a20250923.dist-info}/top_level.txt
RENAMED
File without changes
|