@@ -19,10 +19,10 @@ const (
1919 DockerCatalogURL = "https://desktop.docker.com/mcp/catalog/v3/catalog.yaml"
2020 catalogCacheFileName = "mcp_catalog.json"
2121 fetchTimeout = 15 * time .Second
22- )
2322
24- // catalogJSON is the URL we actually fetch (JSON is ~3x faster to parse than YAML).
25- var catalogJSON = strings .Replace (DockerCatalogURL , ".yaml" , ".json" , 1 )
23+ // catalogJSON is the URL we actually fetch (JSON is ~3x faster to parse than YAML).
24+ catalogJSON = "https://desktop.docker.com/mcp/catalog/v3/catalog.json"
25+ )
2626
2727func RequiredEnvVars (ctx context.Context , serverName string ) ([]Secret , error ) {
2828 server , err := ServerSpec (ctx , serverName )
@@ -40,8 +40,8 @@ func RequiredEnvVars(ctx context.Context, serverName string) ([]Secret, error) {
4040 return server .Secrets , nil
4141}
4242
43- func ServerSpec (ctx context.Context , serverName string ) (Server , error ) {
44- catalog , err := loadCatalog ( ctx )
43+ func ServerSpec (_ context.Context , serverName string ) (Server , error ) {
44+ catalog , err := catalogOnce ( )
4545 if err != nil {
4646 return Server {}, err
4747 }
@@ -54,6 +54,11 @@ func ServerSpec(ctx context.Context, serverName string) (Server, error) {
5454 return server , nil
5555}
5656
57+ // ParseServerRef strips the optional "docker:" prefix from a server reference.
58+ func ParseServerRef (ref string ) string {
59+ return strings .TrimPrefix (ref , "docker:" )
60+ }
61+
5762// cachedCatalog is the on-disk cache format.
5863type cachedCatalog struct {
5964 Catalog Catalog `json:"catalog"`
@@ -69,12 +74,6 @@ var catalogOnce = sync.OnceValues(func() (Catalog, error) {
6974 return fetchAndCache (context .Background ())
7075})
7176
72- // loadCatalog returns the catalog, fetching it at most once per process run.
73- // On network failure it falls back to the disk cache.
74- func loadCatalog (_ context.Context ) (Catalog , error ) {
75- return catalogOnce ()
76- }
77-
7877// fetchAndCache tries to fetch the catalog from the network (using ETag for
7978// conditional requests) and falls back to the disk cache on any failure.
8079func fetchAndCache (ctx context.Context ) (Catalog , error ) {
@@ -128,16 +127,24 @@ func saveToDisk(path string, catalog Catalog, etag string) {
128127 }
129128
130129 dir := filepath .Dir (path )
131- if err := os .MkdirAll (dir , 0o755 ); err != nil {
132- slog .Warn ("Failed to create MCP catalog cache directory" , "error" , err )
133- return
134- }
135130
136131 // Write to a temp file and rename so readers never see a partial file.
132+ // Try creating the temp file first; only create the directory if needed.
137133 tmp , err := os .CreateTemp (dir , ".mcp_catalog_*.tmp" )
138134 if err != nil {
139- slog .Warn ("Failed to create MCP catalog temp file" , "error" , err )
140- return
135+ if ! os .IsNotExist (err ) {
136+ slog .Warn ("Failed to create MCP catalog temp file" , "error" , err )
137+ return
138+ }
139+ if mkErr := os .MkdirAll (dir , 0o755 ); mkErr != nil {
140+ slog .Warn ("Failed to create MCP catalog cache directory" , "error" , mkErr )
141+ return
142+ }
143+ tmp , err = os .CreateTemp (dir , ".mcp_catalog_*.tmp" )
144+ if err != nil {
145+ slog .Warn ("Failed to create MCP catalog temp file" , "error" , err )
146+ return
147+ }
141148 }
142149 tmpName := tmp .Name ()
143150
@@ -159,6 +166,10 @@ func saveToDisk(path string, catalog Catalog, etag string) {
159166 }
160167}
161168
169+ // catalogClient is a dedicated HTTP client for catalog fetches, isolated from
170+ // http.DefaultClient so that other parts of the process cannot interfere.
171+ var catalogClient = & http.Client {}
172+
162173// fetchFromNetwork fetches the catalog, using the ETag for conditional requests.
163174// It returns (nil, "", nil) when the server responds with 304 Not Modified.
164175func fetchFromNetwork (ctx context.Context , etag string ) (Catalog , string , error ) {
@@ -174,7 +185,7 @@ func fetchFromNetwork(ctx context.Context, etag string) (Catalog, string, error)
174185 req .Header .Set ("If-None-Match" , etag )
175186 }
176187
177- resp , err := http . DefaultClient .Do (req )
188+ resp , err := catalogClient .Do (req )
178189 if err != nil {
179190 return nil , "" , err
180191 }
0 commit comments