[object Object]

← back to Cli Printing Press

feat(cli): compact repeated MCP parameter descriptions (#569)

314b5650f4025eda9c49fc6cac8fb50b1da29aae · 2026-05-03 23:03:51 -0700 · Trevin Chow

Files touched

Diff

commit 314b5650f4025eda9c49fc6cac8fb50b1da29aae
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Sun May 3 23:03:51 2026 -0700

    feat(cli): compact repeated MCP parameter descriptions (#569)
---
 internal/generator/generator.go                |  10 ++
 internal/generator/generator_test.go           |  44 ++++++++
 internal/generator/templates/mcp_tools.go.tmpl |  12 +--
 internal/mcpdesc/params.go                     | 135 +++++++++++++++++++++++++
 internal/mcpdesc/params_test.go                | 107 ++++++++++++++++++++
 internal/pipeline/toolsmanifest.go             |  30 +++++-
 internal/pipeline/toolsmanifest_test.go        |  84 +++++++++++++++
 7 files changed, 411 insertions(+), 11 deletions(-)

diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 518fc78d..68f47152 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -150,6 +150,8 @@ type Generator struct {
 	profile   *profiler.APIProfile
 	funcs     template.FuncMap
 	templates map[string]*template.Template
+
+	mcpParamDescriptions *mcpdesc.ParamDescriptionCompactor
 }
 
 func New(s *spec.APISpec, outputDir string) *Generator {
@@ -211,6 +213,7 @@ func New(s *spec.APISpec, outputDir string) *Generator {
 		"add":                    func(a, b int) int { return a + b },
 		"oneline":                naming.OneLine,
 		"composeMCPDesc":         composeMCPDesc,
+		"mcpParamDesc":           g.mcpParamDescription,
 		"flagName":               flagName,
 		"paramIdent":             paramIdent,
 		"safeTypeName":           safeTypeName,
@@ -2870,6 +2873,13 @@ func composeMCPDesc(endpoint spec.Endpoint, noAuth bool, authType string, public
 	})
 }
 
+func (g *Generator) mcpParamDescription(p spec.Param) string {
+	if g.mcpParamDescriptions == nil {
+		g.mcpParamDescriptions = mcpdesc.NewParamDescriptionCompactor(g.Spec)
+	}
+	return naming.OneLine(g.mcpParamDescriptions.Description(p))
+}
+
 func exampleValue(p spec.Param) string {
 	nameLower := strings.ToLower(p.Name)
 
diff --git a/internal/generator/generator_test.go b/internal/generator/generator_test.go
index 0e60c48a..f4786fcf 100644
--- a/internal/generator/generator_test.go
+++ b/internal/generator/generator_test.go
@@ -4129,6 +4129,50 @@ func TestGenerateMCPContextEscapesDomainStrings(t *testing.T) {
 	assert.Contains(t, src, `filter=\"active\"`)
 }
 
+func TestGenerateMCPCompactsRepeatedParamDescriptions(t *testing.T) {
+	t.Parallel()
+
+	apiSpec := minimalSpec("mcp-dedupe")
+	sharedDescription := "Select additional nested resource fields to include in the response. Use comma-separated field names such as owner, permissions, metadata, relationships, and auditTrail; unsupported values are ignored by the upstream API."
+	apiSpec.Resources = map[string]spec.Resource{
+		"items": {
+			Description: "Manage items",
+			Endpoints: map[string]spec.Endpoint{
+				"list": {
+					Method:      "GET",
+					Path:        "/items",
+					Description: "List items",
+					Params:      []spec.Param{{Name: "expand", Type: "string", Description: sharedDescription}},
+				},
+				"search": {
+					Method:      "GET",
+					Path:        "/items/search",
+					Description: "Search items",
+					Params:      []spec.Param{{Name: "expand", Type: "string", Description: sharedDescription}},
+				},
+				"recent": {
+					Method:      "GET",
+					Path:        "/items/recent",
+					Description: "List recent items",
+					Params:      []spec.Param{{Name: "expand", Type: "string", Description: sharedDescription}},
+				},
+			},
+		},
+	}
+
+	outputDir := filepath.Join(t.TempDir(), naming.CLI(apiSpec.Name))
+	require.NoError(t, New(apiSpec, outputDir).Generate())
+
+	toolsData, err := os.ReadFile(filepath.Join(outputDir, "internal", "mcp", "tools.go"))
+	require.NoError(t, err)
+	toolsBody := string(toolsData)
+
+	assert.NotContains(t, toolsBody, sharedDescription,
+		"generated MCP runtime schema should not repeat long shared parameter descriptions verbatim")
+	assert.Equal(t, 3, strings.Count(toolsBody, `mcplib.Description("Select additional nested resource fields to include in the response.")`),
+		"shared param descriptions should stay understandable after compaction")
+}
+
 func TestEnvVarBuiltinFieldDedup(t *testing.T) {
 	t.Parallel()
 	tests := []struct {
diff --git a/internal/generator/templates/mcp_tools.go.tmpl b/internal/generator/templates/mcp_tools.go.tmpl
index 5227f699..db9ee8d6 100644
--- a/internal/generator/templates/mcp_tools.go.tmpl
+++ b/internal/generator/templates/mcp_tools.go.tmpl
@@ -41,11 +41,11 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDescription({{printf "%q" (composeMCPDesc $endpoint $endpoint.NoAuth $.Auth.Type $.MCPPublicCount $.MCPTotalCount)}}),
 {{- range $endpoint.Params}}
 {{- if eq .Type "integer"}}
-			mcplib.WithNumber({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (oneline .Description)}})),
+			mcplib.WithNumber({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (mcpParamDesc .)}})),
 {{- else if eq .Type "boolean"}}
-			mcplib.WithBoolean({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (oneline .Description)}})),
+			mcplib.WithBoolean({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (mcpParamDesc .)}})),
 {{- else}}
