ophis

package module
v1.1.4 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2026 License: Apache-2.0 Imports: 23 Imported by: 2

README

Project Logo

Transform any Cobra CLI into an MCP server

Ophis automatically converts your Cobra commands into MCP tools, and provides CLI commands for integration with Claude Desktop, VSCode, and Cursor.

Quick Start

Install

go get github.com/njayp/ophis

Add to your CLI

package main

import (
    "os"
    "github.com/njayp/ophis"
)

func main() {
    rootCmd := createMyRootCommand()
    rootCmd.AddCommand(ophis.Command(nil))

    if err := rootCmd.Execute(); err != nil {
        os.Exit(1)
    }
}

Enable in Claude Desktop, VSCode, or Cursor

# Claude Desktop
./my-cli mcp claude enable
# Restart Claude Desktop

# VSCode (requires Copilot in Agent Mode)
./my-cli mcp vscode enable

# Cursor
./my-cli mcp cursor enable

Your CLI commands are now available as MCP tools!

Stream over HTTP

Expose your MCP server over HTTP for remote access:

./my-cli mcp stream --host localhost --port 8080

Commands

The ophis.Command(nil) adds these subcommands to your CLI (the default command name is mcp, configurable via Config.CommandName):

mcp
├── start            # Start MCP server on stdio
├── stream           # Stream MCP server over HTTP
├── tools            # Export available MCP tools as JSON
├── claude
│   ├── enable       # Add server to Claude Desktop config
│   ├── disable      # Remove server from Claude Desktop config
│   └── list         # List Claude Desktop MCP servers
├── vscode
│   ├── enable       # Add server to VSCode config
│   ├── disable      # Remove server from VSCode config
│   └── list         # List VSCode MCP servers
└── cursor
    ├── enable       # Add server to Cursor config
    ├── disable      # Remove server from Cursor config
    └── list         # List Cursor MCP servers

Configuration

Control which commands and flags are exposed as MCP tools using selectors. By default, all commands and flags are exposed (except hidden/deprecated).

config := &ophis.Config{
    Selectors: []ophis.Selector{
        {
            CmdSelector: ophis.AllowCmdsContaining("get", "list"),
            LocalFlagSelector: ophis.ExcludeFlags("token", "secret"),
            InheritedFlagSelector: ophis.NoFlags,  // Exclude persistent flags

            // Middleware wraps command execution
            Middleware: func(ctx context.Context, req *mcp.CallToolRequest, in ophis.ToolInput, next func(context.Context, *mcp.CallToolRequest, ophis.ToolInput) (*mcp.CallToolResult, ophis.ToolOutput, error)) (*mcp.CallToolResult, ophis.ToolOutput, error) {
                ctx, cancel := context.WithTimeout(ctx, time.Minute)
                defer cancel()
                return next(ctx, req, in)
            },
        },
    },
}

rootCmd.AddCommand(ophis.Command(config))

Custom Command Name

By default the ophis command is named mcp. If your CLI already uses mcp for something else, set CommandName to avoid the collision:

config := &ophis.Config{
    CommandName: "agent",
}

rootCmd.AddCommand(ophis.Command(config))

The command tree, editor config (enable/disable), and internal filters all use the configured name automatically.

Default Environment Variables

Editors launch MCP server subprocesses with a minimal environment. On macOS this means a PATH of just /usr/bin:/bin:/usr/sbin:/sbin, so tools like helm, kubectl, or docker installed via mise/homebrew/nix won't be found. Use DefaultEnv to capture the current PATH (or any other variables) at enable time:

config := &ophis.Config{
    DefaultEnv: map[string]string{
        "PATH": os.Getenv("PATH"),
    },
}

rootCmd.AddCommand(ophis.Command(config))

These are merged into the editor config written by enable. User-provided --env values take precedence on conflict.

See docs/config.md for detailed configuration options.

How It Works

Ophis bridges Cobra commands and the Model Context Protocol:

  1. Command Discovery: Recursively walks your Cobra command tree
  2. Schema Generation: Creates JSON schemas from command flags and arguments (docs/schema.md)
  3. Tool Execution: Spawns your CLI as a subprocess and captures output (docs/execution.md)

Contributing

Contributions welcome! See CONTRIBUTING.md.

Documentation

Overview

Package ophis transforms Cobra CLI applications into MCP (Model Context Protocol) servers, enabling AI assistants to interact with command-line tools.

