@@ -189,31 +189,26 @@ func toolFor[In, Out any](t *Tool, h ToolHandlerFor[In, Out]) (*Tool, ToolHandle
189
189
190
190
// TODO(v0.3.0): test
191
191
func toolForErr [In , Out any ](t * Tool , h ToolHandlerFor [In , Out ]) (* Tool , ToolHandler , error ) {
192
- var err error
193
192
tt := * t
194
- tt .InputSchema = t .InputSchema
195
- if tt .InputSchema == nil {
196
- tt .InputSchema , err = jsonschema.For [In ](nil )
193
+ var inputResolved * jsonschema.Resolved
194
+ if _ , err := setSchema [In ](& tt .InputSchema , & inputResolved ); err != nil {
195
+ return nil , nil , fmt .Errorf ("input schema: %w" , err )
196
+ }
197
+
198
+ // Handling for zero values:
199
+ //
200
+ // If Out is a pointer type and we've derived the output schema from its
201
+ // element type, use the zero value of its element type in place of a typed
202
+ // nil.
203
+ var (
204
+ elemZero any // only non-nil if Out is a pointer type
205
+ outputResolved * jsonschema.Resolved
206
+ )
207
+ if reflect .TypeFor [Out ]() != reflect .TypeFor [any ]() {
208
+ var err error
209
+ elemZero , err = setSchema [Out ](& t .OutputSchema , & outputResolved )
197
210
if err != nil {
198
- return nil , nil , fmt .Errorf ("input schema: %w" , err )
199
- }
200
- }
201
- inputResolved , err := tt .InputSchema .Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
202
- if err != nil {
203
- return nil , nil , fmt .Errorf ("resolving input schema: %w" , err )
204
- }
205
-
206
- if tt .OutputSchema == nil && reflect .TypeFor [Out ]() != reflect .TypeFor [any ]() {
207
- tt .OutputSchema , err = jsonschema.For [Out ](nil )
208
- }
209
- if err != nil {
210
- return nil , nil , fmt .Errorf ("output schema: %w" , err )
211
- }
212
- var outputResolved * jsonschema.Resolved
213
- if tt .OutputSchema != nil {
214
- outputResolved , err = tt .OutputSchema .Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
215
- if err != nil {
216
- return nil , nil , fmt .Errorf ("resolving output schema: %w" , err )
211
+ return nil , nil , fmt .Errorf ("output schema: %v" , err )
217
212
}
218
213
}
219
214
@@ -255,12 +250,54 @@ func toolForErr[In, Out any](t *Tool, h ToolHandlerFor[In, Out]) (*Tool, ToolHan
255
250
res = & CallToolResult {}
256
251
}
257
252
res .StructuredContent = out
253
+ if elemZero != nil {
254
+ // Avoid typed nil, which will serialize as JSON null.
255
+ // Instead, use the zero value of the non-zero
256
+ var z Out
257
+ if any (out ) == any (z ) { // zero is only non-nil if Out is a pointer type
258
+ res .StructuredContent = elemZero
259
+ }
260
+ }
261
+ if tt .OutputSchema != nil && elemZero != nil {
262
+ res .StructuredContent = elemZero
263
+ }
258
264
return res , nil
259
265
}
260
266
261
267
return & tt , th , nil
262
268
}
263
269
270
+ // setSchema sets the schema and resolved schema corresponding to the type T.
271
+ //
272
+ // If sfield is nil, the schema is derived from T.
273
+ //
274
+ // Pointers are treated equivalently to non-pointers when deriving the schema.
275
+ // If an indirection occurred to derive the schema, a non-nil zero value is
276
+ // returned to be used in place of the typed nil zero value.
277
+ //
278
+ // Note that if sfield already holds a schema, zero will be nil even if T is a
279
+ // pointer: if the user provided the schema, they may have intentionally
280
+ // derived it from the pointer type, and handling of zero values is up to them.
281
+ //
282
+ // TODO(rfindley): we really shouldn't ever return 'null' results. Maybe we
283
+ // should have a jsonschema.Zero(schema) helper?
284
+ func setSchema [T any ](sfield * * jsonschema.Schema , rfield * * jsonschema.Resolved ) (zero any , err error ) {
285
+ rt := reflect .TypeFor [T ]()
286
+ if * sfield == nil {
287
+ if rt .Kind () == reflect .Pointer {
288
+ rt = rt .Elem ()
289
+ zero = reflect .Zero (rt ).Interface ()
290
+ }
291
+ // TODO: we should be able to pass nil opts here.
292
+ * sfield , err = jsonschema .ForType (rt , & jsonschema.ForOptions {})
293
+ }
294
+ if err != nil {
295
+ return zero , err
296
+ }
297
+ * rfield , err = (* sfield ).Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
298
+ return zero , err
299
+ }
300
+
264
301
// AddTool adds a tool and handler to the server.
265
302
//
266
303
// A shallow copy of the tool is made first.
0 commit comments