@@ -79,8 +79,11 @@ def __parse_tool(
79
79
else : # regular parameter
80
80
params .append (p )
81
81
82
- authn_params = identify_required_authn_params (
83
- authn_params , auth_token_getters .keys ()
82
+ authn_params , _ , used_auth_keys = identify_required_authn_params (
83
+ # TODO: Add schema.authRequired as second arg
84
+ authn_params ,
85
+ [],
86
+ auth_token_getters .keys (),
84
87
)
85
88
86
89
tool = ToolboxTool (
@@ -97,9 +100,6 @@ def __parse_tool(
97
100
)
98
101
99
102
used_bound_keys = set (bound_params .keys ())
100
- used_auth_keys : set [str ] = set ()
101
- for required_sources in authn_params .values ():
102
- used_auth_keys .update (required_sources )
103
103
104
104
return tool , used_auth_keys , used_bound_keys
105
105
@@ -160,6 +160,9 @@ async def load_tool(
160
160
for execution. The specific arguments and behavior of the callable
161
161
depend on the tool itself.
162
162
163
+ Raises:
164
+ ValueError: If the loaded tool instance fails to utilize at least
165
+ one provided parameter or auth token (if any provided).
163
166
"""
164
167
# Resolve client headers
165
168
resolved_headers = {
@@ -176,56 +179,131 @@ async def load_tool(
176
179
# parse the provided definition to a tool
177
180
if name not in manifest .tools :
178
181
# TODO: Better exception
179
- raise Exception (f"Tool '{ name } ' not found!" )
180
- tool , _ , _ = self .__parse_tool (
182
+ raise ValueError (f"Tool '{ name } ' not found!" )
183
+ tool , used_auth_keys , used_bound_keys = self .__parse_tool (
181
184
name ,
182
185
manifest .tools [name ],
183
186
auth_token_getters ,
184
187
bound_params ,
185
188
self .__client_headers ,
186
189
)
187
190
191
+ provided_auth_keys = set (auth_token_getters .keys ())
192
+ provided_bound_keys = set (bound_params .keys ())
193
+
194
+ unused_auth = provided_auth_keys - used_auth_keys
195
+ unused_bound = provided_bound_keys - used_bound_keys
196
+
197
+ if unused_auth or unused_bound :
198
+ error_messages = []
199
+ if unused_auth :
200
+ error_messages .append (f"unused auth tokens: { ', ' .join (unused_auth )} " )
201
+ if unused_bound :
202
+ error_messages .append (
203
+ f"unused bound parameters: { ', ' .join (unused_bound )} "
204
+ )
205
+ raise ValueError (
206
+ f"Validation failed for tool '{ name } ': { '; ' .join (error_messages ) } ."
207
+ )
208
+
188
209
return tool
189
210
190
211
async def load_toolset (
191
212
self ,
192
213
name : Optional [str ] = None ,
193
214
auth_token_getters : dict [str , Callable [[], str ]] = {},
194
215
bound_params : Mapping [str , Union [Callable [[], Any ], Any ]] = {},
216
+ strict : bool = False ,
195
217
) -> list [ToolboxTool ]:
196
218
"""
197
219
Asynchronously fetches a toolset and loads all tools defined within it.
198
220
199
221
Args:
200
- name: Name of the toolset to load tools .
222
+ name: Name of the toolset to load. If None, loads the default toolset .
201
223
auth_token_getters: A mapping of authentication service names to
202
224
callables that return the corresponding authentication token.
203
225
bound_params: A mapping of parameter names to bind to specific values or
204
226
callables that are called to produce values as needed.
227
+ strict: If True, raises an error if *any* loaded tool instance fails
228
+ to utilize at least one provided parameter or auth token (if any
229
+ provided). If False (default), raises an error only if a
230
+ user-provided parameter or auth token cannot be applied to *any*
231
+ loaded tool across the set.
205
232
206
233
Returns:
207
234
list[ToolboxTool]: A list of callables, one for each tool defined
208
235
in the toolset.
236
+
237
+ Raises:
238
+ ValueError: If validation fails based on the `strict` flag.
209
239
"""
240
+
210
241
# Resolve client headers
211
242
original_headers = self .__client_headers
212
243
resolved_headers = {
213
244
header_name : await resolve_value (original_headers [header_name ])
214
245
for header_name in original_headers
215
246
}
216
- # Request the definition of the tool from the server
247
+ # Request the definition of the toolset from the server
217
248
url = f"{ self .__base_url } /api/toolset/{ name or '' } "
218
249
async with self .__session .get (url , headers = resolved_headers ) as response :
219
250
json = await response .json ()
220
251
manifest : ManifestSchema = ManifestSchema (** json )
221
252
222
- # parse each tools name and schema into a list of ToolboxTools
223
- tools = [
224
- self .__parse_tool (
225
- n , s , auth_token_getters , bound_params , self .__client_headers
226
- )[0 ]
227
- for n , s in manifest .tools .items ()
228
- ]
253
+ tools : list [ToolboxTool ] = []
254
+ overall_used_auth_keys : set [str ] = set ()
255
+ overall_used_bound_params : set [str ] = set ()
256
+ provided_auth_keys = set (auth_token_getters .keys ())
257
+ provided_bound_keys = set (bound_params .keys ())
258
+
259
+ # parse each tool's name and schema into a list of ToolboxTools
260
+ for tool_name , schema in manifest .tools .items ():
261
+ tool , used_auth_keys , used_bound_keys = self .__parse_tool (
262
+ tool_name ,
263
+ schema ,
264
+ auth_token_getters ,
265
+ bound_params ,
266
+ self .__client_headers ,
267
+ )
268
+ tools .append (tool )
269
+
270
+ if strict :
271
+ unused_auth = provided_auth_keys - used_auth_keys
272
+ unused_bound = provided_bound_keys - used_bound_keys
273
+ if unused_auth or unused_bound :
274
+ error_messages = []
275
+ if unused_auth :
276
+ error_messages .append (
277
+ f"unused auth tokens: { ', ' .join (unused_auth )} "
278
+ )
279
+ if unused_bound :
280
+ error_messages .append (
281
+ f"unused bound parameters: { ', ' .join (unused_bound )} "
282
+ )
283
+ raise ValueError (
284
+ f"Validation failed for tool '{ tool_name } ': { '; ' .join (error_messages ) } ."
285
+ )
286
+ else :
287
+ overall_used_auth_keys .update (used_auth_keys )
288
+ overall_used_bound_params .update (used_bound_keys )
289
+
290
+ unused_auth = provided_auth_keys - overall_used_auth_keys
291
+ unused_bound = provided_bound_keys - overall_used_bound_params
292
+
293
+ if unused_auth or unused_bound :
294
+ error_messages = []
295
+ if unused_auth :
296
+ error_messages .append (
297
+ f"unused auth tokens could not be applied to any tool: { ', ' .join (unused_auth )} "
298
+ )
299
+ if unused_bound :
300
+ error_messages .append (
301
+ f"unused bound parameters could not be applied to any tool: { ', ' .join (unused_bound )} "
302
+ )
303
+ raise ValueError (
304
+ f"Validation failed for toolset '{ name or 'default' } ': { '; ' .join (error_messages ) } ."
305
+ )
306
+
229
307
return tools
230
308
231
309
async def add_headers (
0 commit comments