Ophis automatically converts existing Cobra commands into MCP tools, handling protocol complexity, command execution, and tool registration.

Basic Usage

Add MCP server functionality to your existing Cobra CLI application:

package main

import (
    "os"
    "github.com/njayp/ophis"
)

func main() {
    rootCmd := createMyRootCommand()

    // Add MCP server commands
    rootCmd.AddCommand(ophis.Command(nil))

    if err := rootCmd.Execute(); err != nil {
        os.Exit(1)
    }
}

This adds the following subcommands to your CLI:

  • mcp start: Start the MCP server
  • mcp tools: List available tools
  • mcp claude enable/disable/list: Manage Claude Desktop integration
  • mcp vscode enable/disable/list: Manage VSCode integration

Integration

Enable MCP support in Claude Desktop or VSCode:

# Claude Desktop
./my-cli mcp claude enable

# VSCode (requires Copilot in Agent Mode)
./my-cli mcp vscode enable

Configuration

The Config struct provides fine-grained control over which commands and flags are exposed as MCP tools through a powerful selector system.

Basic filters are always applied automatically:

  • Hidden and deprecated commands/flags are excluded
  • Commands without executable functions are excluded
  • Built-in commands (mcp, help, completion) are excluded

Your selectors add additional filtering on top:

config := &ophis.Config{
    // Selectors are evaluated in order - first match wins
    Selectors: []ophis.Selector{
        {
            CmdSelector: ophis.AllowCmdsContaining("get", "list"),
            // Control which flags are included for matched commands
            LocalFlagSelector: ophis.AllowFlags("namespace", "output"),
            InheritedFlagSelector: ophis.NoFlags,  // Exclude persistent flags
            // Optional: Add middleware to wrap execution
            Middleware: func(ctx context.Context, req *mcp.CallToolRequest, in ophis.ToolInput, next func(context.Context, *mcp.CallToolRequest, ophis.ToolInput) (*mcp.CallToolResult, ophis.ToolOutput, error)) (*mcp.CallToolResult, ophis.ToolOutput, error) {
                // Pre-execution: timeout, logging, auth checks, etc.
                ctx, cancel := context.WithTimeout(ctx, time.Minute)
                defer cancel()
                // Execute the command
                res, out, err := next(ctx, req, in)
                // Post-execution: error handling, response filtering, metrics
                return res, out, err
            },
        },
        {
            CmdSelector: ophis.AllowCmds("mycli delete"),
            LocalFlagSelector: ophis.ExcludeFlags("all", "force"),
            InheritedFlagSelector: ophis.NoFlags,
        },
    },

    // Configure logging
    SloggerOptions: &slog.HandlerOptions{
        Level: slog.LevelDebug,
    },
}

The selector system allows different commands to have different flag filtering rules and middleware, enabling precise control over the exposed tool surface. Each selector defines which commands to match, which flags to include, and optional middleware for wrapping execution with timeouts, logging, and response filtering.

Index

Constants

View Source
const (
	// AnnotationTitle sets the human-readable title for the tool.
	AnnotationTitle = "title"

	// AnnotationReadOnly hints that the tool does not modify its environment.
	AnnotationReadOnly = "readOnlyHint"

	// AnnotationDestructive hints that the tool may perform destructive updates.
	// Only meaningful when ReadOnlyHint is false.
	AnnotationDestructive = "destructiveHint"

	// AnnotationIdempotent hints that calling the tool repeatedly with the same
	// arguments has no additional effect. Only meaningful when ReadOnlyHint is false.
	AnnotationIdempotent = "idempotentHint"

	// AnnotationOpenWorld hints that the tool may interact with external entities
	// outside its closed domain.
	AnnotationOpenWorld = "openWorldHint"
)

Cobra command annotation keys for MCP tool annotations. Set these in cmd.Annotations to populate mcp.ToolAnnotations on the generated tool.

Boolean values are parsed with strconv.ParseBool (accepts "1", "t", "true", "0", "f", "false", etc.).

Example:

cmd.Annotations = map[string]string{
    ophis.AnnotationReadOnly: "true",
    ophis.AnnotationTitle:    "List files",
}

Variables

This section is empty.

Functions

func Command

func Command(config *Config) *cobra.Command

Command creates MCP server management commands for a Cobra CLI. Pass nil for default configuration or provide a Config for customization.

func NoFlags added in v0.7.0

func NoFlags(_ *pflag.Flag) bool

