← 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
A internal/pipeline/mcp_scoring.goA internal/pipeline/mcp_scoring_test.goM internal/pipeline/scorecard.goM internal/pipeline/scorecard_tier2_test.go
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 →