-			mcplib.WithString({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (oneline .Description)}})),
+			mcplib.WithString({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (mcpParamDesc .)}})),
 {{- end}}
 {{- end}}
 {{- if eq (upper $endpoint.Method) "GET"}}
@@ -72,11 +72,11 @@ func RegisterTools(s *server.MCPServer) {
 			mcplib.WithDescription({{printf "%q" (composeMCPDesc $endpoint $endpoint.NoAuth $.Auth.Type $.MCPPublicCount $.MCPTotalCount)}}),
 {{- range $endpoint.Params}}
 {{- if eq .Type "integer"}}
-			mcplib.WithNumber({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (oneline .Description)}})),
+			mcplib.WithNumber({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (mcpParamDesc .)}})),
 {{- else if eq .Type "boolean"}}
-			mcplib.WithBoolean({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (oneline .Description)}})),
+			mcplib.WithBoolean({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (mcpParamDesc .)}})),
 {{- else}}
-			mcplib.WithString({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (oneline .Description)}})),
+			mcplib.WithString({{printf "%q" .Name}}{{if .Required}}, mcplib.Required(){{end}}, mcplib.Description({{printf "%q" (mcpParamDesc .)}})),
 {{- end}}
 {{- end}}
 {{- if eq (upper $endpoint.Method) "GET"}}
