mcpcli

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT Imports: 17 Imported by: 0

README

mcpcli

Turn supplied MCP tool schemas into usable Go command-line interfaces. Cobra owns commands, flags, help, and shell completion; the official MCP Go SDK defines tools/results; Google's JSON Schema library validates arguments. The application owns authentication, transports, discovery, and domain behavior.

go get github.com/OlegHQ/mcpcli

Integrate

root, err := mcpcli.NewCommand(mcpcli.Options{
    Name: "nudge",
    Description: "Work with Nudge",
    Tools: tools, // []*mcp.Tool, available offline
    Invoke: func(ctx context.Context, name string, args json.RawMessage) (*mcp.CallToolResult, error) {
        return session.CallTool(ctx, &mcp.CallToolParams{Name: name, Arguments: args})
    },
    Bindings: map[string]mcpcli.Binding{
        "update_issue": {
            Path: []string{"issue", "update"},
            Positionals: []string{"id"},
            Flags: map[string]string{"patch.title": "title"},
            Columns: []mcpcli.Column{{Header: "ID", Path: "id"}},
        },
    },
})

The supplied schemas produce flag types, descriptions, string-enum completions, and validation. Explicit mappings make human command names intentional. Unmapped tools retain their tool name converted to kebab-case. AddCommands(root, options) adds generated commands to an existing Cobra tree and rejects command/flag collisions, including existing aliases and ancestor authentication/configuration flags. Treat a construction error as fatal and discard that command tree; construction may have already added preceding valid commands.

nudge issue update ISSUE-ID --title 'New title'
nudge issue update --input patch.json
cat patch.json | nudge issue update --input - --output json
nudge issue update --help

Help and completion do not call Invoke. Build a new command tree for each execution, as with ordinary mutable Cobra command objects. The executable controls exit codes: an execution error must produce a nonzero exit. errors.Is(err, mcpcli.ErrTool) distinguishes an MCP isError result.

Run the self-contained example:

go run ./examples/echo echo 'Hello'
go run ./examples/echo echo 'Hello' --output json

Inputs

Only changed flags and supplied positionals become arguments; schema defaults are not eagerly copied. Primitive fields accept typed flag values. Inline nested objects produce flags such as --patch-title; Binding.Flags can rename them. Ordinary collections do not require JSON:

# An array of strings: one flag per value, preserving commas and empty strings.
nudge issue update ISSUE-ID --patch-labels bug --patch-labels 'customer,urgent'

# Explicit empty collection and null are distinct from omission and literal text.
nudge issue update ISSUE-ID --clear-patch-labels --unset-patch-assignee
nudge issue update ISSUE-ID --patch-assignee null  # literal string "null"

Arrays of strings, integers, numbers, or booleans accept repeated flags. Values are not split at commas. Integers retain 64-bit precision, false stays false, and an empty string remains one string element. --clear-FIELD supplies an empty array or object; --unset-FIELD supplies null for a nullable schema. Omit these flags to preserve a field. Do not pass =false to a clear/unset switch: those are actions, not stored booleans.

Arrays of objects

Child flags use a zero-based INDEX=VALUE argument. This creates ordinary Cobra flags with ordinary help and completion; there is no shell-command or dynamic-flag rewriting. For a tool accepting issues with title, labels, and assignee properties:

nudge bulk-create-issues \
  --issues-title '0=Fix retries' \
  --issues-title '1=Document recovery' \
  --issues-labels '0=bug' --issues-labels '0=urgent' \
  --clear-issues-labels 1 \
  --unset-issues-assignee 1

The indices select objects, so repeated scalar-array flags at the same index append elements to that object's array. Supply every required property of each object. Flag order does not matter; indices must be contiguous from zero. Use only decimal indices without a sign or leading zeroes. Everything after the first = is the value, including any further equals signs.

Nested object arrays use one dot-separated index for each surrounding array. For example, --issues-filters-field '0.1=status' sets the second filter of the first issue. --issues-filters-values '0.1=started' adds one scalar to that filter's values. The corresponding clear action is --clear-issues-filters-values 0.1. An array of arrays uses an -item child flag, for example --matrix-item '0=1' --matrix-item '0=2' --matrix-item '1=3' supplies [[1,2],[3]].

