nvidia-nat-mcp 1.3.0a20250910__py3-none-any.whl → 1.5.0a20260117__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/meta/pypi.md +3 -3
- nat/plugins/mcp/__init__.py +1 -1
- nat/plugins/mcp/auth/__init__.py +14 -0
- nat/plugins/mcp/auth/auth_flow_handler.py +208 -0
- nat/plugins/mcp/auth/auth_provider.py +431 -0
- nat/plugins/mcp/auth/auth_provider_config.py +87 -0
- nat/plugins/mcp/auth/register.py +33 -0
- nat/plugins/mcp/auth/service_account/__init__.py +14 -0
- nat/plugins/mcp/auth/service_account/provider.py +136 -0
- nat/plugins/mcp/auth/service_account/provider_config.py +137 -0
- nat/plugins/mcp/auth/service_account/token_client.py +156 -0
- nat/plugins/mcp/auth/token_storage.py +265 -0
- nat/plugins/mcp/cli/__init__.py +15 -0
- nat/plugins/mcp/cli/commands.py +1094 -0
- nat/plugins/mcp/client/__init__.py +15 -0
- nat/plugins/mcp/client/client_base.py +665 -0
- nat/plugins/mcp/client/client_config.py +146 -0
- nat/plugins/mcp/client/client_impl.py +782 -0
- nat/plugins/mcp/exception_handler.py +2 -2
- nat/plugins/mcp/exceptions.py +1 -1
- nat/plugins/mcp/register.py +5 -4
- nat/plugins/mcp/server/__init__.py +15 -0
- nat/plugins/mcp/server/front_end_config.py +109 -0
- nat/plugins/mcp/server/front_end_plugin.py +155 -0
- nat/plugins/mcp/server/front_end_plugin_worker.py +415 -0
- nat/plugins/mcp/server/introspection_token_verifier.py +72 -0
- nat/plugins/mcp/server/memory_profiler.py +320 -0
- nat/plugins/mcp/server/register_frontend.py +27 -0
- nat/plugins/mcp/server/tool_converter.py +290 -0
- nat/plugins/mcp/utils.py +228 -0
- {nvidia_nat_mcp-1.3.0a20250910.dist-info → nvidia_nat_mcp-1.5.0a20260117.dist-info}/METADATA +15 -6
- nvidia_nat_mcp-1.5.0a20260117.dist-info/RECORD +37 -0
- nvidia_nat_mcp-1.5.0a20260117.dist-info/entry_points.txt +9 -0
- nvidia_nat_mcp-1.5.0a20260117.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
- nvidia_nat_mcp-1.5.0a20260117.dist-info/licenses/LICENSE.md +201 -0
- nat/plugins/mcp/client_base.py +0 -406
- nat/plugins/mcp/client_impl.py +0 -229
- nat/plugins/mcp/tool.py +0 -133
- nvidia_nat_mcp-1.3.0a20250910.dist-info/RECORD +0 -13
- nvidia_nat_mcp-1.3.0a20250910.dist-info/entry_points.txt +0 -2
- {nvidia_nat_mcp-1.3.0a20250910.dist-info → nvidia_nat_mcp-1.5.0a20260117.dist-info}/WHEEL +0 -0
- {nvidia_nat_mcp-1.3.0a20250910.dist-info → nvidia_nat_mcp-1.5.0a20260117.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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
|
+
import hashlib
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
from abc import ABC
|
|
20
|
+
from abc import abstractmethod
|
|
21
|
+
|
|
22
|
+
from nat.data_models.authentication import AuthResult
|
|
23
|
+
from nat.data_models.authentication import BasicAuthCred
|
|
24
|
+
from nat.data_models.authentication import BearerTokenCred
|
|
25
|
+
from nat.data_models.authentication import CookieCred
|
|
26
|
+
from nat.data_models.authentication import HeaderCred
|
|
27
|
+
from nat.data_models.authentication import QueryCred
|
|
28
|
+
from nat.data_models.object_store import NoSuchKeyError
|
|
29
|
+
from nat.object_store.interfaces import ObjectStore
|
|
30
|
+
from nat.object_store.models import ObjectStoreItem
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TokenStorageBase(ABC):
|
|
36
|
+
"""
|
|
37
|
+
Abstract base class for token storage implementations.
|
|
38
|
+
|
|
39
|
+
Token storage implementations handle the secure persistence of authentication
|
|
40
|
+
tokens for MCP OAuth2 flows. Implementations can use various backends such as
|
|
41
|
+
object stores, databases, or in-memory storage.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
async def store(self, user_id: str, auth_result: AuthResult) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Store an authentication result for a user.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
user_id: The unique identifier for the user
|
|
51
|
+
auth_result: The authentication result to store
|
|
52
|
+
"""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
async def retrieve(self, user_id: str) -> AuthResult | None:
|
|
57
|
+
"""
|
|
58
|
+
Retrieve an authentication result for a user.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
user_id: The unique identifier for the user
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
The authentication result if found, None otherwise
|
|
65
|
+
"""
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
async def delete(self, user_id: str) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Delete an authentication result for a user.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
user_id: The unique identifier for the user
|
|
75
|
+
"""
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
async def clear_all(self) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Clear all stored authentication results.
|
|
82
|
+
"""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ObjectStoreTokenStorage(TokenStorageBase):
|
|
87
|
+
"""
|
|
88
|
+
Token storage implementation backed by a NeMo Agent toolkit object store.
|
|
89
|
+
|
|
90
|
+
This implementation uses the object store infrastructure to persist tokens,
|
|
91
|
+
which provides encryption at rest, access controls, and persistence across
|
|
92
|
+
restarts when using backends like S3, MySQL, or Redis.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, object_store: ObjectStore):
|
|
96
|
+
"""
|
|
97
|
+
Initialize the object store token storage.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
object_store: The object store instance to use for token persistence
|
|
101
|
+
"""
|
|
102
|
+
self._object_store = object_store
|
|
103
|
+
|
|
104
|
+
def _get_key(self, user_id: str) -> str:
|
|
105
|
+
"""
|
|
106
|
+
Generate the object store key for a user's token.
|
|
107
|
+
|
|
108
|
+
Uses SHA256 hash to ensure the key is S3-compatible and doesn't
|
|
109
|
+
contain special characters like "://" that are invalid in object keys.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
user_id: The user identifier
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
The object store key
|
|
116
|
+
"""
|
|
117
|
+
# Hash the user_id to create an S3-safe key
|
|
118
|
+
user_hash = hashlib.sha256(user_id.encode('utf-8')).hexdigest()
|
|
119
|
+
return f"tokens/{user_hash}"
|
|
120
|
+
|
|
121
|
+
async def store(self, user_id: str, auth_result: AuthResult) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Store an authentication result in the object store.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
user_id: The unique identifier for the user
|
|
127
|
+
auth_result: The authentication result to store
|
|
128
|
+
"""
|
|
129
|
+
key = self._get_key(user_id)
|
|
130
|
+
|
|
131
|
+
# Serialize the AuthResult to JSON with secrets exposed
|
|
132
|
+
# SecretStr values are masked by default, so we need to expose them manually
|
|
133
|
+
# Create a serializable dict with exposed secrets
|
|
134
|
+
auth_dict = auth_result.model_dump(mode='json')
|
|
135
|
+
# Manually expose SecretStr values in credentials
|
|
136
|
+
for i, cred_obj in enumerate(auth_result.credentials):
|
|
137
|
+
if isinstance(cred_obj, BearerTokenCred):
|
|
138
|
+
auth_dict['credentials'][i]['token'] = cred_obj.token.get_secret_value()
|
|
139
|
+
elif isinstance(cred_obj, BasicAuthCred):
|
|
140
|
+
auth_dict['credentials'][i]['username'] = cred_obj.username.get_secret_value()
|
|
141
|
+
auth_dict['credentials'][i]['password'] = cred_obj.password.get_secret_value()
|
|
142
|
+
elif isinstance(cred_obj, HeaderCred | QueryCred | CookieCred):
|
|
143
|
+
auth_dict['credentials'][i]['value'] = cred_obj.value.get_secret_value()
|
|
144
|
+
|
|
145
|
+
data = json.dumps(auth_dict).encode('utf-8')
|
|
146
|
+
|
|
147
|
+
# Prepare metadata
|
|
148
|
+
metadata = {}
|
|
149
|
+
if auth_result.token_expires_at:
|
|
150
|
+
metadata["expires_at"] = auth_result.token_expires_at.isoformat()
|
|
151
|
+
|
|
152
|
+
# Create the object store item
|
|
153
|
+
item = ObjectStoreItem(data=data, content_type="application/json", metadata=metadata if metadata else None)
|
|
154
|
+
|
|
155
|
+
# Store using upsert to handle both new and existing tokens
|
|
156
|
+
await self._object_store.upsert_object(key, item)
|
|
157
|
+
|
|
158
|
+
async def retrieve(self, user_id: str) -> AuthResult | None:
|
|
159
|
+
"""
|
|
160
|
+
Retrieve an authentication result from the object store.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
user_id: The unique identifier for the user
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
The authentication result if found, None otherwise
|
|
167
|
+
"""
|
|
168
|
+
key = self._get_key(user_id)
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
item = await self._object_store.get_object(key)
|
|
172
|
+
# Deserialize the AuthResult from JSON
|
|
173
|
+
auth_result = AuthResult.model_validate_json(item.data)
|
|
174
|
+
return auth_result
|
|
175
|
+
except NoSuchKeyError:
|
|
176
|
+
return None
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.error(f"Error deserializing token for user {user_id}: {e}", exc_info=True)
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
async def delete(self, user_id: str) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Delete an authentication result from the object store.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
user_id: The unique identifier for the user
|
|
187
|
+
"""
|
|
188
|
+
key = self._get_key(user_id)
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
await self._object_store.delete_object(key)
|
|
192
|
+
except NoSuchKeyError:
|
|
193
|
+
# Token doesn't exist, which is fine for delete operations
|
|
194
|
+
pass
|
|
195
|
+
|
|
196
|
+
async def clear_all(self) -> None:
|
|
197
|
+
"""
|
|
198
|
+
Clear all stored authentication results.
|
|
199
|
+
|
|
200
|
+
Note: This implementation does not support clearing all tokens as the
|
|
201
|
+
object store interface doesn't provide a list operation. Individual
|
|
202
|
+
tokens must be deleted explicitly.
|
|
203
|
+
"""
|
|
204
|
+
logger.warning("clear_all() is not supported for ObjectStoreTokenStorage")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class InMemoryTokenStorage(TokenStorageBase):
|
|
208
|
+
"""
|
|
209
|
+
In-memory token storage using the built-in object store provided by the NeMo Agent toolkit.
|
|
210
|
+
|
|
211
|
+
This implementation uses the in-memory object store for token persistence,
|
|
212
|
+
which provides a secure default option that doesn't require external storage
|
|
213
|
+
configuration. Tokens are stored in memory and cleared when the process exits.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
def __init__(self):
|
|
217
|
+
"""
|
|
218
|
+
Initialize the in-memory token storage.
|
|
219
|
+
"""
|
|
220
|
+
from nat.object_store.in_memory_object_store import InMemoryObjectStore
|
|
221
|
+
|
|
222
|
+
# Create a dedicated in-memory object store for tokens
|
|
223
|
+
self._object_store = InMemoryObjectStore()
|
|
224
|
+
|
|
225
|
+
# Wrap with ObjectStoreTokenStorage for the actual implementation
|
|
226
|
+
self._storage = ObjectStoreTokenStorage(self._object_store)
|
|
227
|
+
logger.debug("Initialized in-memory token storage")
|
|
228
|
+
|
|
229
|
+
async def store(self, user_id: str, auth_result: AuthResult) -> None:
|
|
230
|
+
"""
|
|
231
|
+
Store an authentication result in memory.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
user_id: The unique identifier for the user
|
|
235
|
+
auth_result: The authentication result to store
|
|
236
|
+
"""
|
|
237
|
+
await self._storage.store(user_id, auth_result)
|
|
238
|
+
|
|
239
|
+
async def retrieve(self, user_id: str) -> AuthResult | None:
|
|
240
|
+
"""
|
|
241
|
+
Retrieve an authentication result from memory.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
user_id: The unique identifier for the user
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
The authentication result if found, None otherwise
|
|
248
|
+
"""
|
|
249
|
+
return await self._storage.retrieve(user_id)
|
|
250
|
+
|
|
251
|
+
async def delete(self, user_id: str) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Delete an authentication result from memory.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
user_id: The unique identifier for the user
|
|
257
|
+
"""
|
|
258
|
+
await self._storage.delete(user_id)
|
|
259
|
+
|
|
260
|
+
async def clear_all(self) -> None:
|
|
261
|
+
"""
|
|
262
|
+
Clear all stored authentication results from memory.
|
|
263
|
+
"""
|
|
264
|
+
# For in-memory storage, we can access the internal storage
|
|
265
|
+
self._object_store._store.clear()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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
|
+
"""MCP CLI commands."""
|