diff --git a/internal/mcpdesc/params.go b/internal/mcpdesc/params.go
new file mode 100644
index 00000000..cedfefd5
--- /dev/null
+++ b/internal/mcpdesc/params.go
@@ -0,0 +1,135 @@
+package mcpdesc
+
+import (
+	"strings"
+	"unicode/utf8"
+
+	"github.com/mvanhorn/cli-printing-press/v3/internal/naming"
+	"github.com/mvanhorn/cli-printing-press/v3/internal/spec"
+)
+
+const (
+	sharedParamMinOccurrences  = 3
+	sharedParamMinDescription  = 100
+	sharedParamMinSentence     = 24
+	sharedParamDescriptionMax  = 96
+	sharedParamDescriptionTail = "..."
+)
+
+// ParamDescriptionCompactor shortens descriptions only for long, repeated
+// parameter text that appears often enough to bloat endpoint-mirror MCP
+// schemas. Unique, short, and endpoint-specific descriptions pass through.
+type ParamDescriptionCompactor struct {
+	compacted map[paramDescriptionKey]string
+}
+
+type paramDescriptionKey struct {
+	name        string
+	paramType   string
+	description string
+}
+
+func NewParamDescriptionCompactor(api *spec.APISpec) *ParamDescriptionCompactor {
+	if api == nil {
+		return NewParamDescriptionCompactorForEndpoints(nil)
+	}
+	var endpoints []spec.Endpoint
+	for _, resource := range api.Resources {
+		endpoints = appendResourceEndpoints(endpoints, resource)
+	}
+	return NewParamDescriptionCompactorForEndpoints(endpoints)
+}
+
+func NewParamDescriptionCompactorForEndpoints(endpoints []spec.Endpoint) *ParamDescriptionCompactor {
+	counts := map[paramDescriptionKey]int{}
+	for _, endpoint := range endpoints {
+		countParams(counts, endpoint.Params)
+		countParams(counts, endpoint.Body)
+	}
+
+	compacted := map[paramDescriptionKey]string{}
+	for key, count := range counts {
+		if count >= sharedParamMinOccurrences && utf8.RuneCountInString(key.description) >= sharedParamMinDescription {
+			compacted[key] = compactSharedParamDescription(key.description)
+		}
+	}
+	return &ParamDescriptionCompactor{compacted: compacted}
+}
+
+func (c *ParamDescriptionCompactor) Description(p spec.Param) string {
+	description := naming.OneLineNormalize(p.Description)
+	if c == nil || description == "" {
+		return description
+	}
+	if compacted, ok := c.compacted[keyForParamDescription(p, description)]; ok {
+		return compacted
+	}
+	return description
+}
+
+func appendResourceEndpoints(endpoints []spec.Endpoint, resource spec.Resource) []spec.Endpoint {
+	for _, endpoint := range resource.Endpoints {
+		endpoints = append(endpoints, endpoint)
+	}
+	for _, subResource := range resource.SubResources {
+		endpoints = appendResourceEndpoints(endpoints, subResource)
+	}
+	return endpoints
+}
+
+func countParams(counts map[paramDescriptionKey]int, params []spec.Param) {
+	for _, p := range params {
+		description := naming.OneLineNormalize(p.Description)
+		if description == "" {
+			continue
+		}
+		counts[keyForParamDescription(p, description)]++
+	}
+}
+
+func keyForParamDescription(p spec.Param, description string) paramDescriptionKey {
+	return paramDescriptionKey{
+		name:        strings.ToLower(strings.TrimSpace(p.Name)),
+		paramType:   normalizeParamType(p.Type),
+		description: description,
+	}
+}
+
+func normalizeParamType(paramType string) string {
+	normalized := strings.ToLower(strings.TrimSpace(paramType))
+	if normalized == "" {
+		return "string"
+	}
+	return normalized
+}
+
+func compactSharedParamDescription(description string) string {
+	if first := firstSentence(description); len(first) >= sharedParamMinSentence && len(first) <= sharedParamDescriptionMax {
+		return first
+	}
+	return truncateSharedParamDescription(description)
+}
+
+func firstSentence(description string) string {
+	for i, r := range description {
+		if r == '.' || r == '!' || r == '?' {
+			return strings.TrimSpace(description[:i+1])
+		}
+	}
+	return ""
+}
+
+func truncateSharedParamDescription(description string) string {
+	runes := []rune(description)
+	if len(runes) <= sharedParamDescriptionMax {
+		return description
+	}
+	limit := sharedParamDescriptionMax - len(sharedParamDescriptionTail)
+	cut := limit
+	prefix := string(runes[:limit])
+	if idx := strings.LastIndexAny(prefix, " \t"); idx > 0 {
+		cut = idx
+		return strings.TrimSpace(prefix[:cut]) + sharedParamDescriptionTail
+	}
+	return strings.TrimSpace(string(runes[:cut])) + sharedParamDescriptionTail
+}
diff --git a/internal/mcpdesc/params_test.go b/internal/mcpdesc/params_test.go
new file mode 100644
index 00000000..64aa0f79
--- /dev/null
+++ b/internal/mcpdesc/params_test.go
@@ -0,0 +1,107 @@
+package mcpdesc
+
+import (
+	"strings"
+	"testing"
+	"unicode/utf8"
+
+	"github.com/mvanhorn/cli-printing-press/v3/internal/naming"
+	"github.com/mvanhorn/cli-printing-press/v3/internal/spec"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParamDescriptionCompactorPassesThroughUniqueAndShortDescriptions(t *testing.T) {
+	uniqueDescription := "Unique endpoint-specific filter text."
+	api := &spec.APISpec{
+		Resources: map[string]spec.Resource{
+			"items": {
+				Endpoints: map[string]spec.Endpoint{
+					"list": {
+						Params: []spec.Param{
+							{Name: "expand", Type: "string", Description: uniqueDescription},
+							{Name: "limit", Type: "integer", Description: "Max items to return"},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	compactor := NewParamDescriptionCompactor(api)
+
+	assert.Equal(t, uniqueDescription, compactor.Description(spec.Param{Name: "expand", Type: "string", Description: uniqueDescription}))
+	assert.Equal(t, "Max items to return", compactor.Description(spec.Param{Name: "limit", Type: "integer", Description: "Max items to return"}))
+}
+
+func TestParamDescriptionCompactorUsesFullDescriptionsForKeysAndPassThrough(t *testing.T) {
+	sharedPrefix := strings.Repeat("Shared endpoint expansion guidance ", 5)
+	ownerDescription := sharedPrefix + "Allowed values: owner, creator, updater, and permissionSummary."
+	statusDescription := sharedPrefix + "Allowed values: status, lifecycle, archived, and moderationState."
+	auditDescription := sharedPrefix + "Allowed values: createdAt, updatedAt, actor, and requestId."
+	api := &spec.APISpec{
+		Resources: map[string]spec.Resource{
+			"items": {
+				Endpoints: map[string]spec.Endpoint{
+					"list": {
+						Params: []spec.Param{{Name: "expand", Type: "string", Description: ownerDescription}},
+					},
+					"search": {
+						Params: []spec.Param{{Name: "expand", Type: "string", Description: statusDescription}},
+					},
+					"recent": {
+						Params: []spec.Param{{Name: "expand", Type: "string", Description: auditDescription}},
+					},
+				},
+			},
+		},
+	}
+
+	compactor := NewParamDescriptionCompactor(api)
+
+	assert.Equal(t, naming.OneLineNormalize(ownerDescription), compactor.Description(spec.Param{Name: "expand", Type: "string", Description: ownerDescription}))
+	assert.Equal(t, naming.OneLineNormalize(statusDescription), compactor.Description(spec.Param{Name: "expand", Type: "string", Description: statusDescription}))
+	assert.Equal(t, naming.OneLineNormalize(auditDescription), compactor.Description(spec.Param{Name: "expand", Type: "string", Description: auditDescription}))
+}
+
+func TestParamDescriptionCompactorNormalizesEmptyStringTypes(t *testing.T) {
+	description := "Select additional nested resource fields to include in the response. Use comma-separated field names such as owner, permissions, metadata, relationships, and auditTrail; unsupported values are ignored by the upstream API."
+	api := &spec.APISpec{
+		Resources: map[string]spec.Resource{
+			"items": {
+				Endpoints: map[string]spec.Endpoint{
+					"list":   {Params: []spec.Param{{Name: "expand", Description: description}}},
+					"search": {Params: []spec.Param{{Name: "expand", Type: "string", Description: description}}},
+					"recent": {Params: []spec.Param{{Name: "expand", Description: description}}},
+				},
+			},
+		},
+	}
+
+	compactor := NewParamDescriptionCompactor(api)
+
+	assert.Equal(t,
+		"Select additional nested resource fields to include in the response.",
+		compactor.Description(spec.Param{Name: "expand", Type: "string", Description: description}),
+	)
+}
+
+func TestParamDescriptionCompactorTruncatesUnicodeSafely(t *testing.T) {
+	description := strings.Repeat("cafe\u0301 metadata ", 16)
+	api := &spec.APISpec{
+		Resources: map[string]spec.Resource{
+			"items": {
+				Endpoints: map[string]spec.Endpoint{
+					"list":   {Params: []spec.Param{{Name: "expand", Type: "string", Description: description}}},
+					"search": {Params: []spec.Param{{Name: "expand", Type: "string", Description: description}}},
+					"recent": {Params: []spec.Param{{Name: "expand", Type: "string", Description: description}}},
+				},
+			},
+		},
+	}
+
+	got := NewParamDescriptionCompactor(api).Description(spec.Param{Name: "expand", Type: "string", Description: description})
+
+	assert.True(t, utf8.ValidString(got))
+	assert.LessOrEqual(t, utf8.RuneCountInString(got), sharedParamDescriptionMax)
+	assert.True(t, strings.HasSuffix(got, sharedParamDescriptionTail))
+}
diff --git a/internal/pipeline/toolsmanifest.go b/internal/pipeline/toolsmanifest.go
index 73636a63..47aff511 100644
--- a/internal/pipeline/toolsmanifest.go
+++ b/internal/pipeline/toolsmanifest.go
@@ -109,6 +109,7 @@ func WriteToolsManifest(dir string, parsed *spec.APISpec) error {
 
 	// For cookie/composed auth, only include NoAuth endpoints.
 	cookieOrComposed := parsed.Auth.Type == "cookie" || parsed.Auth.Type == "composed"
+	paramDescriptions := mcpdesc.NewParamDescriptionCompactorForEndpoints(manifestEndpoints(parsed, cookieOrComposed))
 
 	manifest := ToolsManifest{
 		APIName:       parsed.Name,
@@ -159,7 +160,7 @@ func WriteToolsManifest(dir string, parsed *spec.APISpec) error {
 				PublicCount: public,
 				TotalCount:  total,
 			})
-			tool := buildManifestTool(toolName, desc, endpoint)
+			tool := buildManifestTool(toolName, desc, endpoint, paramDescriptions.Description)
 			manifest.Tools = append(manifest.Tools, tool)
 		}
 
@@ -181,7 +182,7 @@ func WriteToolsManifest(dir string, parsed *spec.APISpec) error {
 					PublicCount: public,
 					TotalCount:  total,
 				})
-				tool := buildManifestTool(toolName, desc, endpoint)
+				tool := buildManifestTool(toolName, desc, endpoint, paramDescriptions.Description)
 				manifest.Tools = append(manifest.Tools, tool)
 			}
 		}
@@ -201,7 +202,7 @@ func WriteToolsManifest(dir string, parsed *spec.APISpec) error {
 
 // buildManifestTool creates a ManifestTool from an endpoint, classifying
 // each parameter's location.
-func buildManifestTool(name, description string, ep spec.Endpoint) ManifestTool {
+func buildManifestTool(name, description string, ep spec.Endpoint, describeParam func(spec.Param) string) ManifestTool {
 	tool := ManifestTool{
 		Name:        name,
 		Description: description,
@@ -229,7 +230,7 @@ func buildManifestTool(name, description string, ep spec.Endpoint) ManifestTool
 			Name:        p.Name,
 			Type:        normalizeParamType(p.Type),
 			Location:    loc,
-			Description: p.Description,
+			Description: describeParam(p),
 			Required:    p.Required,
 		})
 	}
@@ -240,7 +241,7 @@ func buildManifestTool(name, description string, ep spec.Endpoint) ManifestTool
 			Name:        p.Name,
 			Type:        normalizeParamType(p.Type),
 			Location:    "body",
-			Description: p.Description,
+			Description: describeParam(p),
 			Required:    p.Required,
 		})
 	}
@@ -259,6 +260,25 @@ func buildManifestTool(name, description string, ep spec.Endpoint) ManifestTool
 	return tool
 }
 
+func manifestEndpoints(parsed *spec.APISpec, cookieOrComposed bool) []spec.Endpoint {
+	var endpoints []spec.Endpoint
+	for _, resource := range parsed.Resources {
+		for _, endpoint := range resource.Endpoints {
+			if !cookieOrComposed || endpoint.NoAuth {
+				endpoints = append(endpoints, endpoint)
+			}
+		}
+		for _, subResource := range resource.SubResources {
+			for _, endpoint := range subResource.Endpoints {
+				if !cookieOrComposed || endpoint.NoAuth {
+					endpoints = append(endpoints, endpoint)
+				}
+			}
+		}
+	}
+	return endpoints
+}
+
 // normalizeAuthFormat rewrites the auth format string so that derived
 // placeholders (like {token} from DUB_TOKEN) become the actual env var
 // name ({DUB_TOKEN}). This way the mega MCP's runtime expansion only needs
diff --git a/internal/pipeline/toolsmanifest_test.go b/internal/pipeline/toolsmanifest_test.go
index 88499b30..69f98650 100644
--- a/internal/pipeline/toolsmanifest_test.go
+++ b/internal/pipeline/toolsmanifest_test.go
@@ -6,6 +6,7 @@ import (
 	"path/filepath"
 	"testing"
 
+	"github.com/mvanhorn/cli-printing-press/v3/internal/naming"
 	"github.com/mvanhorn/cli-printing-press/v3/internal/spec"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -286,6 +287,89 @@ func TestWriteToolsManifest_ReclassifiedPathParamKeepsPathLocation(t *testing.T)
 	assert.False(t, calendar.Required, "default fills in if omitted; agent may skip the param")
 }
 
+func TestWriteToolsManifest_CompactsRepeatedParamDescriptions(t *testing.T) {
+	dir := t.TempDir()
+	sharedDescription := "Select additional nested resource fields to include in the response. Use comma-separated field names such as owner, permissions, metadata, relationships, and auditTrail; unsupported values are ignored by the upstream API."
+	parsed := &spec.APISpec{
+		Name:    "test-api",
+		BaseURL: "https://api.test.com",
+		Auth:    spec.AuthConfig{Type: "none"},
+		Resources: map[string]spec.Resource{
+			"Items": {
+				Endpoints: map[string]spec.Endpoint{
+					"List": {
+						Method:      "GET",
+						Path:        "/items",
+						Description: "List items",
+						Params:      []spec.Param{{Name: "expand", Type: "string", Description: sharedDescription}},
+					},
+					"Search": {
+						Method:      "GET",
+						Path:        "/items/search",
+						Description: "Search items",
+						Params:      []spec.Param{{Name: "expand", Type: "string", Description: sharedDescription}},
+					},
+					"Recent": {
+						Method:      "GET",
+						Path:        "/items/recent",
+						Description: "List recent items",
+						Params:      []spec.Param{{Name: "expand", Type: "string", Description: sharedDescription}},
+					},
+				},
+			},
+		},
+	}
+
+	require.NoError(t, WriteToolsManifest(dir, parsed))
+
+	data, err := os.ReadFile(filepath.Join(dir, ToolsManifestFilename))
+	require.NoError(t, err)
+	assert.NotContains(t, string(data), sharedDescription,
+		"tools-manifest.json should not repeat long shared parameter descriptions verbatim")
+
+	var got ToolsManifest
+	require.NoError(t, json.Unmarshal(data, &got))
+	require.Len(t, got.Tools, 3)
+	for _, tool := range got.Tools {
+		require.Len(t, tool.Params, 1)
+		assert.Equal(t, "Select additional nested resource fields to include in the response.", tool.Params[0].Description)
+	}
+}
+
+func TestWriteToolsManifest_PreservesUniqueLongParamDescriptions(t *testing.T) {
+	dir := t.TempDir()
+	description := "Filter records by a curated vendor-specific field path, including deeply nested owner metadata, lifecycle state, and audit fields. Allowed values: owner.profile.email, lifecycle.status, audit.actor, audit.requestId."
+	parsed := &spec.APISpec{
+		Name:    "test-api",
+		BaseURL: "https://api.test.com",
+		Auth:    spec.AuthConfig{Type: "none"},
+		Resources: map[string]spec.Resource{
+			"Items": {
+				Endpoints: map[string]spec.Endpoint{
+					"List": {
+						Method:      "GET",
+						Path:        "/items",
+						Description: "List items",
+						Params:      []spec.Param{{Name: "filter", Type: "string", Description: description}},
+					},
+				},
+			},
+		},
+	}
+
+	require.NoError(t, WriteToolsManifest(dir, parsed))
+
+	data, err := os.ReadFile(filepath.Join(dir, ToolsManifestFilename))
+	require.NoError(t, err)
+
+	var got ToolsManifest
+	require.NoError(t, json.Unmarshal(data, &got))
+	require.Len(t, got.Tools, 1)
+	require.Len(t, got.Tools[0].Params, 1)
+	assert.Equal(t, naming.OneLineNormalize(description), got.Tools[0].Params[0].Description)
+	assert.Contains(t, got.Tools[0].Params[0].Description, "audit.requestId")
+}
+
 func TestWriteToolsManifest_AuthConfigRoundTrip(t *testing.T) {
 	dir := t.TempDir()
 	parsed := &spec.APISpec{

← 847f7c5b fix(skills): tighten publish and polish workflows (#568)  ·  back to Cli Printing Press  ·  feat(cli): add spec-driven tier routing (#570) 5bc73909 →