[object Object]

← back to Cli Printing Press

fix(cli): authenticate mega MCP library fetches with GITHUB_TOKEN

1d4e642c31e39638ccde61336826bee35ba5b858 · 2026-04-18 00:39:01 -0400 · Matt Van Horn

The printing-press-mcp binary fetches registry.json and per-API
tools-manifest.json from raw.githubusercontent.com/mvanhorn/printing-press-library.
The library repo is private, so unauthenticated reads return 404 and
the mega MCP ships with an empty tool catalog.

When GITHUB_TOKEN is set, attach `Authorization: token <value>` to the
outbound GET. Same URL, same response shape, just with auth. Matches
the existing convention in internal/crowdsniff/github.go.

Plan: docs/plans/2026-04-17-002-fix-private-library-registry-auth-plan.md

🤖 Generated with Claude Opus 4.7 (1M context) via [Claude Code](https://claude.com/claude-code) + Compound Engineering v2.56.1

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 1d4e642c31e39638ccde61336826bee35ba5b858
Author: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Date:   Sat Apr 18 00:39:01 2026 -0400

    fix(cli): authenticate mega MCP library fetches with GITHUB_TOKEN
    
    The printing-press-mcp binary fetches registry.json and per-API
    tools-manifest.json from raw.githubusercontent.com/mvanhorn/printing-press-library.
    The library repo is private, so unauthenticated reads return 404 and
    the mega MCP ships with an empty tool catalog.
    
    When GITHUB_TOKEN is set, attach `Authorization: token <value>` to the
    outbound GET. Same URL, same response shape, just with auth. Matches
    the existing convention in internal/crowdsniff/github.go.
    
    Plan: docs/plans/2026-04-17-002-fix-private-library-registry-auth-plan.md
    
    🤖 Generated with Claude Opus 4.7 (1M context) via [Claude Code](https://claude.com/claude-code) + Compound Engineering v2.56.1
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 internal/megamcp/manifest.go      | 12 +++++++++++-
 internal/megamcp/registry.go      | 15 +++++++++++++--
 internal/megamcp/registry_test.go | 26 ++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/internal/megamcp/manifest.go b/internal/megamcp/manifest.go
index 056077b1..b7955db9 100644
--- a/internal/megamcp/manifest.go
+++ b/internal/megamcp/manifest.go
@@ -279,8 +279,18 @@ func tryCache(cachePath, expectedChecksum string) (*ToolsManifest, error) {
 }
 
 // fetchManifestData downloads manifest data from a URL.
+// When GITHUB_TOKEN is set, an Authorization header is attached so private-repo
+// reads via raw.githubusercontent.com succeed.
 func fetchManifestData(manifestURL string) ([]byte, error) {
-	resp, err := http.Get(manifestURL)
+	req, err := http.NewRequest(http.MethodGet, manifestURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("building request: %w", err)
+	}
+	if token := os.Getenv("GITHUB_TOKEN"); token != "" {
+		req.Header.Set("Authorization", "token "+token)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 		return nil, fmt.Errorf("HTTP request: %w", err)
 	}
diff --git a/internal/megamcp/registry.go b/internal/megamcp/registry.go
index 07a69586..33014675 100644
--- a/internal/megamcp/registry.go
+++ b/internal/megamcp/registry.go
@@ -5,17 +5,28 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"os"
 )
 
-// DefaultBaseURL is the default GitHub raw content URL for the public library repo.
+// DefaultBaseURL is the default GitHub raw content URL for the library repo.
 const DefaultBaseURL = "https://raw.githubusercontent.com/mvanhorn/printing-press-library/main"
 
 // FetchRegistry fetches registry.json from baseURL and parses it into a Registry.
 // baseURL is injectable for testing (use httptest.NewServer).
+// When GITHUB_TOKEN is set, an Authorization header is attached so private-repo
+// reads via raw.githubusercontent.com succeed.
 func FetchRegistry(baseURL string) (*Registry, error) {
 	registryURL := baseURL + "/registry.json"
 
-	resp, err := http.Get(registryURL)
+	req, err := http.NewRequest(http.MethodGet, registryURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("building registry request: %w", err)
+	}
+	if token := os.Getenv("GITHUB_TOKEN"); token != "" {
+		req.Header.Set("Authorization", "token "+token)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
 	if err != nil {
 		return nil, fmt.Errorf("fetching registry: %w", err)
 	}
diff --git a/internal/megamcp/registry_test.go b/internal/megamcp/registry_test.go
index 83ddf77b..34f668d3 100644
--- a/internal/megamcp/registry_test.go
+++ b/internal/megamcp/registry_test.go
@@ -96,3 +96,29 @@ func TestFetchRegistryInvalidJSON(t *testing.T) {
 	assert.Nil(t, result)
 	assert.Contains(t, err.Error(), "parsing registry JSON")
 }
+
+func TestFetchRegistryAttachesGitHubToken(t *testing.T) {
+	var gotAuth string
+	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		gotAuth = r.Header.Get("Authorization")
+		w.Header().Set("Content-Type", "application/json")
+		json.NewEncoder(w).Encode(Registry{SchemaVersion: 1})
+	}))
+	defer server.Close()
+
+	t.Run("with token set", func(t *testing.T) {
+		gotAuth = ""
+		t.Setenv("GITHUB_TOKEN", "ghp_testtoken123")
+		_, err := FetchRegistry(server.URL)
+		require.NoError(t, err)
+		assert.Equal(t, "token ghp_testtoken123", gotAuth)
+	})
+
+	t.Run("with token unset", func(t *testing.T) {
+		gotAuth = ""
+		t.Setenv("GITHUB_TOKEN", "")
+		_, err := FetchRegistry(server.URL)
+		require.NoError(t, err)
+		assert.Empty(t, gotAuth)
+	})
+}

← 3fd4a5a8 style(cli): simplify async-detect sibling-filter boolean  ·  back to Cli Printing Press  ·  feat(cli): printing-press patch — AST-inject PR #218 feature 16ed5a56 →