NoFlags is a FlagSelector that excludes all flags.

Types

type CmdSelector added in v0.6.0

type CmdSelector func(*cobra.Command) bool

CmdSelector determines if a command should become an MCP tool. Return true to include the command as a tool. Note: Basic safety filters (hidden, deprecated, non-runnable) are always applied first. Commands are tested against selectors in order; the first matching selector wins.

func AllowCmds added in v1.0.0

func AllowCmds(cmds ...string) CmdSelector

AllowCmds creates a selector that only accepts commands whose path is listed. Example: AllowCmds("kubectl get", "helm list") includes only those exact commands.

func AllowCmdsContaining added in v1.0.0

func AllowCmdsContaining(substrings ...string) CmdSelector

AllowCmdsContaining creates a selector that only accepts commands whose path contains a listed phrase. Example: AllowCmdsContaining("get", "helm list") includes "kubectl get pods" and "helm list".

func ExcludeCmds added in v1.0.0

func ExcludeCmds(cmds ...string) CmdSelector

ExcludeCmds creates a selector that rejects commands whose path is listed. Example: ExcludeCmds("kubectl delete", "helm uninstall") excludes those exact commands.

func ExcludeCmdsContaining added in v1.0.0

func ExcludeCmdsContaining(substrings ...string) CmdSelector

ExcludeCmdsContaining creates a selector that rejects commands whose path contains any listed phrase. Example: ExcludeCmdsContaining("kubectl delete", "admin") excludes "kubectl delete" and "cli admin user".

type Config

type Config struct {
	// CommandName is the Use name for the top-level command returned by Command().
	// It is also used by GetCmdPath to locate the ophis command in the Cobra tree
	// and by cmdFilter to exclude ophis subcommands from tool exposure.
	// Default: "mcp".
	CommandName string

	// Selectors defines rules for converting commands to MCP tools.
	// Each selector specifies which commands to match and which flags to include.
	//
	// Basic safety filters are always applied first:
	//   - Hidden/deprecated commands and flags are excluded
	//   - Non-runnable commands are excluded
	//   - Built-in commands (the ophis command, help, completion) are excluded
	//
	// Then selectors are evaluated in order for each command:
	//   1. The first selector whose CmdSelector returns true is used
	//   2. That selector's FlagSelector determines which flags are included
	//   3. If no selectors match, the command is not exposed as a tool
	//
	// If nil or empty, defaults to exposing all commands with all flags.
	Selectors []Selector

	// DefaultEnv specifies environment variables that are automatically
	// included when `enable` writes a server config for any editor.
	// These are merged with user-provided --env values; user values
	// take precedence on conflict.
	//
	// A common use is to capture PATH so the MCP server subprocess can
	// find executables that live outside the system PATH:
	//
	//   &ophis.Config{
	//       DefaultEnv: map[string]string{
	//           "PATH": os.Getenv("PATH"),
	//       },
	//   }
	//
	// If nil, no default environment variables are added (current behavior).
	DefaultEnv map[string]string

	// ToolNamePrefix replaces the root command name in tool names.
	// This is useful for shortening tool names to comply with API limits (e.g., Claude's 64 char limit).
	// For example, if root command is "omnistrate-ctl" and ToolNamePrefix is "omctl",
	// a command "omnistrate-ctl cost by-cell list" becomes "omctl_cost_by-cell_list" instead of
	// "omnistrate-ctl_cost_by-cell_list".
	// If empty, the root command name is used as-is.
	ToolNamePrefix string

	// SloggerOptions configures logging to stderr.
	// Default: Info level logging.
	SloggerOptions *slog.HandlerOptions

	// ServerOptions for the underlying MCP server.
	ServerOptions *mcp.ServerOptions

	// Transport for stdio transport configuration.
	Transport mcp.Transport
	// contains filtered or unexported fields
}

Config customizes MCP server behavior and command-to-tool conversion.

type ExecuteFunc added in v1.1.0

ExecuteFunc defines the function signature for executing a tool.

type FlagSelector added in v0.6.0

type FlagSelector func(*pflag.Flag) bool

FlagSelector determines if a flag should be included in an MCP tool. Return true to include the flag. Note: Hidden and deprecated flags are always excluded regardless of this selector. This selector is only applied to commands that match the associated CmdSelector.

func AllowFlags added in v1.0.0

func AllowFlags(names ...string) FlagSelector

