tools.go 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. package mcp
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "reflect"
  8. "strconv"
  9. "github.com/invopop/jsonschema"
  10. )
  11. var errToolSchemaConflict = errors.New("provide either InputSchema or RawInputSchema, not both")
  12. // ListToolsRequest is sent from the client to request a list of tools the
  13. // server has.
  14. type ListToolsRequest struct {
  15. PaginatedRequest
  16. Header http.Header `json:"-"`
  17. }
  18. // ListToolsResult is the server's response to a tools/list request from the
  19. // client.
  20. type ListToolsResult struct {
  21. PaginatedResult
  22. Tools []Tool `json:"tools"`
  23. }
  24. // CallToolResult is the server's response to a tool call.
  25. //
  26. // Any errors that originate from the tool SHOULD be reported inside the result
  27. // object, with `isError` set to true, _not_ as an MCP protocol-level error
  28. // response. Otherwise, the LLM would not be able to see that an error occurred
  29. // and self-correct.
  30. //
  31. // However, any errors in _finding_ the tool, an error indicating that the
  32. // server does not support tool calls, or any other exceptional conditions,
  33. // should be reported as an MCP error response.
  34. type CallToolResult struct {
  35. Result
  36. Content []Content `json:"content"` // Can be TextContent, ImageContent, AudioContent, or EmbeddedResource
  37. // Structured content returned as a JSON object in the structuredContent field of a result.
  38. // For backwards compatibility, a tool that returns structured content SHOULD also return
  39. // functionally equivalent unstructured content.
  40. StructuredContent any `json:"structuredContent,omitempty"`
  41. // Whether the tool call ended in an error.
  42. //
  43. // If not set, this is assumed to be false (the call was successful).
  44. IsError bool `json:"isError,omitempty"`
  45. }
  46. // CallToolRequest is used by the client to invoke a tool provided by the server.
  47. type CallToolRequest struct {
  48. Request
  49. Header http.Header `json:"-"` // HTTP headers from the original request
  50. Params CallToolParams `json:"params"`
  51. }
  52. type CallToolParams struct {
  53. Name string `json:"name"`
  54. Arguments any `json:"arguments,omitempty"`
  55. Meta *Meta `json:"_meta,omitempty"`
  56. }
  57. // GetArguments returns the Arguments as map[string]any for backward compatibility
  58. // If Arguments is not a map, it returns an empty map
  59. func (r CallToolRequest) GetArguments() map[string]any {
  60. if args, ok := r.Params.Arguments.(map[string]any); ok {
  61. return args
  62. }
  63. return nil
  64. }
  65. // GetRawArguments returns the Arguments as-is without type conversion
  66. // This allows users to access the raw arguments in any format
  67. func (r CallToolRequest) GetRawArguments() any {
  68. return r.Params.Arguments
  69. }
  70. // BindArguments unmarshals the Arguments into the provided struct
  71. // This is useful for working with strongly-typed arguments
  72. func (r CallToolRequest) BindArguments(target any) error {
  73. if target == nil || reflect.ValueOf(target).Kind() != reflect.Ptr {
  74. return fmt.Errorf("target must be a non-nil pointer")
  75. }
  76. // Fast-path: already raw JSON
  77. if raw, ok := r.Params.Arguments.(json.RawMessage); ok {
  78. return json.Unmarshal(raw, target)
  79. }
  80. data, err := json.Marshal(r.Params.Arguments)
  81. if err != nil {
  82. return fmt.Errorf("failed to marshal arguments: %w", err)
  83. }
  84. return json.Unmarshal(data, target)
  85. }
  86. // GetString returns a string argument by key, or the default value if not found
  87. func (r CallToolRequest) GetString(key string, defaultValue string) string {
  88. args := r.GetArguments()
  89. if val, ok := args[key]; ok {
  90. if str, ok := val.(string); ok {
  91. return str
  92. }
  93. }
  94. return defaultValue
  95. }
  96. // RequireString returns a string argument by key, or an error if not found or not a string
  97. func (r CallToolRequest) RequireString(key string) (string, error) {
  98. args := r.GetArguments()
  99. if val, ok := args[key]; ok {
  100. if str, ok := val.(string); ok {
  101. return str, nil
  102. }
  103. return "", fmt.Errorf("argument %q is not a string", key)
  104. }
  105. return "", fmt.Errorf("required argument %q not found", key)
  106. }
  107. // GetInt returns an int argument by key, or the default value if not found
  108. func (r CallToolRequest) GetInt(key string, defaultValue int) int {
  109. args := r.GetArguments()
  110. if val, ok := args[key]; ok {
  111. switch v := val.(type) {
  112. case int:
  113. return v
  114. case float64:
  115. return int(v)
  116. case string:
  117. if i, err := strconv.Atoi(v); err == nil {
  118. return i
  119. }
  120. }
  121. }
  122. return defaultValue
  123. }
  124. // RequireInt returns an int argument by key, or an error if not found or not convertible to int
  125. func (r CallToolRequest) RequireInt(key string) (int, error) {
  126. args := r.GetArguments()
  127. if val, ok := args[key]; ok {
  128. switch v := val.(type) {
  129. case int:
  130. return v, nil
  131. case float64:
  132. return int(v), nil
  133. case string:
  134. if i, err := strconv.Atoi(v); err == nil {
  135. return i, nil
  136. }
  137. return 0, fmt.Errorf("argument %q cannot be converted to int", key)
  138. default:
  139. return 0, fmt.Errorf("argument %q is not an int", key)
  140. }
  141. }
  142. return 0, fmt.Errorf("required argument %q not found", key)
  143. }
  144. // GetFloat returns a float64 argument by key, or the default value if not found
  145. func (r CallToolRequest) GetFloat(key string, defaultValue float64) float64 {
  146. args := r.GetArguments()
  147. if val, ok := args[key]; ok {
  148. switch v := val.(type) {
  149. case float64:
  150. return v
  151. case int:
  152. return float64(v)
  153. case string:
  154. if f, err := strconv.ParseFloat(v, 64); err == nil {
  155. return f
  156. }
  157. }
  158. }
  159. return defaultValue
  160. }
  161. // RequireFloat returns a float64 argument by key, or an error if not found or not convertible to float64
  162. func (r CallToolRequest) RequireFloat(key string) (float64, error) {
  163. args := r.GetArguments()
  164. if val, ok := args[key]; ok {
  165. switch v := val.(type) {
  166. case float64:
  167. return v, nil
  168. case int:
  169. return float64(v), nil
  170. case string:
  171. if f, err := strconv.ParseFloat(v, 64); err == nil {
  172. return f, nil
  173. }
  174. return 0, fmt.Errorf("argument %q cannot be converted to float64", key)
  175. default:
  176. return 0, fmt.Errorf("argument %q is not a float64", key)
  177. }
  178. }
  179. return 0, fmt.Errorf("required argument %q not found", key)
  180. }
  181. // GetBool returns a bool argument by key, or the default value if not found
  182. func (r CallToolRequest) GetBool(key string, defaultValue bool) bool {
  183. args := r.GetArguments()
  184. if val, ok := args[key]; ok {
  185. switch v := val.(type) {
  186. case bool:
  187. return v
  188. case string:
  189. if b, err := strconv.ParseBool(v); err == nil {
  190. return b
  191. }
  192. case int:
  193. return v != 0
  194. case float64:
  195. return v != 0
  196. }
  197. }
  198. return defaultValue
  199. }
  200. // RequireBool returns a bool argument by key, or an error if not found or not convertible to bool
  201. func (r CallToolRequest) RequireBool(key string) (bool, error) {
  202. args := r.GetArguments()
  203. if val, ok := args[key]; ok {
  204. switch v := val.(type) {
  205. case bool:
  206. return v, nil
  207. case string:
  208. if b, err := strconv.ParseBool(v); err == nil {
  209. return b, nil
  210. }
  211. return false, fmt.Errorf("argument %q cannot be converted to bool", key)
  212. case int:
  213. return v != 0, nil
  214. case float64:
  215. return v != 0, nil
  216. default:
  217. return false, fmt.Errorf("argument %q is not a bool", key)
  218. }
  219. }
  220. return false, fmt.Errorf("required argument %q not found", key)
  221. }
  222. // GetStringSlice returns a string slice argument by key, or the default value if not found
  223. func (r CallToolRequest) GetStringSlice(key string, defaultValue []string) []string {
  224. args := r.GetArguments()
  225. if val, ok := args[key]; ok {
  226. switch v := val.(type) {
  227. case []string:
  228. return v
  229. case []any:
  230. result := make([]string, 0, len(v))
  231. for _, item := range v {
  232. if str, ok := item.(string); ok {
  233. result = append(result, str)
  234. }
  235. }
  236. return result
  237. }
  238. }
  239. return defaultValue
  240. }
  241. // RequireStringSlice returns a string slice argument by key, or an error if not found or not convertible to string slice
  242. func (r CallToolRequest) RequireStringSlice(key string) ([]string, error) {
  243. args := r.GetArguments()
  244. if val, ok := args[key]; ok {
  245. switch v := val.(type) {
  246. case []string:
  247. return v, nil
  248. case []any:
  249. result := make([]string, 0, len(v))
  250. for i, item := range v {
  251. if str, ok := item.(string); ok {
  252. result = append(result, str)
  253. } else {
  254. return nil, fmt.Errorf("item %d in argument %q is not a string", i, key)
  255. }
  256. }
  257. return result, nil
  258. default:
  259. return nil, fmt.Errorf("argument %q is not a string slice", key)
  260. }
  261. }
  262. return nil, fmt.Errorf("required argument %q not found", key)
  263. }
  264. // GetIntSlice returns an int slice argument by key, or the default value if not found
  265. func (r CallToolRequest) GetIntSlice(key string, defaultValue []int) []int {
  266. args := r.GetArguments()
  267. if val, ok := args[key]; ok {
  268. switch v := val.(type) {
  269. case []int:
  270. return v
  271. case []any:
  272. result := make([]int, 0, len(v))
  273. for _, item := range v {
  274. switch num := item.(type) {
  275. case int:
  276. result = append(result, num)
  277. case float64:
  278. result = append(result, int(num))
  279. case string:
  280. if i, err := strconv.Atoi(num); err == nil {
  281. result = append(result, i)
  282. }
  283. }
  284. }
  285. return result
  286. }
  287. }
  288. return defaultValue
  289. }
  290. // RequireIntSlice returns an int slice argument by key, or an error if not found or not convertible to int slice
  291. func (r CallToolRequest) RequireIntSlice(key string) ([]int, error) {
  292. args := r.GetArguments()
  293. if val, ok := args[key]; ok {
  294. switch v := val.(type) {
  295. case []int:
  296. return v, nil
  297. case []any:
  298. result := make([]int, 0, len(v))
  299. for i, item := range v {
  300. switch num := item.(type) {
  301. case int:
  302. result = append(result, num)
  303. case float64:
  304. result = append(result, int(num))
  305. case string:
  306. if i, err := strconv.Atoi(num); err == nil {
  307. result = append(result, i)
  308. } else {
  309. return nil, fmt.Errorf("item %d in argument %q cannot be converted to int", i, key)
  310. }
  311. default:
  312. return nil, fmt.Errorf("item %d in argument %q is not an int", i, key)
  313. }
  314. }
  315. return result, nil
  316. default:
  317. return nil, fmt.Errorf("argument %q is not an int slice", key)
  318. }
  319. }
  320. return nil, fmt.Errorf("required argument %q not found", key)
  321. }
  322. // GetFloatSlice returns a float64 slice argument by key, or the default value if not found
  323. func (r CallToolRequest) GetFloatSlice(key string, defaultValue []float64) []float64 {
  324. args := r.GetArguments()
  325. if val, ok := args[key]; ok {
  326. switch v := val.(type) {
  327. case []float64:
  328. return v
  329. case []any:
  330. result := make([]float64, 0, len(v))
  331. for _, item := range v {
  332. switch num := item.(type) {
  333. case float64:
  334. result = append(result, num)
  335. case int:
  336. result = append(result, float64(num))
  337. case string:
  338. if f, err := strconv.ParseFloat(num, 64); err == nil {
  339. result = append(result, f)
  340. }
  341. }
  342. }
  343. return result
  344. }
  345. }
  346. return defaultValue
  347. }
  348. // RequireFloatSlice returns a float64 slice argument by key, or an error if not found or not convertible to float64 slice
  349. func (r CallToolRequest) RequireFloatSlice(key string) ([]float64, error) {
  350. args := r.GetArguments()
  351. if val, ok := args[key]; ok {
  352. switch v := val.(type) {
  353. case []float64:
  354. return v, nil
  355. case []any:
  356. result := make([]float64, 0, len(v))
  357. for i, item := range v {
  358. switch num := item.(type) {
  359. case float64:
  360. result = append(result, num)
  361. case int:
  362. result = append(result, float64(num))
  363. case string:
  364. if f, err := strconv.ParseFloat(num, 64); err == nil {
  365. result = append(result, f)
  366. } else {
  367. return nil, fmt.Errorf("item %d in argument %q cannot be converted to float64", i, key)
  368. }
  369. default:
  370. return nil, fmt.Errorf("item %d in argument %q is not a float64", i, key)
  371. }
  372. }
  373. return result, nil
  374. default:
  375. return nil, fmt.Errorf("argument %q is not a float64 slice", key)
  376. }
  377. }
  378. return nil, fmt.Errorf("required argument %q not found", key)
  379. }
  380. // GetBoolSlice returns a bool slice argument by key, or the default value if not found
  381. func (r CallToolRequest) GetBoolSlice(key string, defaultValue []bool) []bool {
  382. args := r.GetArguments()
  383. if val, ok := args[key]; ok {
  384. switch v := val.(type) {
  385. case []bool:
  386. return v
  387. case []any:
  388. result := make([]bool, 0, len(v))
  389. for _, item := range v {
  390. switch b := item.(type) {
  391. case bool:
  392. result = append(result, b)
  393. case string:
  394. if parsed, err := strconv.ParseBool(b); err == nil {
  395. result = append(result, parsed)
  396. }
  397. case int:
  398. result = append(result, b != 0)
  399. case float64:
  400. result = append(result, b != 0)
  401. }
  402. }
  403. return result
  404. }
  405. }
  406. return defaultValue
  407. }
  408. // RequireBoolSlice returns a bool slice argument by key, or an error if not found or not convertible to bool slice
  409. func (r CallToolRequest) RequireBoolSlice(key string) ([]bool, error) {
  410. args := r.GetArguments()
  411. if val, ok := args[key]; ok {
  412. switch v := val.(type) {
  413. case []bool:
  414. return v, nil
  415. case []any:
  416. result := make([]bool, 0, len(v))
  417. for i, item := range v {
  418. switch b := item.(type) {
  419. case bool:
  420. result = append(result, b)
  421. case string:
  422. if parsed, err := strconv.ParseBool(b); err == nil {
  423. result = append(result, parsed)
  424. } else {
  425. return nil, fmt.Errorf("item %d in argument %q cannot be converted to bool", i, key)
  426. }
  427. case int:
  428. result = append(result, b != 0)
  429. case float64:
  430. result = append(result, b != 0)
  431. default:
  432. return nil, fmt.Errorf("item %d in argument %q is not a bool", i, key)
  433. }
  434. }
  435. return result, nil
  436. default:
  437. return nil, fmt.Errorf("argument %q is not a bool slice", key)
  438. }
  439. }
  440. return nil, fmt.Errorf("required argument %q not found", key)
  441. }
  442. // MarshalJSON implements custom JSON marshaling for CallToolResult
  443. func (r CallToolResult) MarshalJSON() ([]byte, error) {
  444. m := make(map[string]any)
  445. // Marshal Meta if present
  446. if r.Meta != nil {
  447. m["_meta"] = r.Meta
  448. }
  449. // Marshal Content array
  450. content := make([]any, len(r.Content))
  451. for i, c := range r.Content {
  452. content[i] = c
  453. }
  454. m["content"] = content
  455. // Marshal StructuredContent if present
  456. if r.StructuredContent != nil {
  457. m["structuredContent"] = r.StructuredContent
  458. }
  459. // Marshal IsError if true
  460. if r.IsError {
  461. m["isError"] = r.IsError
  462. }
  463. return json.Marshal(m)
  464. }
  465. // UnmarshalJSON implements custom JSON unmarshaling for CallToolResult
  466. func (r *CallToolResult) UnmarshalJSON(data []byte) error {
  467. var raw map[string]any
  468. if err := json.Unmarshal(data, &raw); err != nil {
  469. return err
  470. }
  471. // Unmarshal Meta
  472. if meta, ok := raw["_meta"]; ok {
  473. if metaMap, ok := meta.(map[string]any); ok {
  474. r.Meta = NewMetaFromMap(metaMap)
  475. }
  476. }
  477. // Unmarshal Content array
  478. if contentRaw, ok := raw["content"]; ok {
  479. if contentArray, ok := contentRaw.([]any); ok {
  480. r.Content = make([]Content, len(contentArray))
  481. for i, item := range contentArray {
  482. itemBytes, err := json.Marshal(item)
  483. if err != nil {
  484. return err
  485. }
  486. content, err := UnmarshalContent(itemBytes)
  487. if err != nil {
  488. return err
  489. }
  490. r.Content[i] = content
  491. }
  492. }
  493. }
  494. // Unmarshal StructuredContent if present
  495. if structured, ok := raw["structuredContent"]; ok {
  496. r.StructuredContent = structured
  497. }
  498. // Unmarshal IsError
  499. if isError, ok := raw["isError"]; ok {
  500. if isErrorBool, ok := isError.(bool); ok {
  501. r.IsError = isErrorBool
  502. }
  503. }
  504. return nil
  505. }
  506. // ToolListChangedNotification is an optional notification from the server to
  507. // the client, informing it that the list of tools it offers has changed. This may
  508. // be issued by servers without any previous subscription from the client.
  509. type ToolListChangedNotification struct {
  510. Notification
  511. }
  512. // Tool represents the definition for a tool the client can call.
  513. type Tool struct {
  514. // Meta is a metadata object that is reserved by MCP for storing additional information.
  515. Meta *Meta `json:"_meta,omitempty"`
  516. // The name of the tool.
  517. Name string `json:"name"`
  518. // A human-readable description of the tool.
  519. Description string `json:"description,omitempty"`
  520. // A JSON Schema object defining the expected parameters for the tool.
  521. InputSchema ToolInputSchema `json:"inputSchema"`
  522. // Alternative to InputSchema - allows arbitrary JSON Schema to be provided
  523. RawInputSchema json.RawMessage `json:"-"` // Hide this from JSON marshaling
  524. // A JSON Schema object defining the expected output returned by the tool .
  525. OutputSchema ToolOutputSchema `json:"outputSchema,omitempty"`
  526. // Optional JSON Schema defining expected output structure
  527. RawOutputSchema json.RawMessage `json:"-"` // Hide this from JSON marshaling
  528. // Optional properties describing tool behavior
  529. Annotations ToolAnnotation `json:"annotations"`
  530. }
  531. // GetName returns the name of the tool.
  532. func (t Tool) GetName() string {
  533. return t.Name
  534. }
  535. // MarshalJSON implements the json.Marshaler interface for Tool.
  536. // It handles marshaling either InputSchema or RawInputSchema based on which is set.
  537. func (t Tool) MarshalJSON() ([]byte, error) {
  538. // Create a map to build the JSON structure
  539. m := make(map[string]any, 5)
  540. // Add the name and description
  541. m["name"] = t.Name
  542. if t.Description != "" {
  543. m["description"] = t.Description
  544. }
  545. // Determine which input schema to use
  546. if t.RawInputSchema != nil {
  547. if t.InputSchema.Type != "" {
  548. return nil, fmt.Errorf("tool %s has both InputSchema and RawInputSchema set: %w", t.Name, errToolSchemaConflict)
  549. }
  550. m["inputSchema"] = t.RawInputSchema
  551. } else {
  552. // Use the structured InputSchema
  553. m["inputSchema"] = t.InputSchema
  554. }
  555. // Add output schema if present
  556. if t.RawOutputSchema != nil {
  557. if t.OutputSchema.Type != "" {
  558. return nil, fmt.Errorf("tool %s has both OutputSchema and RawOutputSchema set: %w", t.Name, errToolSchemaConflict)
  559. }
  560. m["outputSchema"] = t.RawOutputSchema
  561. } else if t.OutputSchema.Type != "" { // If no output schema is specified, do not return anything
  562. m["outputSchema"] = t.OutputSchema
  563. }
  564. m["annotations"] = t.Annotations
  565. return json.Marshal(m)
  566. }
  567. // ToolArgumentsSchema represents a JSON Schema for tool arguments.
  568. type ToolArgumentsSchema struct {
  569. Defs map[string]any `json:"$defs,omitempty"`
  570. Type string `json:"type"`
  571. Properties map[string]any `json:"properties,omitempty"`
  572. Required []string `json:"required,omitempty"`
  573. }
  574. type ToolInputSchema ToolArgumentsSchema // For retro-compatibility
  575. type ToolOutputSchema ToolArgumentsSchema
  576. // MarshalJSON implements the json.Marshaler interface for ToolInputSchema.
  577. func (tis ToolArgumentsSchema) MarshalJSON() ([]byte, error) {
  578. m := make(map[string]any)
  579. m["type"] = tis.Type
  580. if tis.Defs != nil {
  581. m["$defs"] = tis.Defs
  582. }
  583. // Marshal Properties to '{}' rather than `nil` when its length equals zero
  584. if tis.Properties != nil {
  585. m["properties"] = tis.Properties
  586. }
  587. if len(tis.Required) > 0 {
  588. m["required"] = tis.Required
  589. }
  590. return json.Marshal(m)
  591. }
  592. type ToolAnnotation struct {
  593. // Human-readable title for the tool
  594. Title string `json:"title,omitempty"`
  595. // If true, the tool does not modify its environment
  596. ReadOnlyHint *bool `json:"readOnlyHint,omitempty"`
  597. // If true, the tool may perform destructive updates
  598. DestructiveHint *bool `json:"destructiveHint,omitempty"`
  599. // If true, repeated calls with same args have no additional effect
  600. IdempotentHint *bool `json:"idempotentHint,omitempty"`
  601. // If true, tool interacts with external entities
  602. OpenWorldHint *bool `json:"openWorldHint,omitempty"`
  603. }
  604. // ToolOption is a function that configures a Tool.
  605. // It provides a flexible way to set various properties of a Tool using the functional options pattern.
  606. type ToolOption func(*Tool)
  607. // PropertyOption is a function that configures a property in a Tool's input schema.
  608. // It allows for flexible configuration of JSON Schema properties using the functional options pattern.
  609. type PropertyOption func(map[string]any)
  610. //
  611. // Core Tool Functions
  612. //
  613. // NewTool creates a new Tool with the given name and options.
  614. // The tool will have an object-type input schema with configurable properties.
  615. // Options are applied in order, allowing for flexible tool configuration.
  616. func NewTool(name string, opts ...ToolOption) Tool {
  617. tool := Tool{
  618. Name: name,
  619. InputSchema: ToolInputSchema{
  620. Type: "object",
  621. Properties: make(map[string]any),
  622. Required: nil, // Will be omitted from JSON if empty
  623. },
  624. Annotations: ToolAnnotation{
  625. Title: "",
  626. ReadOnlyHint: ToBoolPtr(false),
  627. DestructiveHint: ToBoolPtr(true),
  628. IdempotentHint: ToBoolPtr(false),
  629. OpenWorldHint: ToBoolPtr(true),
  630. },
  631. }
  632. for _, opt := range opts {
  633. opt(&tool)
  634. }
  635. return tool
  636. }
  637. // NewToolWithRawSchema creates a new Tool with the given name and a raw JSON
  638. // Schema. This allows for arbitrary JSON Schema to be used for the tool's input
  639. // schema.
  640. //
  641. // NOTE a [Tool] built in such a way is incompatible with the [ToolOption] and
  642. // runtime errors will result from supplying a [ToolOption] to a [Tool] built
  643. // with this function.
  644. func NewToolWithRawSchema(name, description string, schema json.RawMessage) Tool {
  645. tool := Tool{
  646. Name: name,
  647. Description: description,
  648. RawInputSchema: schema,
  649. }
  650. return tool
  651. }
  652. // WithDescription adds a description to the Tool.
  653. // The description should provide a clear, human-readable explanation of what the tool does.
  654. func WithDescription(description string) ToolOption {
  655. return func(t *Tool) {
  656. t.Description = description
  657. }
  658. }
  659. // WithInputSchema creates a ToolOption that sets the input schema for a tool.
  660. // It accepts any Go type, usually a struct, and automatically generates a JSON schema from it.
  661. func WithInputSchema[T any]() ToolOption {
  662. return func(t *Tool) {
  663. var zero T
  664. // Generate schema using invopop/jsonschema library
  665. // Configure reflector to generate clean, MCP-compatible schemas
  666. reflector := jsonschema.Reflector{
  667. DoNotReference: true, // Removes $defs map, outputs entire structure inline
  668. Anonymous: true, // Hides auto-generated Schema IDs
  669. AllowAdditionalProperties: true, // Removes additionalProperties: false
  670. }
  671. schema := reflector.Reflect(zero)
  672. // Clean up schema for MCP compliance
  673. schema.Version = "" // Remove $schema field
  674. // Convert to raw JSON for MCP
  675. mcpSchema, err := json.Marshal(schema)
  676. if err != nil {
  677. // Skip and maintain backward compatibility
  678. return
  679. }
  680. t.InputSchema.Type = ""
  681. t.RawInputSchema = json.RawMessage(mcpSchema)
  682. }
  683. }
  684. // WithRawInputSchema sets a raw JSON schema for the tool's input.
  685. // Use this when you need full control over the schema or when working with
  686. // complex schemas that can't be generated from Go types. The jsonschema library
  687. // can handle complex schemas and provides nice extension points, so be sure to
  688. // check that out before using this.
  689. func WithRawInputSchema(schema json.RawMessage) ToolOption {
  690. return func(t *Tool) {
  691. t.RawInputSchema = schema
  692. }
  693. }
  694. // WithOutputSchema creates a ToolOption that sets the output schema for a tool.
  695. // It accepts any Go type, usually a struct, and automatically generates a JSON schema from it.
  696. func WithOutputSchema[T any]() ToolOption {
  697. return func(t *Tool) {
  698. var zero T
  699. // Generate schema using invopop/jsonschema library
  700. // Configure reflector to generate clean, MCP-compatible schemas
  701. reflector := jsonschema.Reflector{
  702. DoNotReference: true, // Removes $defs map, outputs entire structure inline
  703. Anonymous: true, // Hides auto-generated Schema IDs
  704. AllowAdditionalProperties: true, // Removes additionalProperties: false
  705. }
  706. schema := reflector.Reflect(zero)
  707. // Clean up schema for MCP compliance
  708. schema.Version = "" // Remove $schema field
  709. // Convert to raw JSON for MCP
  710. mcpSchema, err := json.Marshal(schema)
  711. if err != nil {
  712. // Skip and maintain backward compatibility
  713. return
  714. }
  715. // Retrieve the schema from raw JSON
  716. if err := json.Unmarshal(mcpSchema, &t.OutputSchema); err != nil {
  717. // Skip and maintain backward compatibility
  718. return
  719. }
  720. // Always set the type to "object" as of the current MCP spec
  721. // https://modelcontextprotocol.io/specification/2025-06-18/server/tools#output-schema
  722. t.OutputSchema.Type = "object"
  723. }
  724. }
  725. // WithRawOutputSchema sets a raw JSON schema for the tool's output.
  726. // Use this when you need full control over the schema or when working with
  727. // complex schemas that can't be generated from Go types. The jsonschema library
  728. // can handle complex schemas and provides nice extension points, so be sure to
  729. // check that out before using this.
  730. func WithRawOutputSchema(schema json.RawMessage) ToolOption {
  731. return func(t *Tool) {
  732. t.RawOutputSchema = schema
  733. }
  734. }
  735. // WithToolAnnotation adds optional hints about the Tool.
  736. func WithToolAnnotation(annotation ToolAnnotation) ToolOption {
  737. return func(t *Tool) {
  738. t.Annotations = annotation
  739. }
  740. }
  741. // WithTitleAnnotation sets the Title field of the Tool's Annotations.
  742. // It provides a human-readable title for the tool.
  743. func WithTitleAnnotation(title string) ToolOption {
  744. return func(t *Tool) {
  745. t.Annotations.Title = title
  746. }
  747. }
  748. // WithReadOnlyHintAnnotation sets the ReadOnlyHint field of the Tool's Annotations.
  749. // If true, it indicates the tool does not modify its environment.
  750. func WithReadOnlyHintAnnotation(value bool) ToolOption {
  751. return func(t *Tool) {
  752. t.Annotations.ReadOnlyHint = &value
  753. }
  754. }
  755. // WithDestructiveHintAnnotation sets the DestructiveHint field of the Tool's Annotations.
  756. // If true, it indicates the tool may perform destructive updates.
  757. func WithDestructiveHintAnnotation(value bool) ToolOption {
  758. return func(t *Tool) {
  759. t.Annotations.DestructiveHint = &value
  760. }
  761. }
  762. // WithIdempotentHintAnnotation sets the IdempotentHint field of the Tool's Annotations.
  763. // If true, it indicates repeated calls with the same arguments have no additional effect.
  764. func WithIdempotentHintAnnotation(value bool) ToolOption {
  765. return func(t *Tool) {
  766. t.Annotations.IdempotentHint = &value
  767. }
  768. }
  769. // WithOpenWorldHintAnnotation sets the OpenWorldHint field of the Tool's Annotations.
  770. // If true, it indicates the tool interacts with external entities.
  771. func WithOpenWorldHintAnnotation(value bool) ToolOption {
  772. return func(t *Tool) {
  773. t.Annotations.OpenWorldHint = &value
  774. }
  775. }
  776. //
  777. // Common Property Options
  778. //
  779. // Description adds a description to a property in the JSON Schema.
  780. // The description should explain the purpose and expected values of the property.
  781. func Description(desc string) PropertyOption {
  782. return func(schema map[string]any) {
  783. schema["description"] = desc
  784. }
  785. }
  786. // Required marks a property as required in the tool's input schema.
  787. // Required properties must be provided when using the tool.
  788. func Required() PropertyOption {
  789. return func(schema map[string]any) {
  790. schema["required"] = true
  791. }
  792. }
  793. // Title adds a display-friendly title to a property in the JSON Schema.
  794. // This title can be used by UI components to show a more readable property name.
  795. func Title(title string) PropertyOption {
  796. return func(schema map[string]any) {
  797. schema["title"] = title
  798. }
  799. }
  800. //
  801. // String Property Options
  802. //
  803. // DefaultString sets the default value for a string property.
  804. // This value will be used if the property is not explicitly provided.
  805. func DefaultString(value string) PropertyOption {
  806. return func(schema map[string]any) {
  807. schema["default"] = value
  808. }
  809. }
  810. // Enum specifies a list of allowed values for a string property.
  811. // The property value must be one of the specified enum values.
  812. func Enum(values ...string) PropertyOption {
  813. return func(schema map[string]any) {
  814. schema["enum"] = values
  815. }
  816. }
  817. // MaxLength sets the maximum length for a string property.
  818. // The string value must not exceed this length.
  819. func MaxLength(max int) PropertyOption {
  820. return func(schema map[string]any) {
  821. schema["maxLength"] = max
  822. }
  823. }
  824. // MinLength sets the minimum length for a string property.
  825. // The string value must be at least this length.
  826. func MinLength(min int) PropertyOption {
  827. return func(schema map[string]any) {
  828. schema["minLength"] = min
  829. }
  830. }
  831. // Pattern sets a regex pattern that a string property must match.
  832. // The string value must conform to the specified regular expression.
  833. func Pattern(pattern string) PropertyOption {
  834. return func(schema map[string]any) {
  835. schema["pattern"] = pattern
  836. }
  837. }
  838. //
  839. // Number Property Options
  840. //
  841. // DefaultNumber sets the default value for a number property.
  842. // This value will be used if the property is not explicitly provided.
  843. func DefaultNumber(value float64) PropertyOption {
  844. return func(schema map[string]any) {
  845. schema["default"] = value
  846. }
  847. }
  848. // Max sets the maximum value for a number property.
  849. // The number value must not exceed this maximum.
  850. func Max(max float64) PropertyOption {
  851. return func(schema map[string]any) {
  852. schema["maximum"] = max
  853. }
  854. }
  855. // Min sets the minimum value for a number property.
  856. // The number value must not be less than this minimum.
  857. func Min(min float64) PropertyOption {
  858. return func(schema map[string]any) {
  859. schema["minimum"] = min
  860. }
  861. }
  862. // MultipleOf specifies that a number must be a multiple of the given value.
  863. // The number value must be divisible by this value.
  864. func MultipleOf(value float64) PropertyOption {
  865. return func(schema map[string]any) {
  866. schema["multipleOf"] = value
  867. }
  868. }
  869. //
  870. // Boolean Property Options
  871. //
  872. // DefaultBool sets the default value for a boolean property.
  873. // This value will be used if the property is not explicitly provided.
  874. func DefaultBool(value bool) PropertyOption {
  875. return func(schema map[string]any) {
  876. schema["default"] = value
  877. }
  878. }
  879. //
  880. // Array Property Options
  881. //
  882. // DefaultArray sets the default value for an array property.
  883. // This value will be used if the property is not explicitly provided.
  884. func DefaultArray[T any](value []T) PropertyOption {
  885. return func(schema map[string]any) {
  886. schema["default"] = value
  887. }
  888. }
  889. //
  890. // Property Type Helpers
  891. //
  892. // WithBoolean adds a boolean property to the tool schema.
  893. // It accepts property options to configure the boolean property's behavior and constraints.
  894. func WithBoolean(name string, opts ...PropertyOption) ToolOption {
  895. return func(t *Tool) {
  896. schema := map[string]any{
  897. "type": "boolean",
  898. }
  899. for _, opt := range opts {
  900. opt(schema)
  901. }
  902. // Remove required from property schema and add to InputSchema.required
  903. if required, ok := schema["required"].(bool); ok && required {
  904. delete(schema, "required")
  905. t.InputSchema.Required = append(t.InputSchema.Required, name)
  906. }
  907. t.InputSchema.Properties[name] = schema
  908. }
  909. }
  910. // WithNumber adds a number property to the tool schema.
  911. // It accepts property options to configure the number property's behavior and constraints.
  912. func WithNumber(name string, opts ...PropertyOption) ToolOption {
  913. return func(t *Tool) {
  914. schema := map[string]any{
  915. "type": "number",
  916. }
  917. for _, opt := range opts {
  918. opt(schema)
  919. }
  920. // Remove required from property schema and add to InputSchema.required
  921. if required, ok := schema["required"].(bool); ok && required {
  922. delete(schema, "required")
  923. t.InputSchema.Required = append(t.InputSchema.Required, name)
  924. }
  925. t.InputSchema.Properties[name] = schema
  926. }
  927. }
  928. // WithString adds a string property to the tool schema.
  929. // It accepts property options to configure the string property's behavior and constraints.
  930. func WithString(name string, opts ...PropertyOption) ToolOption {
  931. return func(t *Tool) {
  932. schema := map[string]any{
  933. "type": "string",
  934. }
  935. for _, opt := range opts {
  936. opt(schema)
  937. }
  938. // Remove required from property schema and add to InputSchema.required
  939. if required, ok := schema["required"].(bool); ok && required {
  940. delete(schema, "required")
  941. t.InputSchema.Required = append(t.InputSchema.Required, name)
  942. }
  943. t.InputSchema.Properties[name] = schema
  944. }
  945. }
  946. // WithObject adds an object property to the tool schema.
  947. // It accepts property options to configure the object property's behavior and constraints.
  948. func WithObject(name string, opts ...PropertyOption) ToolOption {
  949. return func(t *Tool) {
  950. schema := map[string]any{
  951. "type": "object",
  952. "properties": map[string]any{},
  953. }
  954. for _, opt := range opts {
  955. opt(schema)
  956. }
  957. // Remove required from property schema and add to InputSchema.required
  958. if required, ok := schema["required"].(bool); ok && required {
  959. delete(schema, "required")
  960. t.InputSchema.Required = append(t.InputSchema.Required, name)
  961. }
  962. t.InputSchema.Properties[name] = schema
  963. }
  964. }
  965. // WithArray adds an array property to the tool schema.
  966. // It accepts property options to configure the array property's behavior and constraints.
  967. func WithArray(name string, opts ...PropertyOption) ToolOption {
  968. return func(t *Tool) {
  969. schema := map[string]any{
  970. "type": "array",
  971. }
  972. for _, opt := range opts {
  973. opt(schema)
  974. }
  975. // Remove required from property schema and add to InputSchema.required
  976. if required, ok := schema["required"].(bool); ok && required {
  977. delete(schema, "required")
  978. t.InputSchema.Required = append(t.InputSchema.Required, name)
  979. }
  980. t.InputSchema.Properties[name] = schema
  981. }
  982. }
  983. // Properties defines the properties for an object schema
  984. func Properties(props map[string]any) PropertyOption {
  985. return func(schema map[string]any) {
  986. schema["properties"] = props
  987. }
  988. }
  989. // AdditionalProperties specifies whether additional properties are allowed in the object
  990. // or defines a schema for additional properties
  991. func AdditionalProperties(schema any) PropertyOption {
  992. return func(schemaMap map[string]any) {
  993. schemaMap["additionalProperties"] = schema
  994. }
  995. }
  996. // MinProperties sets the minimum number of properties for an object
  997. func MinProperties(min int) PropertyOption {
  998. return func(schema map[string]any) {
  999. schema["minProperties"] = min
  1000. }
  1001. }
  1002. // MaxProperties sets the maximum number of properties for an object
  1003. func MaxProperties(max int) PropertyOption {
  1004. return func(schema map[string]any) {
  1005. schema["maxProperties"] = max
  1006. }
  1007. }
  1008. // PropertyNames defines a schema for property names in an object
  1009. func PropertyNames(schema map[string]any) PropertyOption {
  1010. return func(schemaMap map[string]any) {
  1011. schemaMap["propertyNames"] = schema
  1012. }
  1013. }
  1014. // Items defines the schema for array items.
  1015. // Accepts any schema definition for maximum flexibility.
  1016. //
  1017. // Example:
  1018. //
  1019. // Items(map[string]any{
  1020. // "type": "object",
  1021. // "properties": map[string]any{
  1022. // "name": map[string]any{"type": "string"},
  1023. // "age": map[string]any{"type": "number"},
  1024. // },
  1025. // })
  1026. //
  1027. // For simple types, use ItemsString(), ItemsNumber(), ItemsBoolean() instead.
  1028. func Items(schema any) PropertyOption {
  1029. return func(schemaMap map[string]any) {
  1030. schemaMap["items"] = schema
  1031. }
  1032. }
  1033. // MinItems sets the minimum number of items for an array
  1034. func MinItems(min int) PropertyOption {
  1035. return func(schema map[string]any) {
  1036. schema["minItems"] = min
  1037. }
  1038. }
  1039. // MaxItems sets the maximum number of items for an array
  1040. func MaxItems(max int) PropertyOption {
  1041. return func(schema map[string]any) {
  1042. schema["maxItems"] = max
  1043. }
  1044. }
  1045. // UniqueItems specifies whether array items must be unique
  1046. func UniqueItems(unique bool) PropertyOption {
  1047. return func(schema map[string]any) {
  1048. schema["uniqueItems"] = unique
  1049. }
  1050. }
  1051. // WithStringItems configures an array's items to be of type string.
  1052. //
  1053. // Supported options: Description(), DefaultString(), Enum(), MaxLength(), MinLength(), Pattern()
  1054. // Note: Options like Required() are not valid for item schemas and will be ignored.
  1055. //
  1056. // Examples:
  1057. //
  1058. // mcp.WithArray("tags", mcp.WithStringItems())
  1059. // mcp.WithArray("colors", mcp.WithStringItems(mcp.Enum("red", "green", "blue")))
  1060. // mcp.WithArray("names", mcp.WithStringItems(mcp.MinLength(1), mcp.MaxLength(50)))
  1061. //
  1062. // Limitations: Only supports simple string arrays. Use Items() for complex objects.
  1063. func WithStringItems(opts ...PropertyOption) PropertyOption {
  1064. return func(schema map[string]any) {
  1065. itemSchema := map[string]any{
  1066. "type": "string",
  1067. }
  1068. for _, opt := range opts {
  1069. opt(itemSchema)
  1070. }
  1071. schema["items"] = itemSchema
  1072. }
  1073. }
  1074. // WithStringEnumItems configures an array's items to be of type string with a specified enum.
  1075. // Example:
  1076. //
  1077. // mcp.WithArray("priority", mcp.WithStringEnumItems([]string{"low", "medium", "high"}))
  1078. //
  1079. // Limitations: Only supports string enums. Use WithStringItems(Enum(...)) for more flexibility.
  1080. func WithStringEnumItems(values []string) PropertyOption {
  1081. return func(schema map[string]any) {
  1082. schema["items"] = map[string]any{
  1083. "type": "string",
  1084. "enum": values,
  1085. }
  1086. }
  1087. }
  1088. // WithNumberItems configures an array's items to be of type number.
  1089. //
  1090. // Supported options: Description(), DefaultNumber(), Min(), Max(), MultipleOf()
  1091. // Note: Options like Required() are not valid for item schemas and will be ignored.
  1092. //
  1093. // Examples:
  1094. //
  1095. // mcp.WithArray("scores", mcp.WithNumberItems(mcp.Min(0), mcp.Max(100)))
  1096. // mcp.WithArray("prices", mcp.WithNumberItems(mcp.Min(0)))
  1097. //
  1098. // Limitations: Only supports simple number arrays. Use Items() for complex objects.
  1099. func WithNumberItems(opts ...PropertyOption) PropertyOption {
  1100. return func(schema map[string]any) {
  1101. itemSchema := map[string]any{
  1102. "type": "number",
  1103. }
  1104. for _, opt := range opts {
  1105. opt(itemSchema)
  1106. }
  1107. schema["items"] = itemSchema
  1108. }
  1109. }
  1110. // WithBooleanItems configures an array's items to be of type boolean.
  1111. //
  1112. // Supported options: Description(), DefaultBool()
  1113. // Note: Options like Required() are not valid for item schemas and will be ignored.
  1114. //
  1115. // Examples:
  1116. //
  1117. // mcp.WithArray("flags", mcp.WithBooleanItems())
  1118. // mcp.WithArray("permissions", mcp.WithBooleanItems(mcp.Description("User permissions")))
  1119. //
  1120. // Limitations: Only supports simple boolean arrays. Use Items() for complex objects.
  1121. func WithBooleanItems(opts ...PropertyOption) PropertyOption {
  1122. return func(schema map[string]any) {
  1123. itemSchema := map[string]any{
  1124. "type": "boolean",
  1125. }
  1126. for _, opt := range opts {
  1127. opt(itemSchema)
  1128. }
  1129. schema["items"] = itemSchema
  1130. }
  1131. }