MCP Server
Any script built with :usage can serve as a Model Context Protocol (MCP) tool server. One command — no Python, no Node.js, no wrapper scripts.
Like completion and docgen, the mcp command is an Additional Command — always available without appearing in your usage array. Requires the native builtin (.so).
How It Works
The MCP server speaks JSON-RPC 2.0 over stdio. When an AI agent connects, it:
- Discovers tools — each subcommand becomes a tool with a JSON Schema derived from your
:argsdeclarations - Invokes tools — the agent calls a tool, argsh maps the JSON arguments back to CLI flags and re-invokes your script as a subprocess
- Returns results — stdout becomes the tool result, stderr is included for context
Your script is both the CLI and the tool server. The :usage/:args declarations are the single source of truth.
Client Configuration
Claude Code
Create .mcp.json at your project root:
Claude Desktop
Add to claude_desktop_config.json:
Cursor
Add to .cursor/mcp.json:
Example
Given this script:
#!/usr/bin/env bash
source argsh
main() {
local config
local -a verbose args=(
'verbose|v:+' "Enable verbose output"
'config|c' "Config file path"
)
local -a usage=(
'serve' "Start the server"
'build' "Build the project"
)
:usage "My application" "${@}"
"${usage[@]}"
}
serve() {
:args "Start the server" "${@}"
echo "serving on :8080"
[[ -z "${config:-}" ]] || echo "config=${config}"
}
build() {
:args "Build the project" "${@}"
echo "building..."
}
main "${@}"
What the agent sees
When connected, tools/list returns:
{
"tools": [
{
"name": "myapp_serve",
"description": "Start the server",
"inputSchema": {
"type": "object",
"properties": {
"verbose": {
"type": "boolean",
"description": "Enable verbose output"
},
"config": {
"type": "string",
"description": "Config file path"
}
},
"required": []
}
},
{
"name": "myapp_build",
"description": "Build the project",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
}
]
}
Tool invocation
The agent sends:
argsh reconstructs the CLI call: ./myapp serve --config /etc/app.yaml --verbose, captures the output, and returns:
Protocol Details
The server implements MCP 2025-11-25 with the tools capability:
| Method | Type | Response |
|---|---|---|
initialize | Request | Protocol version, capabilities, server info |
notifications/initialized | Notification | No response (client acknowledgment) |
ping | Request | {} |
tools/list | Request | Tool definitions with inputSchema |
tools/call | Request | Tool result with content and isError |
Unknown methods return error code -32601 (Method not found).
Type Mapping
argsh types map to JSON Schema types in inputSchema:
| argsh type | JSON Schema | Notes |
|---|---|---|
:+ (boolean flag) | "boolean" | true → --flag, false/absent → omitted |
:~int | "integer" | |
:~float | "number" | |
| default (string) | "string" |
Required flags (:! modifier) populate the required array.
Compared to docgen llm
docgen llm | mcp | |
|---|---|---|
| Output | Static JSON file | Live stdio server |
| Use case | Embed schemas in API calls | Agent connects directly |
| Execution | Agent runs CLI manually | argsh handles invocation |
| Protocol | Provider-specific (Claude/OpenAI) | Standard MCP (any client) |
Use docgen llm when you need to embed tool definitions in your own agent code. Use mcp when the client natively supports MCP.