@@ -96,114 +96,148 @@ func run() error {
96
96
func publish (examples []example ) error {
97
97
published := 0
98
98
for _ , example := range examples {
99
- log .Printf ("Publishing example starting on line %d" , example .line )
100
-
101
- expected := map [string ]any {}
102
- if err := json .Unmarshal (example .content , & expected ); err != nil {
103
- log .Println (" ⛔ Example isn't valid JSON:" , err )
99
+ if err := publishExample (example ); err != nil {
100
+ log .Printf (" ⛔ %v" , err )
104
101
continue
105
102
}
103
+ published ++
104
+ }
106
105
107
- // Handle both old ServerDetail format and new PublishRequest format
108
- var serverData map [string ]any
109
- if server , exists := expected ["server" ]; exists {
110
- // New PublishRequest format
111
- serverData = server .(map [string ]any )
112
- } else {
113
- // Old ServerDetail format (backward compatibility)
114
- serverData = expected
115
- }
106
+ msg := fmt .Sprintf ("published %d/%d examples" , published , len (examples ))
107
+ if published != len (examples ) {
108
+ return errors .New (msg )
109
+ }
110
+ log .Println (msg )
111
+ return nil
112
+ }
116
113
117
- // Remove any existing namespace prefix and add anonymous prefix
118
- if ! strings .HasPrefix (serverData ["name" ].(string ), "io.modelcontextprotocol.anonymous/" ) {
119
- parts := strings .SplitN (serverData ["name" ].(string ), "/" , 2 )
120
- serverName := parts [len (parts )- 1 ]
121
- serverData ["name" ] = "io.modelcontextprotocol.anonymous/" + serverName
122
- }
114
+ func publishExample (example example ) error {
115
+ log .Printf ("Publishing example starting on line %d" , example .line )
123
116
124
- // Update the expected structure if it's PublishRequest format
125
- if _ , exists := expected ["server" ]; exists {
126
- expected ["server" ] = serverData
127
- }
128
- example .content , _ = json .Marshal (expected )
117
+ expected , err := parseExample (example )
118
+ if err != nil {
119
+ return err
120
+ }
129
121
130
- p := filepath .Join ("bin" , fmt .Sprintf ("example-line-%d.json" , example .line ))
131
- if err := os .WriteFile (p , example .content , 0600 ); err != nil {
132
- log .Printf (" ⛔ Failed to write example JSON to %s: %v\n " , p , err )
133
- continue
134
- }
135
- defer os .Remove (p )
122
+ if err := publishToRegistry (expected , example .line ); err != nil {
123
+ return err
124
+ }
136
125
137
- ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
138
- defer cancel ()
139
- cmd := exec .CommandContext (ctx , "./bin/publisher" , "publish" , "--mcp-file" , p , "--registry-url" , registryURL , "--auth-method" , "none" )
140
- cmd .WaitDelay = 100 * time .Millisecond
126
+ log .Print (" ✅ registry response matches example\n \n " )
127
+ return nil
128
+ }
141
129
142
- out , err := cmd .CombinedOutput ()
143
- if errors .Is (err , exec .ErrNotFound ) || errors .Is (err , os .ErrNotExist ) {
144
- return errors .New (" ⛔ publisher not found; did you run tests/integration/run.sh?" )
145
- }
146
- output := strings .TrimSpace ("publisher output:\n \t " + strings .ReplaceAll (string (out ), "\n " , "\n \t " ))
147
- if err != nil {
148
- return errors .New (" ⛔ " + output )
149
- }
150
- log .Println (" ✅" , output )
130
+ func parseExample (example example ) (map [string ]any , error ) {
131
+ expected := map [string ]any {}
132
+ if err := json .Unmarshal (example .content , & expected ); err != nil {
133
+ return nil , fmt .Errorf ("example isn't valid JSON: %w" , err )
134
+ }
151
135
152
- m := publishedIDRegex .FindStringSubmatch (output )
153
- if len (m ) != 2 || m [1 ] == "" {
154
- return errors .New (" ⛔ Didn't find ID in publisher output" )
155
- }
156
- id := m [1 ]
136
+ // Handle both old ServerDetail format and new PublishRequest format
137
+ var serverData map [string ]any
138
+ if server , exists := expected ["server" ]; exists {
139
+ // New PublishRequest format
140
+ serverData = server .(map [string ]any )
141
+ } else {
142
+ // Old ServerDetail format (backward compatibility)
143
+ serverData = expected
144
+ }
157
145
158
- ctx , cancel = context .WithTimeout (context .Background (), 5 * time .Second )
159
- defer cancel ()
160
- req , err := http .NewRequestWithContext (ctx , http .MethodGet , registryURL + "/v0/servers/" + id , nil )
161
- if err != nil {
162
- return fmt .Errorf (" ⛔ %w" , err )
163
- }
164
- res , err := http .DefaultClient .Do (req )
165
- if err != nil {
166
- return fmt .Errorf (" ⛔ %w" , err )
167
- }
168
- content , err := io .ReadAll (res .Body )
169
- if err != nil {
170
- return fmt .Errorf (" ⛔ failed to read registry response: %w" , err )
171
- }
172
- defer res .Body .Close ()
173
- if res .StatusCode != http .StatusOK {
174
- return fmt .Errorf (" ⛔ registry responded %d: %s" , res .StatusCode , string (content ))
175
- }
146
+ // Remove any existing namespace prefix and add anonymous prefix
147
+ if ! strings .HasPrefix (serverData ["name" ].(string ), "io.modelcontextprotocol.anonymous/" ) {
148
+ parts := strings .SplitN (serverData ["name" ].(string ), "/" , 2 )
149
+ serverName := parts [len (parts )- 1 ]
150
+ serverData ["name" ] = "io.modelcontextprotocol.anonymous/" + serverName
151
+ }
176
152
177
- actual := map [string ]any {}
178
- if err := json .Unmarshal (content , & actual ); err != nil {
179
- return fmt .Errorf (" ⛔ failed to unmarshal registry response: %w" , err )
180
- }
181
-
182
- // Both API response and expected are now in extension wrapper format
183
- // Compare the server portions of both
184
- actualServer , ok := actual ["server" ]
185
- if ! ok {
186
- return fmt .Errorf (" ⛔ expected server field in registry response" )
187
- }
188
-
189
- // Extract expected server portion for comparison
190
- expectedServer := expected
191
- if server , exists := expected ["server" ]; exists {
192
- expectedServer = server .(map [string ]any )
193
- }
194
-
195
- if err := compare (expectedServer , actualServer ); err != nil {
196
- return fmt .Errorf (` ⛔ example "%s": %w` , expectedServer ["name" ], err )
197
- }
198
- log .Print (" ✅ registry response matches example\n \n " )
199
- published ++
153
+ // Update the expected structure if it's PublishRequest format
154
+ if _ , exists := expected ["server" ]; exists {
155
+ expected ["server" ] = serverData
200
156
}
201
157
202
- msg := fmt .Sprintf ("published %d/%d examples" , published , len (examples ))
203
- if published != len (examples ) {
204
- return errors .New (msg )
158
+ return expected , nil
159
+ }
160
+
161
+ func publishToRegistry (expected map [string ]any , line int ) error {
162
+ content , _ := json .Marshal (expected )
163
+ p := filepath .Join ("bin" , fmt .Sprintf ("example-line-%d.json" , line ))
164
+ if err := os .WriteFile (p , content , 0600 ); err != nil {
165
+ return fmt .Errorf ("failed to write example JSON to %s: %w" , p , err )
166
+ }
167
+ defer os .Remove (p )
168
+
169
+ id , err := runPublisher (p )
170
+ if err != nil {
171
+ return err
172
+ }
173
+
174
+ return verifyPublishedServer (id , expected )
175
+ }
176
+
177
+ func runPublisher (filePath string ) (string , error ) {
178
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
179
+ defer cancel ()
180
+ cmd := exec .CommandContext (ctx , "./bin/publisher" , "publish" , "--mcp-file" , filePath , "--registry-url" , registryURL , "--auth-method" , "none" )
181
+ cmd .WaitDelay = 100 * time .Millisecond
182
+
183
+ out , err := cmd .CombinedOutput ()
184
+ if errors .Is (err , exec .ErrNotFound ) || errors .Is (err , os .ErrNotExist ) {
185
+ return "" , errors .New ("publisher not found; did you run tests/integration/run.sh?" )
186
+ }
187
+ output := strings .TrimSpace ("publisher output:\n \t " + strings .ReplaceAll (string (out ), "\n " , "\n \t " ))
188
+ if err != nil {
189
+ return "" , errors .New (output )
190
+ }
191
+ log .Println (" ✅" , output )
192
+
193
+ m := publishedIDRegex .FindStringSubmatch (output )
194
+ if len (m ) != 2 || m [1 ] == "" {
195
+ return "" , errors .New ("didn't find ID in publisher output" )
196
+ }
197
+ return m [1 ], nil
198
+ }
199
+
200
+ func verifyPublishedServer (id string , expected map [string ]any ) error {
201
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
202
+ defer cancel ()
203
+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , registryURL + "/v0/servers/" + id , nil )
204
+ if err != nil {
205
+ return err
206
+ }
207
+ res , err := http .DefaultClient .Do (req )
208
+ if err != nil {
209
+ return err
210
+ }
211
+ content , err := io .ReadAll (res .Body )
212
+ if err != nil {
213
+ return fmt .Errorf ("failed to read registry response: %w" , err )
214
+ }
215
+ defer res .Body .Close ()
216
+ if res .StatusCode != http .StatusOK {
217
+ return fmt .Errorf ("registry responded %d: %s" , res .StatusCode , string (content ))
218
+ }
219
+
220
+ actual := map [string ]any {}
221
+ if err := json .Unmarshal (content , & actual ); err != nil {
222
+ return fmt .Errorf ("failed to unmarshal registry response: %w" , err )
223
+ }
224
+
225
+ // Both API response and expected are now in extension wrapper format
226
+ // Compare the server portions of both
227
+ actualServer , ok := actual ["server" ]
228
+ if ! ok {
229
+ return fmt .Errorf ("expected server field in registry response" )
230
+ }
231
+
232
+ // Extract expected server portion for comparison
233
+ expectedServer := expected
234
+ if server , exists := expected ["server" ]; exists {
235
+ expectedServer = server .(map [string ]any )
236
+ }
237
+
238
+ if err := compare (expectedServer , actualServer ); err != nil {
239
+ return fmt .Errorf (`example "%s": %w` , expectedServer ["name" ], err )
205
240
}
206
- log .Println (msg )
207
241
return nil
208
242
}
209
243
0 commit comments