@@ -20,77 +20,55 @@ import (
2020
2121const registryURL = "http://localhost:8080"
2222
23- var publishedIDRegex = regexp .MustCompile (`"x-io\.modelcontextprotocol\.registry":\s*\{[^}]*"id":\s*"([^"]+)"` )
24-
2523func main () {
2624 log .SetFlags (0 )
2725 if err := run (); err != nil {
2826 log .Fatal (err )
2927 }
3028}
3129
32- func getAnonymousToken () (string , error ) {
33- ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
34- defer cancel ()
35-
36- req , err := http .NewRequestWithContext (ctx , http .MethodPost , registryURL + "/v0/auth/none" , nil )
30+ func run () error {
31+ examplesPath := filepath .Join ("docs" , "server-json" , "examples.md" )
32+ examples , err := getExamples (examplesPath )
3733 if err != nil {
38- return "" , fmt . Errorf ("failed to create request : %w " , err )
34+ log . Fatalf ("failed to extract examples : %v " , err )
3935 }
40- req . Header . Set ( "Content-Type " , "application/json" )
36+ log . Printf ( "Found %d examples in %q \n " , len ( examples ), examplesPath )
4137
42- resp , err := http .DefaultClient .Do (req )
38+ // Set up authentication using the new login workflow
39+ err = setupPublisherAuth ()
4340 if err != nil {
44- return "" , fmt .Errorf ("failed to get anonymous token: %w" , err )
45- }
46- defer resp .Body .Close ()
47-
48- if resp .StatusCode != http .StatusOK {
49- body , _ := io .ReadAll (resp .Body )
50- return "" , fmt .Errorf ("auth endpoint returned %d: %s" , resp .StatusCode , string (body ))
51- }
52-
53- var tokenResponse struct {
54- RegistryToken string `json:"registry_token"`
55- ExpiresAt int `json:"expires_at"`
41+ log .Fatalf ("failed to set up publisher auth: %v" , err )
5642 }
43+ defer cleanupPublisherAuth ()
5744
58- if err := json .NewDecoder (resp .Body ).Decode (& tokenResponse ); err != nil {
59- return "" , fmt .Errorf ("failed to decode token response: %w" , err )
60- }
45+ return publish (examples )
46+ }
6147
62- if tokenResponse . RegistryToken == "" {
63- return "" , fmt . Errorf ( "received empty token from auth endpoint" )
64- }
48+ func setupPublisherAuth () error {
49+ ctx , cancel := context . WithTimeout ( context . Background (), 5 * time . Second )
50+ defer cancel ()
6551
66- log .Printf ("Got anonymous token (expires at %d)" , tokenResponse .ExpiresAt )
67- return tokenResponse .RegistryToken , nil
68- }
52+ cmd := exec .CommandContext (ctx , "./bin/publisher" , "login" , "none" , "--registry" , registryURL )
53+ cmd .WaitDelay = 100 * time .Millisecond
6954
70- func run () error {
71- examplesPath := filepath .Join ("docs" , "server-json" , "examples.md" )
72- examples , err := examples (examplesPath )
55+ out , err := cmd .CombinedOutput ()
7356 if err != nil {
74- if errors .Is (err , os .ErrNotExist ) {
75- log .Fatalf ("%q not found; run this test from the repo root" , examplesPath )
76- }
77- log .Fatalf ("failed to extract examples from %q: %v" , examplesPath , err )
57+ return fmt .Errorf ("login failed: %s" , string (out ))
7858 }
7959
80- log .Printf ("Found %d examples in %q\n " , len (examples ), examplesPath )
60+ log .Printf ("Publisher login successful: %s" , strings .TrimSpace (string (out )))
61+ return nil
62+ }
8163
82- // Get anonymous token from the none endpoint
83- token , err := getAnonymousToken ()
64+ func cleanupPublisherAuth () {
65+ // Clean up the token file
66+ homeDir , err := os .UserHomeDir ()
8467 if err != nil {
85- log . Fatalf ( "failed to get anonymous token: %v" , err )
68+ return
8669 }
87-
88- if err := os .WriteFile (".mcpregistry_registry_token" , []byte (token ), 0600 ); err != nil {
89- log .Fatalf ("failed to write token: %v" , err )
90- }
91- defer os .Remove (".mcpregistry_registry_token" )
92-
93- return publish (examples )
70+ tokenPath := filepath .Join (homeDir , ".mcp_publisher_token" )
71+ os .Remove (tokenPath )
9472}
9573
9674func publish (examples []example ) error {
@@ -177,24 +155,95 @@ func publishToRegistry(expected map[string]any, line int) error {
177155func runPublisher (filePath string ) (string , error ) {
178156 ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
179157 defer cancel ()
180- cmd := exec .CommandContext (ctx , "./bin/publisher" , "publish" , "--mcp-file" , filePath , "--registry-url" , registryURL , "--auth-method" , "none" )
158+
159+ cmd := exec .CommandContext (ctx , "./bin/publisher" , "publish" , filePath )
181160 cmd .WaitDelay = 100 * time .Millisecond
182161
183162 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- }
187163 output := strings .TrimSpace ("publisher output:\n \t " + strings .ReplaceAll (string (out ), "\n " , "\n \t " ))
188164 if err != nil {
189165 return "" , errors .New (output )
190166 }
191167 log .Println (" ✅" , output )
192168
193- m := publishedIDRegex .FindStringSubmatch (output )
194- if len (m ) != 2 || m [1 ] == "" {
195- return "" , errors .New ("didn't find ID in publisher output" )
169+ // Get the server name from the file to look up the ID
170+ serverName , err := getServerNameFromFile (filePath )
171+ if err != nil {
172+ return "" , fmt .Errorf ("failed to get server name from file: %w" , err )
173+ }
174+
175+ // Find the server in the registry by name
176+ return findServerIDByName (serverName )
177+ }
178+
179+ func getServerNameFromFile (filePath string ) (string , error ) {
180+ data , err := os .ReadFile (filePath )
181+ if err != nil {
182+ return "" , err
183+ }
184+
185+ var serverData map [string ]any
186+ if err := json .Unmarshal (data , & serverData ); err != nil {
187+ return "" , err
188+ }
189+
190+ // Handle both old ServerDetail format and new PublishRequest format
191+ if server , exists := serverData ["server" ]; exists {
192+ // New PublishRequest format
193+ if serverMap , ok := server .(map [string ]any ); ok {
194+ if name , ok := serverMap ["name" ].(string ); ok {
195+ return name , nil
196+ }
197+ }
198+ } else if name , ok := serverData ["name" ].(string ); ok {
199+ // Old ServerDetail format
200+ return name , nil
201+ }
202+
203+ return "" , errors .New ("could not find server name in file" )
204+ }
205+
206+ func findServerIDByName (serverName string ) (string , error ) {
207+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
208+ defer cancel ()
209+
210+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , registryURL + "/v0/servers" , nil )
211+ if err != nil {
212+ return "" , err
213+ }
214+
215+ resp , err := http .DefaultClient .Do (req )
216+ if err != nil {
217+ return "" , err
218+ }
219+ defer resp .Body .Close ()
220+
221+ if resp .StatusCode != http .StatusOK {
222+ body , _ := io .ReadAll (resp .Body )
223+ return "" , fmt .Errorf ("registry responded %d: %s" , resp .StatusCode , string (body ))
224+ }
225+
226+ var serverList struct {
227+ Servers []map [string ]any `json:"servers"`
196228 }
197- return m [1 ], nil
229+ if err := json .NewDecoder (resp .Body ).Decode (& serverList ); err != nil {
230+ return "" , fmt .Errorf ("failed to decode server list: %w" , err )
231+ }
232+
233+ // Find the server with matching name
234+ for _ , server := range serverList .Servers {
235+ if registryMeta , ok := server ["x-io.modelcontextprotocol.registry" ].(map [string ]any ); ok {
236+ if id , ok := registryMeta ["id" ].(string ); ok {
237+ if serverData , ok := server ["server" ].(map [string ]any ); ok {
238+ if name , ok := serverData ["name" ].(string ); ok && name == serverName {
239+ return id , nil
240+ }
241+ }
242+ }
243+ }
244+ }
245+
246+ return "" , fmt .Errorf ("could not find server with name %s" , serverName )
198247}
199248
200249func verifyPublishedServer (id string , expected map [string ]any ) error {
@@ -221,20 +270,20 @@ func verifyPublishedServer(id string, expected map[string]any) error {
221270 if err := json .Unmarshal (content , & actual ); err != nil {
222271 return fmt .Errorf ("failed to unmarshal registry response: %w" , err )
223272 }
224-
273+
225274 // Both API response and expected are now in extension wrapper format
226275 // Compare the server portions of both
227276 actualServer , ok := actual ["server" ]
228277 if ! ok {
229278 return fmt .Errorf ("expected server field in registry response" )
230279 }
231-
280+
232281 // Extract expected server portion for comparison
233282 expectedServer := expected
234283 if server , exists := expected ["server" ]; exists {
235284 expectedServer = server .(map [string ]any )
236285 }
237-
286+
238287 if err := compare (expectedServer , actualServer ); err != nil {
239288 return fmt .Errorf (`example "%s": %w` , expectedServer ["name" ], err )
240289 }
@@ -246,9 +295,10 @@ type example struct {
246295 line int
247296}
248297
249- func examples (path string ) ([]example , error ) {
298+ func getExamples (path string ) ([]example , error ) {
250299 b , err := os .ReadFile (path )
251300 if err != nil {
301+ log .Fatalf ("examples not found; run this test from the repo root" )
252302 return nil , err
253303 }
254304
0 commit comments