1
0

introspect.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. package tools
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "github.com/vektah/gqlparser/v2/ast"
  7. )
  8. // CallToolResult represents the result of a tool call
  9. type CallToolResult struct {
  10. Content []ContentBlock `json:"content"`
  11. IsError bool `json:"isError,omitempty"`
  12. }
  13. // ContentBlock represents a content block in a tool result
  14. type ContentBlock struct {
  15. Type string `json:"type"`
  16. Text string `json:"text"`
  17. }
  18. // Introspect returns GraphQL schema information
  19. func Introspect(ctx context.Context, schema *ast.Schema, args map[string]interface{}) (CallToolResult, error) {
  20. typeName, _ := args["typeName"].(string)
  21. var result strings.Builder
  22. if typeName != "" {
  23. // Introspect specific type
  24. result.WriteString(introspectType(schema, typeName))
  25. } else {
  26. // Full schema overview
  27. result.WriteString("# ARP GraphQL Schema Overview\n\n")
  28. // Query type
  29. result.WriteString("## Query Type\n")
  30. result.WriteString(introspectType(schema, "Query"))
  31. result.WriteString("\n")
  32. // Mutation type
  33. result.WriteString("## Mutation Type\n")
  34. result.WriteString(introspectType(schema, "Mutation"))
  35. result.WriteString("\n")
  36. // Subscription type
  37. result.WriteString("## Subscription Type\n")
  38. result.WriteString(introspectType(schema, "Subscription"))
  39. result.WriteString("\n")
  40. // Object types
  41. result.WriteString("## Object Types\n")
  42. for _, t := range schema.Types {
  43. if t.Kind == ast.Object && !strings.HasPrefix(t.Name, "__") && t.Name != "Query" && t.Name != "Mutation" && t.Name != "Subscription" {
  44. result.WriteString(fmt.Sprintf("### %s\n", t.Name))
  45. result.WriteString(introspectType(schema, t.Name))
  46. result.WriteString("\n")
  47. }
  48. }
  49. // Input types
  50. result.WriteString("## Input Types\n")
  51. for _, t := range schema.Types {
  52. if t.Kind == ast.InputObject && !strings.HasPrefix(t.Name, "__") {
  53. result.WriteString(fmt.Sprintf("### %s\n", t.Name))
  54. result.WriteString(introspectInputType(t))
  55. result.WriteString("\n")
  56. }
  57. }
  58. }
  59. return CallToolResult{
  60. Content: []ContentBlock{
  61. {Type: "text", Text: result.String()},
  62. },
  63. }, nil
  64. }
  65. func introspectType(schema *ast.Schema, typeName string) string {
  66. t := schema.Types[typeName]
  67. if t == nil {
  68. return fmt.Sprintf("Type '%s' not found\n", typeName)
  69. }
  70. var result strings.Builder
  71. switch t.Kind {
  72. case ast.Object:
  73. result.WriteString(fmt.Sprintf("Type: %s (Object)\n", t.Name))
  74. if t.Description != "" {
  75. result.WriteString(fmt.Sprintf("Description: %s\n", t.Description))
  76. }
  77. result.WriteString("\nFields:\n")
  78. for _, field := range t.Fields {
  79. result.WriteString(formatField(field))
  80. }
  81. case ast.Interface:
  82. result.WriteString(fmt.Sprintf("Type: %s (Interface)\n", t.Name))
  83. if t.Description != "" {
  84. result.WriteString(fmt.Sprintf("Description: %s\n", t.Description))
  85. }
  86. result.WriteString("\nFields:\n")
  87. for _, field := range t.Fields {
  88. result.WriteString(formatField(field))
  89. }
  90. case ast.Union:
  91. result.WriteString(fmt.Sprintf("Type: %s (Union)\n", t.Name))
  92. result.WriteString("Members:\n")
  93. for _, member := range t.Types {
  94. result.WriteString(fmt.Sprintf(" - %s\n", member))
  95. }
  96. case ast.Enum:
  97. result.WriteString(fmt.Sprintf("Type: %s (Enum)\n", t.Name))
  98. result.WriteString("Values:\n")
  99. for _, val := range t.EnumValues {
  100. result.WriteString(fmt.Sprintf(" - %s\n", val.Name))
  101. }
  102. default:
  103. result.WriteString(fmt.Sprintf("Type: %s (%s)\n", t.Name, t.Kind))
  104. }
  105. return result.String()
  106. }
  107. func introspectInputType(t *ast.Definition) string {
  108. var result strings.Builder
  109. result.WriteString(fmt.Sprintf("Type: %s (Input)\n", t.Name))
  110. if t.Description != "" {
  111. result.WriteString(fmt.Sprintf("Description: %s\n", t.Description))
  112. }
  113. result.WriteString("\nFields:\n")
  114. for _, field := range t.Fields {
  115. result.WriteString(formatInputField(field))
  116. }
  117. return result.String()
  118. }
  119. func formatField(field *ast.FieldDefinition) string {
  120. var result strings.Builder
  121. // Field name and type
  122. result.WriteString(fmt.Sprintf(" %s", field.Name))
  123. if len(field.Arguments) > 0 {
  124. result.WriteString("(")
  125. for i, arg := range field.Arguments {
  126. if i > 0 {
  127. result.WriteString(", ")
  128. }
  129. result.WriteString(fmt.Sprintf("%s: %s", arg.Name, formatType(arg.Type)))
  130. }
  131. result.WriteString(")")
  132. }
  133. result.WriteString(fmt.Sprintf(": %s", formatType(field.Type)))
  134. // Description
  135. if field.Description != "" {
  136. result.WriteString(fmt.Sprintf(" - %s", field.Description))
  137. }
  138. // Deprecation
  139. if field.Directives != nil {
  140. for _, d := range field.Directives {
  141. if d.Name == "deprecated" {
  142. result.WriteString(" [DEPRECATED]")
  143. if reason := d.Arguments.ForName("reason"); reason != nil {
  144. result.WriteString(fmt.Sprintf(" - %s", reason.Value.Raw))
  145. }
  146. }
  147. }
  148. }
  149. result.WriteString("\n")
  150. return result.String()
  151. }
  152. func formatInputField(field *ast.FieldDefinition) string {
  153. var result strings.Builder
  154. result.WriteString(fmt.Sprintf(" %s: %s", field.Name, formatType(field.Type)))
  155. if field.Description != "" {
  156. result.WriteString(fmt.Sprintf(" - %s", field.Description))
  157. }
  158. result.WriteString("\n")
  159. return result.String()
  160. }
  161. func formatType(t *ast.Type) string {
  162. if t == nil {
  163. return "Unknown"
  164. }
  165. base := t.Name()
  166. if t.NamedType != "" {
  167. base = t.NamedType
  168. }
  169. if t.Elem != nil {
  170. return fmt.Sprintf("[%s]", formatType(t.Elem))
  171. }
  172. if t.NonNull {
  173. return base + "!"
  174. }
  175. return base
  176. }