Each scalar field/index may be assigned once. Duplicate scalar flags, parent-plus-child inputs, and collection values combined with clear/unset actions fail before invocation. Helpers follow field aliases (patch.labels aliased to labels gives --clear-labels); indexed binding paths use [], for example issues.[].title. Helper names participate in the same collision checks as other flags.

Optional JSON input

Full-array JSON flag values remain supported for compatibility, for example --patch-labels '["bug","urgent"]'. A string that is itself a valid JSON array is interpreted as a full-array value; use optional whole-object input when such text must be a literal array element. A full-array value cannot be combined with repeated elements for the same field. Parent object/complex-array flags also retain their JSON form.

--input FILE accepts one complete JSON object; --input - reads stdin. It cannot combine with argument flags or positionals. The default maximum input is 20,000,000 bytes, configurable through Options.MaxInputBytes. Input JSON numbers retain their original representation when passed to Invoke. Validation uses native 64-bit integers and floating-point numbers; larger integers/out-of-range numbers fail explicitly.

The complete schema is validated by github.com/google/jsonschema-go, including properties not expanded into flags. Child flag discovery is bounded to eight nested levels. Nested properties reached only through $ref or unions do not become flattened child flags. $ref, unions, and more complex nested schemas can use the parent JSON flag or --input; this library does not invent a competing JSON Schema engine. Property names containing dots should use whole-object input, because binding/column paths use dot notation. Schema flags conflicting with reserved names (input, output, help), generated helpers, or inherited flags must be renamed through bindings. For example, alias a tool’s url property to resource-url when the application already uses --url for its server endpoint. Command and flag names must start with an ASCII letter or digit and contain only letters, digits, hyphens, and underscores.

Outputs and errors

--output table renders human output with text/tabwriter. Binding.Columns selects stable useful fields; otherwise scalar keys are displayed. Common data and items envelopes are unwrapped for tables, with nextCursor displayed. Terminal control characters in human output are sanitized.

--output json emits the complete MCP CallToolResult, preserving both structuredContent and every content block. This is intentionally not a lossy text-only projection. Machine consumers can inspect .structuredContent directly.

Successful results go to Cobra's stdout writer. MCP tool errors go to its stderr writer and return ErrTool; transport errors propagate to the application's error boundary. No retry, prompt, credential logging, or automatic pagination occurs. Applications should sanitize private transport errors before exposing them. Render(writer, result, format, columns) is also available to existing commands.

The library installs --output only if it is not already inherited or present. Options.OutputFormat can supply an application resolver, for example to implement auto using its own terminal detection. It must return table or json.

Design references

Development

go test ./...
go vet ./...

MIT licensed. This library does not embed server credentials, discover remote servers implicitly, execute shell strings, or require a daemon.

Documentation

Overview

Package mcpcli adapts MCP tool schemas to Cobra commands. Applications own authentication, discovery, transport, and domain-specific presentation.

Index

Constants

This section is empty.

Variables

View Source
var ErrTool = errors.New("MCP tool failed")

Functions

func AddCommands

func AddCommands(root *cobra.Command, opts Options) error

func NewCommand

func NewCommand(opts Options) (*cobra.Command, error)

func Render

func Render(w io.Writer, result *mcp.CallToolResult, format string, columns []Column) error

Render emits the complete MCP result in JSON mode. Human mode presents structured content where available; applications supply useful column paths.

Types

type Binding

type Binding struct {
	Path        []string
	Positionals []string
	Flags       map[string]string
	Columns     []Column
}

type Column

type Column struct{ Header, Path string }

type Options

type Options struct {
	Name, Description, Version string
	Tools                      []*mcp.Tool
	Invoke                     InvokeFunc
	Bindings                   map[string]Binding
	// MaxInputBytes bounds --input files/stdin; zero uses 20,000,000 bytes.
	MaxInputBytes int64
	OutputFormat  func(*cobra.Command) string
}

Directories

Path Synopsis
examples
echo command
Example: an offline schema supplies CLI help; Invoke supplies execution.
Example: an offline schema supplies CLI help; Invoke supplies execution.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL