[object Object]

← back to Cli Printing Press

feat(cli): scorecard dimensions for remote transport, tool design, surface strategy (#245)

2d07a0218423e3090cbdc033cd9998006fc7c309 · 2026-04-22 21:09:28 -0700 · Matt Van Horn

Adds three MCP-shape dimensions to SteinerScore so the scorer reflects the
patterns introduced in U1-U3:

- mcp_remote_transport: 10 when both stdio+http compile in, 7 for http-only,
  5 for stdio-only (baseline — stdio can't reach cloud agents). Unscored
  when no MCP surface.
- mcp_tool_design: 10 for code-orchestration or intent ratio >= 0.3, 7 for
  sparse intents, 5 for plain endpoint-mirror at scale. Unscored for small
  APIs (<10 endpoints) where intent grouping has little value.
- mcp_surface_strategy: 10 for code-orchestration, 7 for intents, 2 for
  endpoint-mirror above 50 endpoints (article's named anti-pattern).
  Unscored at small scale where strategy doesn't matter.

All three follow the mcp_token_efficiency precedent — opt-in via
(score, bool) return so absence yields unscored rather than zero, keeping
tier-1 denominator math correct for CLIs without the relevant surface.

Plan: docs/plans/2026-04-22-003-feat-mcp-production-readiness-plan.md (U4)

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 2d07a0218423e3090cbdc033cd9998006fc7c309
Author: Matt Van Horn <mvanhorn@users.noreply.github.com>
Date:   Wed Apr 22 21:09:28 2026 -0700

    feat(cli): scorecard dimensions for remote transport, tool design, surface strategy (#245)
    
    Adds three MCP-shape dimensions to SteinerScore so the scorer reflects the
    patterns introduced in U1-U3:
    
    - mcp_remote_transport: 10 when both stdio+http compile in, 7 for http-only,
      5 for stdio-only (baseline — stdio can't reach cloud agents). Unscored
      when no MCP surface.
    - mcp_tool_design: 10 for code-orchestration or intent ratio >= 0.3, 7 for
      sparse intents, 5 for plain endpoint-mirror at scale. Unscored for small
      APIs (<10 endpoints) where intent grouping has little value.
    - mcp_surface_strategy: 10 for code-orchestration, 7 for intents, 2 for
      endpoint-mirror above 50 endpoints (article's named anti-pattern).
      Unscored at small scale where strategy doesn't matter.
    
    All three follow the mcp_token_efficiency precedent — opt-in via
    (score, bool) return so absence yields unscored rather than zero, keeping
    tier-1 denominator math correct for CLIs without the relevant surface.
    
    Plan: docs/plans/2026-04-22-003-feat-mcp-production-readiness-plan.md (U4)
    
    Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
    Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 internal/pipeline/mcp_scoring.go          | 174 ++++++++++++++++++++++++++
 internal/pipeline/mcp_scoring_test.go     | 197 ++++++++++++++++++++++++++++++
 internal/pipeline/scorecard.go            |  68 ++++++++---
 internal/pipeline/scorecard_tier2_test.go |   4 +-
 4 files changed, 423 insertions(+), 20 deletions(-)

diff --git a/internal/pipeline/mcp_scoring.go b/internal/pipeline/mcp_scoring.go
new file mode 100644
index 00000000..8ff45409
--- /dev/null
+++ b/internal/pipeline/mcp_scoring.go
@@ -0,0 +1,174 @@
+package pipeline
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+// Thresholds for the tool-design dimension. Intent-grouping is only worth
+// scoring when the endpoint mirror would otherwise emit enough tools for
+// agents to feel the pain — small APIs (under ~10 endpoints) are fine as a
+// plain mirror and shouldn't be docked for not using intents.
+const toolDesignMinEndpoints = 10
+
+// Threshold above which an endpoint-mirror surface counts as the article's
+// named anti-pattern for large APIs. Matches the spec's
+// DefaultOrchestrationThreshold; kept independent here so scoring does not
+// couple to the spec package's default.
+const surfaceStrategyLargeThreshold = 50
+
+// mcpSurface captures the shape of the generated MCP surface inferred from
+// the CLI source tree. Each scorer consumes this summary instead of
+// rediscovering the file layout.
+type mcpSurface struct {
+	present         bool
+	mainPath        string // cmd/<cli>-pp-mcp/main.go
+	toolsPath       string // internal/mcp/tools.go
+	intentsPresent  bool   // internal/mcp/intents.go exists
+	codeOrchPresent bool   // internal/mcp/code_orch.go exists
+	endpointTools   int    // count of endpoint-mirror NewTool(...) registrations
+	intentTools     int    // count of intent tool registrations inside intents.go
+}
+
+// detectMCPSurface walks the printed CLI's generated layout and returns a
+// summary of the MCP surface. A nil return (present=false) signals that the
+// CLI does not emit an MCP server — the caller should mark every MCP-only
+// dimension as unscored.
+func detectMCPSurface(dir string) mcpSurface {
+	var s mcpSurface
+
+	// Find the cmd/<name>-pp-mcp/main.go by matching the suffix rather than
+	// deriving the name; keeps this scorer decoupled from naming.CLI / naming.MCP.
+	cmdDir := filepath.Join(dir, "cmd")
+	entries, err := os.ReadDir(cmdDir)
+	if err != nil {
+		return s
+	}
+	for _, e := range entries {
+		if !e.IsDir() {
+			continue
+		}
+		if strings.HasSuffix(e.Name(), "-pp-mcp") {
+			s.mainPath = filepath.Join(cmdDir, e.Name(), "main.go")
+			if _, err := os.Stat(s.mainPath); err == nil {
+				s.present = true
+			}
+			break
+		}
+	}
+	if !s.present {
+		return s
+	}
+
+	s.toolsPath = filepath.Join(dir, "internal", "mcp", "tools.go")
+	if data, err := os.ReadFile(s.toolsPath); err == nil {
+		s.endpointTools = strings.Count(string(data), "mcplib.NewTool(")
+	}
+
+	if data, err := os.ReadFile(filepath.Join(dir, "internal", "mcp", "intents.go")); err == nil {
+		s.intentsPresent = true
+		s.intentTools = strings.Count(string(data), "mcplib.NewTool(")
+	}
+	if _, err := os.Stat(filepath.Join(dir, "internal", "mcp", "code_orch.go")); err == nil {
+		s.codeOrchPresent = true
+	}
+	return s
+}
+
+// scoreMCPRemoteTransport scores 0-10 based on the transports the generated
+// binary compiles support for. The article's first pattern is "build remote
+// servers for maximum reach" — stdio-only servers cannot reach cloud-hosted
+// agents, so they score below the default line. Servers that compile in
+// both transports (stdio for local plus http for hosted) get full marks.
+//
+// Returns (0, false) when the CLI emits no MCP surface so the dimension can
+// be excluded from the tier-1 denominator.
+func scoreMCPRemoteTransport(dir string) (int, bool) {
+	s := detectMCPSurface(dir)
+	if !s.present {
+		return 0, false
+	}
+	data, err := os.ReadFile(s.mainPath)
+	if err != nil {
+		return 0, false
+	}
+	body := string(data)
+	hasStdio := strings.Contains(body, "server.ServeStdio")
+	hasHTTP := strings.Contains(body, "NewStreamableHTTPServer") || strings.Contains(body, "ServeStreamableHTTP")
+	switch {
+	case hasStdio && hasHTTP:
+		return 10, true
+	case hasHTTP:
+		return 7, true
+	case hasStdio:
+		return 5, true
+	default:
+		return 0, true
+	}
+}
+
+// scoreMCPToolDesign scores 0-10 based on whether the MCP surface was
+// designed around agent-facing intents (article's second pattern) or left
+// as a one-to-one endpoint mirror (article's named anti-pattern). Small
+// APIs are unscored because intent-grouping has little value at low
+// endpoint counts.
+//
+// Scoring:
+//   - code-orchestration: 10 (explicitly thin surface — article's reference shape)
+//   - intents present + ratio >= 0.3: 10 (solid coverage)
+//   - intents present + ratio <  0.3: 7  (some coverage)
+//   - endpoint-mirror only: 5 (baseline — works, but leaves value on the table)
+//
+// When present but endpoint count < toolDesignMinEndpoints, returns
+// (0, false) — the decision doesn't meaningfully affect an agent at small
+// surface sizes.
+func scoreMCPToolDesign(dir string) (int, bool) {
+	s := detectMCPSurface(dir)
+	if !s.present {
+		return 0, false
+	}
+	if s.codeOrchPresent {
+		return 10, true
+	}
+	if s.endpointTools < toolDesignMinEndpoints {
+		return 0, false
+	}
+	if s.intentsPresent && s.intentTools > 0 {
+		ratio := float64(s.intentTools) / float64(s.endpointTools+s.intentTools)
+		if ratio >= 0.3 {
+			return 10, true
+		}
+		return 7, true
+	}
+	return 5, true
+}
+
+// scoreMCPSurfaceStrategy scores 0-10 based on whether the MCP surface
+// strategy matches the API's size. Very large APIs (50+ endpoints) that
+// ship as a plain endpoint-mirror are the article's named anti-pattern —
+// even well-grouped intents eventually overflow context at those sizes.
+//
+// Scoring (only when endpointTools + intentTools > surfaceStrategyLargeThreshold):
+//   - code-orchestration: 10 (matches Cloudflare's reference case)
+//   - intents present: 7  (mitigates the problem, doesn't eliminate it)
+//   - endpoint-mirror:  2  (named anti-pattern at scale)
+//
+// Small APIs are (0, false) — the decision does not matter at low scale.
+func scoreMCPSurfaceStrategy(dir string) (int, bool) {
+	s := detectMCPSurface(dir)
+	if !s.present {
+		return 0, false
+	}
+	total := s.endpointTools + s.intentTools
+	if !s.codeOrchPresent && total <= surfaceStrategyLargeThreshold {
+		return 0, false
+	}
+	if s.codeOrchPresent {
+		return 10, true
+	}
+	if s.intentsPresent && s.intentTools > 0 {
+		return 7, true
+	}
+	return 2, true
+}
diff --git a/internal/pipeline/mcp_scoring_test.go b/internal/pipeline/mcp_scoring_test.go
new file mode 100644
index 00000000..78d0e873
--- /dev/null
+++ b/internal/pipeline/mcp_scoring_test.go
@@ -0,0 +1,197 @@
+package pipeline
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+// writeMCPFile is a table-test friendly helper that mkdir-p's the path and
+// writes body. Kept local so mcp_scoring_test.go stays self-contained.
+func writeMCPFile(t *testing.T, dir, rel, body string) {
+	t.Helper()
+	full := filepath.Join(dir, rel)
+	require.NoError(t, os.MkdirAll(filepath.Dir(full), 0o755))
+	require.NoError(t, os.WriteFile(full, []byte(body), 0o644))
+}
+
+// stdioOnlyMain mimics the template output for a spec that did not opt into
+// remote transport. The scorer inspects strings, so the exact surrounding
+// code doesn't matter — only that ServeStdio appears and ServeStreamableHTTP /
+// NewStreamableHTTPServer do not.
+const stdioOnlyMain = `package main
+func main() { server.ServeStdio(s) }
+`
+
+// remoteOnlyMain simulates a hypothetical http-only spec. The generator
+// doesn't emit this today (stdio is always in the effective list when http
+// is declared), but the scorer should still award the correct middle band
+// so it remains valid if a future template change lands.
+const remoteOnlyMain = `package main
+func main() { httpSrv := server.NewStreamableHTTPServer(s); httpSrv.Start(":7777") }
+`
+
+// bothTransportsMain is what the current template emits when a spec declares
+// transport: [stdio, http]. Both branches show up in the same source file.
+const bothTransportsMain = `package main
+func main() {
+	switch *transport {
+	case "stdio":
+		server.ServeStdio(s)
+	case "http":
+		httpSrv := server.NewStreamableHTTPServer(s)
+		httpSrv.Start(*addr)
+	}
+}
+`
+
+func TestScoreMCPRemoteTransport(t *testing.T) {
+	t.Run("unscored when no MCP emitted", func(t *testing.T) {
+		dir := t.TempDir()
+		score, scored := scoreMCPRemoteTransport(dir)
+		assert.False(t, scored, "no MCP surface → unscored")
+		assert.Equal(t, 0, score)
+	})
+
+	t.Run("stdio only scores baseline", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		score, scored := scoreMCPRemoteTransport(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 5, score, "stdio-only gets middle-low band (remote-unreachable)")
+	})
+
+	t.Run("http only scores partial", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", remoteOnlyMain)
+		score, scored := scoreMCPRemoteTransport(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 7, score)
+	})
+
+	t.Run("both transports score full", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", bothTransportsMain)
+		score, scored := scoreMCPRemoteTransport(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 10, score)
+	})
+}
+
+// buildToolsGo fabricates an internal/mcp/tools.go containing `n` endpoint
+// tool registrations. The scorer counts mcplib.NewTool( occurrences, so the
+// surrounding code is irrelevant.
+func buildToolsGo(n int) string {
+	var b strings.Builder
+	b.WriteString("package mcp\nfunc RegisterTools(s *server.MCPServer) {\n")
+	for i := 0; i < n; i++ {
+		b.WriteString("\tmcplib.NewTool(\"endpoint_")
+		b.WriteString(string(rune('a' + i%26)))
+		b.WriteString("\",)\n")
+	}
+	b.WriteString("}\n")
+	return b.String()
+}
+
+func TestScoreMCPToolDesign(t *testing.T) {
+	t.Run("unscored when no MCP emitted", func(t *testing.T) {
+		dir := t.TempDir()
+		score, scored := scoreMCPToolDesign(dir)
+		assert.False(t, scored)
+		assert.Equal(t, 0, score)
+	})
+
+	t.Run("unscored when endpoint count under threshold", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(5))
+		score, scored := scoreMCPToolDesign(dir)
+		assert.False(t, scored, "small surfaces don't get docked for not using intents")
+		assert.Equal(t, 0, score)
+	})
+
+	t.Run("endpoint mirror at scale scores baseline", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(20))
+		score, scored := scoreMCPToolDesign(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 5, score, "plain endpoint-mirror at scale gets baseline 5, not zero")
+	})
+
+	t.Run("code orchestration wins full marks", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", "package mcp\n")
+		writeMCPFile(t, dir, "internal/mcp/code_orch.go", "package mcp\nfunc RegisterCodeOrchestrationTools() {}\n")
+		score, scored := scoreMCPToolDesign(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 10, score)
+	})
+
+	t.Run("intents with good coverage score full marks", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(10))
+		// 5 intents vs 10 endpoints → ratio 0.33, above 0.3 threshold.
+		writeMCPFile(t, dir, "internal/mcp/intents.go", buildToolsGo(5))
+		score, scored := scoreMCPToolDesign(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 10, score)
+	})
+
+	t.Run("sparse intents score partial", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(20))
+		// 2 intents vs 20 endpoints → ratio 0.09, well below 0.3.
+		writeMCPFile(t, dir, "internal/mcp/intents.go", buildToolsGo(2))
+		score, scored := scoreMCPToolDesign(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 7, score)
+	})
+}
+
+func TestScoreMCPSurfaceStrategy(t *testing.T) {
+	t.Run("unscored at small scale", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(20))
+		score, scored := scoreMCPSurfaceStrategy(dir)
+		assert.False(t, scored, "below 50-endpoint threshold → strategy doesn't matter")
+		assert.Equal(t, 0, score)
+	})
+
+	t.Run("endpoint mirror at scale is the article's anti-pattern", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(80))
+		score, scored := scoreMCPSurfaceStrategy(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 2, score, "endpoint mirror at 80 endpoints scores low, not zero")
+	})
+
+	t.Run("intents at scale score partial", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", buildToolsGo(60))
+		writeMCPFile(t, dir, "internal/mcp/intents.go", buildToolsGo(4))
+		score, scored := scoreMCPSurfaceStrategy(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 7, score)
+	})
+
+	t.Run("code orchestration wins at any scale", func(t *testing.T) {
+		dir := t.TempDir()
+		writeMCPFile(t, dir, "cmd/demo-pp-mcp/main.go", stdioOnlyMain)
+		writeMCPFile(t, dir, "internal/mcp/tools.go", "package mcp\n")
+		writeMCPFile(t, dir, "internal/mcp/code_orch.go", "package mcp\n")
+		score, scored := scoreMCPSurfaceStrategy(dir)
+		assert.True(t, scored)
+		assert.Equal(t, 10, score)
+	})
+}
diff --git a/internal/pipeline/scorecard.go b/internal/pipeline/scorecard.go
index 06663015..f38c448a 100644
--- a/internal/pipeline/scorecard.go
+++ b/internal/pipeline/scorecard.go
@@ -40,22 +40,25 @@ type Scorecard struct {
 
 // SteinerScore breaks down the Steinberger bar into 11 dimensions, each 0-10.
 type SteinerScore struct {
-	OutputModes    int `json:"output_modes"`             // 0-10
-	Auth           int `json:"auth"`                     // 0-10
-	ErrorHandling  int `json:"error_handling"`           // 0-10
-	TerminalUX     int `json:"terminal_ux"`              // 0-10
-	README         int `json:"readme"`                   // 0-10
-	Doctor         int `json:"doctor"`                   // 0-10
-	AgentNative    int `json:"agent_native"`             // 0-10
-	MCPQuality     int `json:"mcp_quality"`              // 0-10
-	MCPTokenEff    int `json:"mcp_token_efficiency"`     // 0-10; unscored when no MCP surface
-	LocalCache     int `json:"local_cache"`              // 0-10
-	CacheFreshness int `json:"cache_freshness"`          // 0-10; unscored when the CLI has no local store
-	Breadth        int `json:"breadth"`                  // 0-10: how many commands (penalizes empty CLIs)
-	Vision         int `json:"vision"`                   // 0-10
-	Workflows      int `json:"workflows"`                // 0-10
-	Insight        int `json:"insight"`                  // 0-10
-	AgentWorkflow  int `json:"agent_workflow_readiness"` // 0-10: HeyGen-derived - async jobs, profiles, deliver, feedback
+	OutputModes        int `json:"output_modes"`             // 0-10
+	Auth               int `json:"auth"`                     // 0-10
+	ErrorHandling      int `json:"error_handling"`           // 0-10
+	TerminalUX         int `json:"terminal_ux"`              // 0-10
+	README             int `json:"readme"`                   // 0-10
+	Doctor             int `json:"doctor"`                   // 0-10
+	AgentNative        int `json:"agent_native"`             // 0-10
+	MCPQuality         int `json:"mcp_quality"`              // 0-10
+	MCPTokenEff        int `json:"mcp_token_efficiency"`     // 0-10; unscored when no MCP surface
+	MCPRemoteTransport int `json:"mcp_remote_transport"`     // 0-10; unscored when no MCP surface. Rewards remote-capable servers per Anthropic's 2026-04 MCP guidance.
+	MCPToolDesign      int `json:"mcp_tool_design"`          // 0-10; unscored when no MCP surface or endpoint count below toolDesignMinEndpoints. Rewards intent-grouped tools vs. endpoint mirrors.
+	MCPSurfaceStrategy int `json:"mcp_surface_strategy"`     // 0-10; unscored unless the endpoint surface exceeds surfaceStrategyLargeThreshold or code-orchestration is explicitly used. Penalizes endpoint-mirror at scale.
+	LocalCache         int `json:"local_cache"`              // 0-10
+	CacheFreshness     int `json:"cache_freshness"`          // 0-10; unscored when the CLI has no local store
+	Breadth            int `json:"breadth"`                  // 0-10: how many commands (penalizes empty CLIs)
+	Vision             int `json:"vision"`                   // 0-10
+	Workflows          int `json:"workflows"`                // 0-10
+	Insight            int `json:"insight"`                  // 0-10
+	AgentWorkflow      int `json:"agent_workflow_readiness"` // 0-10: HeyGen-derived - async jobs, profiles, deliver, feedback
 	// Tier 2: Domain Correctness (semantic checks)
 	PathValidity          int    `json:"path_validity"`           // 0-10
 	AuthProtocol          int    `json:"auth_protocol"`           // 0-10
@@ -99,6 +102,21 @@ func RunScorecard(outputDir, pipelineDir, specPath string, verifyReport *VerifyR
 	} else {
 		sc.UnscoredDimensions = append(sc.UnscoredDimensions, "mcp_token_efficiency")
 	}
+	if remoteScore, scored := scoreMCPRemoteTransport(outputDir); scored {
+		sc.Steinberger.MCPRemoteTransport = remoteScore
+	} else {
+		sc.UnscoredDimensions = append(sc.UnscoredDimensions, "mcp_remote_transport")
+	}
+	if toolDesignScore, scored := scoreMCPToolDesign(outputDir); scored {
+		sc.Steinberger.MCPToolDesign = toolDesignScore
+	} else {
+		sc.UnscoredDimensions = append(sc.UnscoredDimensions, "mcp_tool_design")
+	}
+	if strategyScore, scored := scoreMCPSurfaceStrategy(outputDir); scored {
+		sc.Steinberger.MCPSurfaceStrategy = strategyScore
+	} else {
+		sc.UnscoredDimensions = append(sc.UnscoredDimensions, "mcp_surface_strategy")
+	}
 	sc.Steinberger.LocalCache = scoreLocalCache(outputDir)
 	if cacheFreshnessScore, scored := scoreCacheFreshness(outputDir); scored {
 		sc.Steinberger.CacheFreshness = cacheFreshnessScore
@@ -157,7 +175,9 @@ func RunScorecard(outputDir, pipelineDir, specPath string, verifyReport *VerifyR
 		sc.UnscoredDimensions = append(sc.UnscoredDimensions, "live_api_verification")
 	}
 
-	// Tier 1: Infrastructure (string-matching, 150 max; 140 when no MCP surface)
+	// Tier 1: Infrastructure (string-matching, 190 max; reduced per dimension
+	// when the opt-in MCP, cache_freshness, and new MCP-shape dimensions are
+	// absent for this CLI — see tier1Max below).
 	tier1Raw := sc.Steinberger.OutputModes +
 		sc.Steinberger.Auth +
 		sc.Steinberger.ErrorHandling +
@@ -167,6 +187,9 @@ func RunScorecard(outputDir, pipelineDir, specPath string, verifyReport *VerifyR
 		sc.Steinberger.AgentNative +
 		sc.Steinberger.MCPQuality +
 		sc.Steinberger.MCPTokenEff +
+		sc.Steinberger.MCPRemoteTransport +
+		sc.Steinberger.MCPToolDesign +
+		sc.Steinberger.MCPSurfaceStrategy +
 		sc.Steinberger.LocalCache +
 		sc.Steinberger.CacheFreshness +
 		sc.Steinberger.Breadth +
@@ -194,13 +217,22 @@ func RunScorecard(outputDir, pipelineDir, specPath string, verifyReport *VerifyR
 	// Weighted composite: Tier 1 = 50%, Tier 2 = 50% of final 100-point scale.
 	// Tier 1 max is 160 with MCP + cache_freshness, 150 without MCP, 150 without
 	// cache_freshness, 140 without either.
-	tier1Max := 160
+	tier1Max := 190
 	if sc.IsDimensionUnscored("mcp_token_efficiency") {
 		tier1Max -= 10
 	}
 	if sc.IsDimensionUnscored("cache_freshness") {
 		tier1Max -= 10
 	}
+	if sc.IsDimensionUnscored("mcp_remote_transport") {
+		tier1Max -= 10
+	}
+	if sc.IsDimensionUnscored("mcp_tool_design") {
+		tier1Max -= 10
+	}
+	if sc.IsDimensionUnscored("mcp_surface_strategy") {
+		tier1Max -= 10
+	}
 	tier1Normalized := (tier1Raw * 50) / tier1Max // scale 0-tier1Max to 0-50
 	// Tier 2 max is 60 when live verify ran, 50 otherwise. The live-verify
 	// dimension is opt-in: a CLI that only ran mock-backed verify keeps the
diff --git a/internal/pipeline/scorecard_tier2_test.go b/internal/pipeline/scorecard_tier2_test.go
index 16f32871..c028d9c1 100644
--- a/internal/pipeline/scorecard_tier2_test.go
+++ b/internal/pipeline/scorecard_tier2_test.go
@@ -514,7 +514,7 @@ func runLinks() string {
 		pipelineDir := t.TempDir()
 		sc, err := RunScorecard(dir, pipelineDir, "", nil)
 		assert.NoError(t, err)
-		assert.ElementsMatch(t, []string{"mcp_token_efficiency", "cache_freshness", "path_validity", "auth_protocol", "live_api_verification"}, sc.UnscoredDimensions)
+		assert.ElementsMatch(t, []string{"mcp_token_efficiency", "mcp_remote_transport", "mcp_tool_design", "mcp_surface_strategy", "cache_freshness", "path_validity", "auth_protocol", "live_api_verification"}, sc.UnscoredDimensions)
 		assert.NotContains(t, sc.GapReport, "path_validity scored 0/10 - needs improvement")
 		assert.NotContains(t, sc.GapReport, "auth_protocol scored 0/10 - needs improvement")
 	})
@@ -843,7 +843,7 @@ func runLinks() string {
 		body := string(data)
 		assert.True(t, strings.Contains(body, `"path_validity":0`))
 		assert.True(t, strings.Contains(body, `"auth_protocol":0`))
-		assert.True(t, strings.Contains(body, `"unscored_dimensions":["mcp_token_efficiency","cache_freshness","path_validity","auth_protocol","live_api_verification"]`))
+		assert.True(t, strings.Contains(body, `"unscored_dimensions":["mcp_token_efficiency","mcp_remote_transport","mcp_tool_design","mcp_surface_strategy","cache_freshness","path_validity","auth_protocol","live_api_verification"]`))
 	})
 }
 

← a4207be8 feat(cli): code-orchestration thin surface for large-surface  ·  back to Cli Printing Press  ·  feat(cli): mcp-audit subcommand + docs for the new MCP surfa 38db0610 →