charmbracelet/crush · error

error parsing parameters: %s

Error message

error parsing parameters: %s

What it means

RunTool is the entry point for invoking an MCP tool; it expects `input` to be a JSON object that unmarshals into map[string]any. If json.Unmarshal fails (malformed JSON, or valid JSON that is not an object such as an array or string), the parse error is returned as `error parsing parameters: ...` and the tool is never called.

Source

Thrown at internal/agent/tools/mcp/tools.go:39

type ToolResult struct {
	Type      string
	Content   string
	Data      []byte
	MediaType string
}

var allTools = csync.NewMap[string, []*Tool]()

// Tools returns all available MCP tools.
func Tools() iter.Seq2[string, []*Tool] {
	return allTools.Seq2()
}

// RunTool runs an MCP tool with the given input parameters.
func RunTool(ctx context.Context, cfg *config.ConfigStore, name, toolName string, input string) (ToolResult, error) {
	var args map[string]any
	if err := json.Unmarshal([]byte(input), &args); err != nil {
		return ToolResult{}, fmt.Errorf("error parsing parameters: %s", err)
	}

	c, err := getOrRenewClient(ctx, cfg, name)
	if err != nil {
		return ToolResult{}, err
	}
	result, err := c.CallTool(ctx, &mcp.CallToolParams{
		Name:      toolName,
		Arguments: args,
	})
	if err != nil {
		return ToolResult{}, err
	}

	if len(result.Content) == 0 {
		return ToolResult{Type: "text", Content: ""}, nil
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Marshal arguments with json.Marshal from a map[string]any/struct instead of hand-building the JSON string
  2. Validate the input parses as a JSON object before calling RunTool (json.Valid or an Unmarshal into map[string]any)
  3. Strip markdown fences or prose the model may have wrapped around the JSON
  4. Check for trailing commas/comments — Go's encoding/json rejects them; emit strict JSON

Example fix

// before
input := fmt.Sprintf(`{"path": %q}`, path) // breaks on quotes
// after
args, _ := json.Marshal(map[string]any{"path": path})
result, err := mcp.RunTool(ctx, cfg, name, toolName, string(args))
Defensive patterns

Strategy: validation

Validate before calling

var probe map[string]any
if err := json.Unmarshal([]byte(input), &probe); err != nil {
    return fmt.Errorf("tool input is not a JSON object: %w", err)
}
result, err := mcp.RunTool(ctx, cfg, name, toolName, input)

Type guard

func isJSONObject(s string) bool {
    var m map[string]any
    return json.Unmarshal([]byte(s), &m) == nil && m != nil
}

Try / catch

result, err := mcp.RunTool(ctx, cfg, name, toolName, input)
var synthErr *json.SyntaxError
if errors.As(err, &synthErr) || strings.HasPrefix(err.Error(), "error parsing parameters") {
    // re-marshal arguments from a typed struct instead of retrying raw
    input = mustMarshalJSON(argsStruct)
    result, err = mcp.RunTool(ctx, cfg, name, toolName, input)
}

Prevention

When it happens

Trigger: Calling mcp.RunTool (or the agent invoking an MCP tool) with input that is not a JSON object: raw text, truncated JSON, single quotes instead of double quotes, a JSON array or bare string, or unescaped newlines/control characters inside strings.

Common situations: LLM emits pseudo-JSON with trailing commas or comments; tool arguments built by string concatenation instead of marshalling; upstream caller passes the raw model output without validating it is an object first.

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/67135563edb49359. Report an issue: GitHub.