AllowFlags creates a selector that only accepts flags whose name is listed. Example: AllowFlags("namespace", "output") includes only flags named "namespace" and "output".

func ExcludeFlags added in v1.0.0

func ExcludeFlags(names ...string) FlagSelector

ExcludeFlags creates a selector that rejects flags whose name is listed. Example: ExcludeFlags("color", "kubeconfig") excludes flags named "color" and "kubeconfig".

type MiddlewareFunc added in v1.1.0

MiddlewareFunc is middleware hook that runs after each tool call Common uses: error handling, response filtering, metrics collection.

type Selector added in v0.6.0

type Selector struct {
	// CmdSelector determines if this selector applies to a command.
	// If nil, accepts all commands that pass basic safety filters.
	// Cannot be used to bypass safety filters (hidden, deprecated, non-runnable).
	CmdSelector CmdSelector

	// LocalFlagSelector determines which flags to include for commands matched by CmdSelector.
	// If nil, includes all flags that pass basic safety filters.
	// Cannot be used to bypass safety filters (hidden, deprecated flags).
	LocalFlagSelector FlagSelector

	// InheritedFlagSelector determines which persistent flags to include for commands matched by CmdSelector.
	// If nil, includes all flags that pass basic safety filters.
	// Cannot be used to bypass safety filters (hidden, deprecated flags).
	InheritedFlagSelector FlagSelector

	// Middleware is an optional middleware hook that wraps around tool execution.
	// Common uses: error handling, response filtering, metrics collection.
	// If nil, no middleware is applied.
	Middleware MiddlewareFunc
}

Selector contains selectors for filtering commands and flags. When multiple selectors are configured, they are evaluated in order. The first selector whose CmdSelector matches a command is used, and its FlagSelector determines which flags are included for that command.

Basic safety filters are always applied automatically:

  • Hidden/deprecated commands and flags are excluded
  • Non-runnable commands are excluded
  • Built-in commands (mcp, help, completion) are excluded

This allows fine-grained control within safe boundaries, such as:

  • Exposing different flags for different command groups
  • Applying stricter flag filtering to dangerous commands
  • Having a default catch-all selector with common flag exclusions

type ToolInput added in v1.0.7

type ToolInput struct {
	Flags map[string]any `json:"flags" jsonschema:"Command line flags"`
	Args  []string       `json:"args,omitempty" jsonschema:"Positional command line arguments"`
}

ToolInput represents the input structure for command tools. Do not `omitempty` the Flags field, there may be required flags inside.

type ToolOutput added in v1.0.7

type ToolOutput struct {
	StdOut   string `json:"stdout,omitempty" jsonschema:"Standard output"`
	StdErr   string `json:"stderr,omitempty" jsonschema:"Standard error"`
	ExitCode int    `json:"exitCode" jsonschema:"Exit code"`
}

ToolOutput represents the output structure for command tools.

Directories

Path Synopsis
examples module
internal
bridge/flags
Package flags provides JSON schema generation from Cobra command flags.
Package flags provides JSON schema generation from Cobra command flags.
cfgmgr/cmd/claude
Package claude provides CLI commands for managing Claude Desktop MCP servers.
Package claude provides CLI commands for managing Claude Desktop MCP servers.
cfgmgr/cmd/cursor
Package cursor provides CLI commands for managing Cursor MCP servers.
Package cursor provides CLI commands for managing Cursor MCP servers.
cfgmgr/cmd/vscode
Package vscode provides CLI commands for managing VSCode MCP servers.
Package vscode provides CLI commands for managing VSCode MCP servers.
cfgmgr/manager
Package manager provides configuration management for MCP servers across platforms.
Package manager provides configuration management for MCP servers across platforms.
cfgmgr/manager/claude
Package claude provides configuration management for Claude Desktop MCP servers.
Package claude provides configuration management for Claude Desktop MCP servers.
cfgmgr/manager/cursor
Package cursor provides configuration management for Cursor MCP servers.
Package cursor provides configuration management for Cursor MCP servers.
cfgmgr/manager/vscode
Package vscode provides configuration management for VSCode MCP servers.
Package vscode provides configuration management for VSCode MCP servers.
schema
Package schema provides JSON schema caching utilities.
Package schema provides JSON schema caching utilities.
Package test provides integration testing helper functions for ophis It assumes ophis.Command is a root level subcommand
Package test provides integration testing helper functions for ophis It assumes ophis.Command is a root level subcommand

Jump to

Keyboard shortcuts

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