Swift Argument Parser MCP
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
- On startup,
MCPServerruns the current executable with--experimental-dump-helpto discover the full command tree and argument metadata. - For each registered
MCPCommand, it builds an MCP tool with a JSON SchemainputSchemaderived from the command's arguments, options, and flags. - 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
structuredContentobject for programmatic consumption:{ "stdout": "...", "stderr": "...", "exitCode": 0, "terminationReason": "exit", "stdoutTruncated": false, "stderrTruncated": false, "durationMs": 42 }terminationReasonis"exit"for a normal exit or"uncaughtSignal"if the child was killed.isErroris 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.exitCodeand the rawstdout/stderrfields.
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.
No comments yet
Be the first to share your take.