Install
$ agentstack add mcp-ilia3546-swift-argument-parser-mcp ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Swift Argument Parser MCP
[](https://github.com/ilia3546/swift-argument-parser-mcp/actions/workflows/ci.yml) [](https://swiftpackageindex.com/ilia3546/swift-argument-parser-mcp) [](https://swiftpackageindex.com/ilia3546/swift-argument-parser-mcp) [](https://swiftpackageindex.com/ilia3546/swift-argument-parser-mcp/documentation) [](LICENSE) [](https://boosty.to/ilia3546/donate)
Turn any Swift CLI into an MCP server in minutes.
ArgumentParserMCP lets AI agents call your [Swift Argument Parser][sap] commands as tools via the [Model Context Protocol][mcp]. Add one conformance, register your commands, and your CLI is ready for Claude, Cursor, and other MCP clients.
[sap]: https://github.com/apple/swift-argument-parser [mcp]: https://modelcontextprotocol.io
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-help
to discover the full command tree and argument metadata.
- For each registered
MCPCommand, it builds an MCP tool with a JSON SchemainputSchema
derived 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:
``json { "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: "", 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:
- [ArgumentParserMCP documentation][docs]
[docs]: https://swiftpackageindex.com/ilia3546/swift-argument-parser-mcp/documentation/argumentparsermcp
Requirements
- Swift 6.0+
- macOS 13+ or Linux
License
This library is released under the Apache 2.0 license. See [LICENSE](LICENSE) for details.
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ilia3546
- Source: ilia3546/swift-argument-parser-mcp
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.