@@ -140,7 +140,7 @@ func (s *Server) RemovePrompts(names ...string) {
140140 func () bool { return s .prompts .remove (names ... ) })
141141}
142142
143- // AddRawTool adds a [Tool] to the server, or replaces one with the same name.
143+ // AddTool adds a [Tool] to the server, or replaces one with the same name.
144144// The Tool argument must not be modified after this call.
145145//
146146// The tool's input schema must be non-nil. For a tool that takes no input,
@@ -150,7 +150,14 @@ func (s *Server) RemovePrompts(names ...string) {
150150// When the handler is invoked as part of a CallTool request, req.Params.Arguments
151151// will be a json.RawMessage. Unmarshaling the arguments and validating them against the
152152// input schema are the handler author's responsibility.
153- func (s * Server ) AddRawTool (t * Tool , h RawToolHandler ) {
153+ func (s * Server ) AddTool (t * Tool , h ToolHandler ) {
154+ if t .InputSchema == nil {
155+ // This prevents the tool author from forgetting to write a schema where
156+ // one should be provided. If we papered over this by supplying the empty
157+ // schema, then every input would be validated and the problem wouldn't be
158+ // discovered until runtime, when the LLM sent bad data.
159+ panic (fmt .Errorf ("AddTool %q: missing input schema" , t .Name ))
160+ }
154161 st := & serverTool {tool : t , handler : h }
155162 // Assume there was a change, since add replaces existing tools.
156163 // (It's possible a tool was replaced with an identical one, but not worth checking.)
@@ -160,43 +167,50 @@ func (s *Server) AddRawTool(t *Tool, h RawToolHandler) {
160167 func () bool { s .tools .add (st ); return true })
161168}
162169
170+ // ToolFor returns a shallow copy of t and a [ToolHandler] that wraps h.
163171// If the tool's input schema is nil, it is set to the schema inferred from the In
164172// type parameter, using [jsonschema.For].
165173// If the tool's output schema is nil and the Out type parameter is not the empty
166174// interface, then the output schema is set to the schema inferred from Out.
167- func RawToolHandlerFor [In , Out any ](t * Tool , h ToolHandlerFor [In , Out ]) RawToolHandler {
168- hh , err := toolForErr (t , h )
175+ //
176+ // Most users will call [AddTool]. Use [ToolFor] if you wish to wrap the ToolHandler
177+ // before calling [Server.AddTool].
178+ func ToolFor [In , Out any ](t * Tool , h ToolHandlerFor [In , Out ]) (* Tool , ToolHandler ) {
179+ tt , hh , err := toolForErr (t , h )
169180 if err != nil {
170181 panic (fmt .Sprintf ("ToolFor: tool %q: %v" , t .Name , err ))
171182 }
172- return hh
183+ return tt , hh
173184}
174185
175186// TODO(v0.3.0): test
176- func toolForErr [In , Out any ](t * Tool , h ToolHandlerFor [In , Out ]) (RawToolHandler , error ) {
187+ func toolForErr [In , Out any ](t * Tool , h ToolHandlerFor [In , Out ]) (* Tool , ToolHandler , error ) {
177188 var err error
178- inputSchema := t .InputSchema
179- if inputSchema == nil {
180- inputSchema , err = jsonschema.For [In ](nil )
189+ tt := * t
190+ tt .InputSchema = t .InputSchema
191+ if tt .InputSchema == nil {
192+ tt .InputSchema , err = jsonschema.For [In ](nil )
181193 if err != nil {
182- return nil , fmt .Errorf ("input schema: %w" , err )
194+ return nil , nil , fmt .Errorf ("input schema: %w" , err )
183195 }
184196 }
185- inputResolved , err := inputSchema .Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
197+ inputResolved , err := tt . InputSchema .Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
186198 if err != nil {
187- return nil , fmt .Errorf ("resolving input schema: %w" , err )
199+ return nil , nil , fmt .Errorf ("resolving input schema: %w" , err )
188200 }
189201
190- outputSchema := t .OutputSchema
191- if outputSchema == nil && reflect .TypeFor [Out ]() != reflect .TypeFor [any ]() {
192- outputSchema , err = jsonschema.For [Out ](nil )
202+ if tt .OutputSchema == nil && reflect .TypeFor [Out ]() != reflect .TypeFor [any ]() {
203+ tt .OutputSchema , err = jsonschema.For [Out ](nil )
193204 }
194205 if err != nil {
195- return nil , fmt .Errorf ("output schema: %w" , err )
206+ return nil , nil , fmt .Errorf ("output schema: %w" , err )
196207 }
197- outputResolved , err := outputSchema .Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
198- if err != nil {
199- return nil , fmt .Errorf ("resolving output schema: %w" , err )
208+ var outputResolved * jsonschema.Resolved
209+ if tt .OutputSchema != nil {
210+ outputResolved , err = tt .OutputSchema .Resolve (& jsonschema.ResolveOptions {ValidateDefaults : true })
211+ if err != nil {
212+ return nil , nil , fmt .Errorf ("resolving output schema: %w" , err )
213+ }
200214 }
201215
202216 th := func (ctx context.Context , req * ServerRequest [* CallToolParams ]) (* CallToolResult , error ) {
@@ -228,12 +242,12 @@ func toolForErr[In, Out any](t *Tool, h ToolHandlerFor[In, Out]) (RawToolHandler
228242 return res , nil
229243 }
230244
231- return th , nil
245+ return & tt , th , nil
232246}
233247
234- // AddTool is a convenience for s.AddRawTool(t, RawToolHandler (t, h)).
248+ // AddTool is a convenience for s.AddTool(ToolFor (t, h)).
235249func AddTool [In , Out any ](s * Server , t * Tool , h ToolHandlerFor [In , Out ]) {
236- s .AddRawTool ( t , RawToolHandlerFor (t , h ))
250+ s .AddTool ( ToolFor (t , h ))
237251}
238252
239253// RemoveTools removes the tools with the given names.
0 commit comments