@@ -7,10 +7,134 @@ import (
77 "regexp"
88 "strings"
99
10+ "github.com/getkin/kin-openapi/openapi3"
1011 "github.com/jedisct1/openapi-mcp/pkg/openapi2mcp"
1112 "gopkg.in/yaml.v3"
1213)
1314
15+ // collectUsedSchemas traverses the OpenAPI document and collects all schema names that are referenced
16+ func collectUsedSchemas (doc * openapi3.T ) map [string ]bool {
17+ used := make (map [string ]bool )
18+
19+ // Helper function to extract schema name from $ref
20+ extractSchemaName := func (ref string ) string {
21+ if strings .HasPrefix (ref , "#/components/schemas/" ) {
22+ return strings .TrimPrefix (ref , "#/components/schemas/" )
23+ }
24+ return ""
25+ }
26+
27+ // Helper function to recursively collect refs from a schema
28+ var collectRefsFromSchema func (* openapi3.SchemaRef )
29+ collectRefsFromSchema = func (schemaRef * openapi3.SchemaRef ) {
30+ if schemaRef == nil {
31+ return
32+ }
33+
34+ // Check if this is a reference
35+ if schemaRef .Ref != "" {
36+ if name := extractSchemaName (schemaRef .Ref ); name != "" {
37+ if ! used [name ] {
38+ used [name ] = true
39+ // Recursively check the referenced schema
40+ if doc .Components != nil && doc .Components .Schemas != nil {
41+ if refSchema , exists := doc .Components .Schemas [name ]; exists {
42+ collectRefsFromSchema (refSchema )
43+ }
44+ }
45+ }
46+ }
47+ return
48+ }
49+
50+ // Check the schema value itself
51+ if schemaRef .Value != nil {
52+ schema := schemaRef .Value
53+
54+ // Check properties
55+ for _ , propRef := range schema .Properties {
56+ collectRefsFromSchema (propRef )
57+ }
58+
59+ // Check items (for arrays)
60+ if schema .Items != nil {
61+ collectRefsFromSchema (schema .Items )
62+ }
63+
64+ // Check additionalProperties
65+ if schema .AdditionalProperties .Schema != nil {
66+ collectRefsFromSchema (schema .AdditionalProperties .Schema )
67+ }
68+
69+ // Check allOf, anyOf, oneOf
70+ for _ , ref := range schema .AllOf {
71+ collectRefsFromSchema (ref )
72+ }
73+ for _ , ref := range schema .AnyOf {
74+ collectRefsFromSchema (ref )
75+ }
76+ for _ , ref := range schema .OneOf {
77+ collectRefsFromSchema (ref )
78+ }
79+
80+ // Check not
81+ if schema .Not != nil {
82+ collectRefsFromSchema (schema .Not )
83+ }
84+ }
85+ }
86+
87+ // Traverse all paths and operations
88+ if doc .Paths != nil {
89+ for _ , pathItem := range doc .Paths .Map () {
90+ // Check parameters at path level
91+ for _ , paramRef := range pathItem .Parameters {
92+ if paramRef != nil && paramRef .Value != nil && paramRef .Value .Schema != nil {
93+ collectRefsFromSchema (paramRef .Value .Schema )
94+ }
95+ }
96+
97+ // Check each operation
98+ for _ , op := range pathItem .Operations () {
99+ if op == nil {
100+ continue
101+ }
102+
103+ // Check parameters
104+ for _ , paramRef := range op .Parameters {
105+ if paramRef != nil && paramRef .Value != nil && paramRef .Value .Schema != nil {
106+ collectRefsFromSchema (paramRef .Value .Schema )
107+ }
108+ }
109+
110+ // Check request body
111+ if op .RequestBody != nil && op .RequestBody .Value != nil {
112+ for _ , mediaType := range op .RequestBody .Value .Content {
113+ if mediaType .Schema != nil {
114+ collectRefsFromSchema (mediaType .Schema )
115+ }
116+ }
117+ }
118+
119+ // Check responses
120+ if op .Responses != nil {
121+ for _ , respRef := range op .Responses .Map () {
122+ if respRef != nil && respRef .Value != nil {
123+ for _ , mediaType := range respRef .Value .Content {
124+ if mediaType .Schema != nil {
125+ collectRefsFromSchema (mediaType .Schema )
126+ }
127+ }
128+ }
129+ }
130+ }
131+ }
132+ }
133+ }
134+
135+ return used
136+ }
137+
14138// main is the entrypoint for the openapi-mcp CLI.
15139// It parses flags, loads the OpenAPI spec, and dispatches to the appropriate mode (server, doc, dry-run, etc).
16140func main () {
@@ -264,6 +388,17 @@ func main() {
264388 }
265389 }
266390
391+ // Clean up unused components/schemas
392+ if doc .Components != nil && doc .Components .Schemas != nil {
393+ usedSchemas := collectUsedSchemas (doc )
394+ // Remove unused schemas
395+ for schemaName := range doc .Components .Schemas {
396+ if _ , used := usedSchemas [schemaName ]; ! used {
397+ delete (doc .Components .Schemas , schemaName )
398+ }
399+ }
400+ }
401+
267402 // Output the filtered OpenAPI spec as a valid OpenAPI file using kin-openapi's marshaling
268403 ext := ""
269404 if dot := len (specPath ) - 1 - len (specPath ); dot >= 0 {
0 commit comments