Swift Argument Parser MCP

CI Swift Platforms Documentation License Support on Boosty

Turn any Swift CLI into an MCP server in minutes.

ArgumentParserMCP lets AI agents call your Swift Argument Parser commands as tools via the Model Context Protocol. Add one conformance, register your commands, and your CLI is ready for Claude, Cursor, and other MCP clients.

Usage

Start with a regular Argument Parser command and conform it to MCPCommand:

import ArgumentParser
import ArgumentParserMCP

struct RepeatPhrase: ParsableCommand, MCPCommand {

    static let configuration = CommandConfiguration(
        abstract: "Repeat a phrase multiple times."
    )

    @Flag(help: "Include a counter with each repetition.")
    var includeCounter = false

    @Option(name: .shortAndLong, help: "How many times to repeat 'phrase'.")
    var count: Int? = nil

    @Argument(help: "The phrase to repeat.")
    var phrase: String

    mutating func run() throws {
        let repeatCount = count ?? 2
        for i in 1...repeatCount {
            if includeCounter {
                print("\(i): \(phrase)")
            } else {
                print(phrase)
            }
        }
    }
}

Then add a subcommand that starts the MCP server:

import ArgumentParser
import ArgumentParserMCP

struct MCP: AsyncParsableCommand {

    static let configuration = CommandConfiguration(
        abstract: "MCP Server for AI agents"
    )

    mutating func run() async throws {
        let server = MCPServer(
            name: "my-cli",
            version: "1.0.0",
            commands: [RepeatPhrase.self]
        )
        try await server.start()
    }
}

Register both subcommands in your root command:

@main
struct MyCLI: AsyncParsableCommand {

    static let configuration = CommandConfiguration(
        subcommands: [
            RepeatPhrase.self,
            MCP.self,
        ]
    )
}

That's it. Your CLI now speaks MCP over stdio when invoked with the mcp subcommand:

$ my-cli mcp

The library automatically introspects your CLI via --experimental-dump-help, generates JSON Schema for every registered command, and dispatches tool calls back to the same binary as subprocesses.

How It Works

AI Agent ──stdio──> my-cli mcp (MCPServer)
                       |
                       |── tools/list  -> tool definitions from --experimental-dump-help
                       |
                       └── tools/call  -> my-cli repeat-phrase --count 3 "hello"
                                          merged log + structured result
  1. On startup, MCPServer runs the current executable with --experimental-dump-help to discover the full command tree and argument metadata.
  2. For each registered MCPCommand, it builds an MCP tool with a JSON Schema inputSchema derived from the command's arguments, options, and flags.
  3. When an agent calls a tool, the server converts JSON arguments back to CLI arguments and invokes the appropriate subcommand as a child process.

Tool Call Result

Every tool call produces two parallel views of the subprocess output:

  • A human-readable text content block with stdout and stderr interleaved in best-effort line-arrival order. Lines that came from stderr are prefixed with [stderr] so they remain distinguishable in the merged form.

  • A structuredContent object for programmatic consumption:

    {
      "stdout": "...",
      "stderr": "...",
      "exitCode": 0,
      "terminationReason": "exit",
      "stdoutTruncated": false,
      "stderrTruncated": false,
      "durationMs": 42
    }
    

    terminationReason is "exit" for a normal exit or "uncaughtSignal" if the child was killed. isError is set when the process exits non-zero or is killed.

Captured output is capped per stream (default 256 KiB) to keep large outputs from blowing up the agent's context. When the cap is hit the relevant *Truncated flag is set. Adjust via the outputCapBytes parameter on MCPServer.init.

The merged log approximates what a developer would see in a terminal, not the exact write order at the source — pipe and libc buffering on the child's side mean order is best-effort. For strict checks, use structuredContent.exitCode and the raw stdout / stderr fields.

Customization

Custom Tool Descriptions

By default, the MCP tool description is built from CommandConfiguration.abstract and .discussion. Override mcpDescription for a custom one:

extension RepeatPhrase: MCPCommand {

    static var mcpDescription: String {
        "Repeats a given phrase N times. Useful for testing and demonstrations."
    }
}

Argument Interceptor

Use transformArguments to add, remove, or modify CLI arguments before execution on a per-command basis:

extension Deploy: MCPCommand {

    static func transformArguments(_ arguments: [String]) -> [String] {
        arguments + ["--non-interactive"]
    }
}

Global Arguments

Pass arguments that should be appended to every command invocation:

let server = MCPServer(
    name: "my-cli",
    version: "1.0.0",
    commands: [Deploy.self, Status.self],
    globalArguments: ["--verbose"]
)

Server Instructions

Pass an instructions string to give the agent server-level context about your CLI — what it's for, when to reach for which tool, or conventions to follow. MCP clients surface this during the initialize handshake:

let server = MCPServer(
    name: "my-cli",
    version: "1.0.0",
    commands: [Deploy.self, Status.self],
    instructions: """
        Production deploy CLI. Run `status` before `deploy`. \
        Tool calls are non-interactive; never wait for prompts.
        """
)

Adding ArgumentParserMCP as a Dependency

To use the ArgumentParserMCP library in a SwiftPM project, add it to the dependencies for your package and your CLI target:

let package = Package(
    // name, platforms, products, etc.
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
        .package(url: "https://github.com/ilia3546/swift-argument-parser-mcp", from: "1.0.0"),
    ],
    targets: [
        .executableTarget(name: "<command-line-tool>", dependencies: [
            .product(name: "ArgumentParser", package: "swift-argument-parser"),
            .product(name: "ArgumentParserMCP", package: "swift-argument-parser-mcp"),
        ]),
    ]
)

Connecting to an MCP Client

Add your CLI to the client's MCP configuration. For example, in Claude Code (settings.json):

{
  "mcpServers": {
    "my-cli": {
      "command": "/path/to/my-cli",
      "args": ["mcp"]
    }
  }
}

Documentation

API documentation is generated automatically from source and hosted by Swift Package Index:

Requirements

  • Swift 6.0+
  • macOS 13+ or Linux

License

This library is released under the Apache 2.0 license. See LICENSE for details.