@mcp-layer/cli
v1.3.2
Published
CLI framework for interacting with MCP servers using discovered schemas.
Readme
@mcp-layer/cli
@mcp-layer/cli is a CLI framework that turns MCP server schemas into a usable command line. It discovers a server from configuration, connects with the transport defined for that server (stdio, Streamable HTTP, or SSE), extracts the unified schema from @mcp-layer/schema, and renders commands for tools, prompts, resources, and templates. You can also extend it with custom commands.
Table of Contents
- Install
- Quick start
- Command surface
- Input handling
- Output formatting
- Color output
- Per-command help
- Output formats
- Configuration and server selection
- Embedding and custom commands
- API
- Global flags
- Development
- Runtime Error Reference
Install
pnpm add @mcp-layer/cliQuick start
Run the CLI against a configured MCP server:
mcp-layer servers list
mcp-layer tools list
mcp-layer tools echo --text "hello"Run the same commands against a remote server by selecting a config entry that uses url/endpoint:
mcp-layer tools list --server remote
mcp-layer tools echo --server remote --text "hello"Command surface
The CLI exposes the same schema items for tools, prompts, resources, and templates.
mcp-layer servers list
mcp-layer tools list
mcp-layer tools <tool>
mcp-layer prompts list
mcp-layer prompts <prompt>
mcp-layer resources list
mcp-layer resources <uri>
mcp-layer templates list
mcp-layer templates <template>Shorthand form:
mcp-layer tools:echo --text "hello"
mcp-layer prompts:kickoff --json '{"topic":"launch"}'Input handling
Inputs come from the MCP schema. You can supply them three ways:
--json '{"key":"value"}'for inline JSON--input ./payload.jsonfor JSON files--key valuefor schema properties (flags are generated from input schema fields)
Array and object inputs support a few additional forms:
- Arrays can be repeated:
--items one --items two - Arrays can be JSON:
--items '["one","two"]' - Objects can use dot notation:
--meta.tag alpha - Objects can be JSON:
--meta '{"tag":"alpha"}' - Scalar values are coerced using the schema type (e.g.,
--count 5,--enabled true).
If a parameter clashes with a CLI flag (like --help), pass tool arguments after --:
mcp-layer tools echo -- --help "not a real help flag"Output formatting
The CLI formats MCP responses for readability by default:
- Text content prints as plain text.
- Markdown content renders as ANSI Markdown when stdout is a TTY (pipes receive plain text).
- Images and audio show a short hint with MIME type and size.
- Resource links show name, description, and URI.
- Unsupported content types render as a labeled JSON fallback.
Use --raw to emit raw MCP payloads (plain text or binary bytes when a single payload is returned). If multiple content items are present, --raw returns JSON. This makes piping to files straightforward:
mcp-layer tools <tool> --raw > output.json
mcp-layer tools <tool> --raw > payload.binFor single resource payloads, --raw emits the unrendered text content (or binary bytes), which makes it easy to pipe markdown or plain text into a file:
mcp-layer resources resource://manual --raw > manual.mdDisable markdown rendering with --no-markdown.
Server-provided text is sanitized to strip ANSI escape sequences by default. If you trust the server and want to preserve ANSI output, pass --allow-ansi.
Example:
mcp-layer tools <tool> --allow-ansiColor output
Color output is enabled by default when stdout is a TTY. Disable it with --no-color or by setting NO_COLOR=1. You can customize the colors via accent and subtle in cli() options.
Per-command help
Use --help after a command to see its flags and examples:
mcp-layer tools echo --help
mcp-layer prompts kickoff --helpWhen a server is selected, help output uses the server name/version and lists all discovered tools, prompts, resources, and templates for that server.
Custom commands registered via cli().command() are included in the main help output and have their own --help rendering.
Example:
import { cli } from '@mcp-layer/cli';
await cli({ name: 'acme-mcp' })
.command(
{
name: 'status',
description: 'Show CLI status.'
},
async function statusCommand(argv) {
process.stdout.write(JSON.stringify({ ok: true }, null, 2));
}
)
.render();Output formats
List commands support JSON output:
mcp-layer tools list --format json
mcp-layer resources list --format jsonRun/read/render commands render formatted output by default. Use --raw for JSON (or binary bytes when a single binary payload is returned).
Configuration and server selection
@mcp-layer/cli uses @mcp-layer/config to discover MCP server definitions. Transport is selected automatically from the chosen server entry through @mcp-layer/connect.
The MCP spec defines available transports, but config keys are host-tool specific. References:
- MCP transport protocol: MCP Transports
- Example host config schemas: VS Code MCP config, Claude Code MCP config
- Connector coverage in this repo:
@mcp-layer/configconnectors
Selection order below is @mcp-layer/connect behavior:
Automatic selection behavior:
commandentries connect over stdio.url/endpointentries connect over Streamable HTTP by default.--transport sse(runtime override) forces legacy SSE for URL-based entries.
Use --transport when you need an explicit runtime override (for example, forcing SSE against a legacy endpoint) without adding non-standard keys to shared config files.
When multiple servers are configured, choose one with --server:
mcp-layer tools list --server demoControl server listing in help output via showServers:
showServers: truealways renders the Servers section.showServers: falsealways hides the Servers section. Default:true.
You can also point at a specific config file or directory:
mcp-layer tools list --config ./mcp.json
mcp-layer tools list --config ~/configsEmbedding and custom commands
You can embed the CLI and add custom commands using the same parser and help renderer.
import { cli } from '@mcp-layer/cli';
/**
* Render the CLI with a custom command.
* @returns {Promise<void>}
*/
async function main() {
await cli({ name: 'acme-mcp', description: 'Acme MCP CLI' })
.command(
{
name: 'status',
description: 'Report CLI configuration state.'
},
async function statusCommand(argv, helpers) {
const verbose = Boolean(argv.verbose);
const done = helpers.spinner('Loading status');
process.stdout.write(JSON.stringify({ ok: true, verbose }, null, 2));
done();
}
)
.render();
}
main();API
cli(options)
Creates a CLI instance.
Options:
name: CLI name displayed in help output.version: CLI version string.description: CLI description for help output.colors: enable or disable color output.accent: hex color for headings (default#FFA500).subtle: hex color for flag names (default#696969).spinner: enable the loading spinner.markdown: enable markdown rendering for text output.ansi: allow ANSI escape sequences in server-provided text.server: default server name.config: default config path.showServers: show or hide the Servers section in help output.timeout: fail if MCP initialization exceeds the timeout in milliseconds (opt-in only).
cli().command(options, handler)
Registers a custom command.
options.name: command name.options.description: summary for help output.handler(argv, helpers): async handler invoked with parsed args.helpers.spinner(text): start a spinner and return astop()function.
cli().render([argv])
Executes the CLI. If argv is omitted, it uses process.argv.
Global flags
--server <name>: select a configured server.--config <path>: point at a config file or directory.--transport <mode>: override transport at runtime (stdio,streamable-http, orsse).--timeout <ms>: fail if MCP initialization exceeds the timeout in milliseconds (opt-in).--format <json>: use JSON for list output.--json <string>: supply inline JSON input.--input <path>: supply JSON input from a file.--raw: emit raw JSON (or binary bytes for a single binary payload).--no-markdown: disable markdown rendering.--allow-ansi: preserve ANSI escape sequences from server text.--no-spinner: disable the loading spinner.--no-color: disable color output.
Development
pnpm test --filter @mcp-layer/cliRuntime Error Reference
This section is written for high-pressure debugging moments. Each entry maps directly to the CLI's MCP discovery and invocation paths.
Unknown "{targetType}" target "{targetName}".
Thrown from: cli.render
This happens when you run contextual help for a specific target (tools <name> --help, prompts <name> --help, resources <uri> --help, templates <uri> --help) and that target is not in the MCP catalog returned by the selected server.
Step-by-step resolution:
- Confirm which server the CLI selected: run
mcp-layer servers listor pass--server <name>explicitly. - List the available targets from that exact server:
mcp-layer tools list,mcp-layer prompts list,mcp-layer resources list, ormcp-layer templates list. - Compare the failing target value character-for-character with list output (case and punctuation must match).
- If the target should exist but does not, check the MCP server implementation for that capability and restart/reload the server.
mcp-layer --server dev tools list
mcp-layer --server dev tools weather.get --helpUnknown command "{command}".
Thrown from: cli.render
This happens when the first positional token does not match any built-in CLI surface (tools, prompts, resources, templates, servers, help) and does not match a registered custom command.
Step-by-step resolution:
- Run
mcp-layer --helpand verify the exact command name and shape. - Check for typos or singular/plural mixups (
toolvstools). - If you expected a custom command, confirm it was registered before calling
cli().render(...). - Re-run with the exact command shown by help output.
# Wrong: tool list
mcp-layer tools listUnknown prompt "{promptName}".
Thrown from: cli.render
This happens on prompts <name> exec when the connected MCP server does not expose a prompt with that name in prompts/list.
Step-by-step resolution:
- Run
mcp-layer prompts list --server <name>and copy the prompt name directly. - Confirm the same
--serverand--configvalues are used between list and exec commands. - Verify the MCP server currently loaded the prompt in its handler registry.
- Retry
prompts <name> execwith the exact listed name.
mcp-layer --server dev prompts list
mcp-layer --server dev prompts summarize.exec exec --topic "release notes"Unknown resource "{resourceUri}".
Thrown from: cli.render
This happens on resources <uri> exec when no resource with that URI exists in the connected MCP server's resources/list catalog.
Step-by-step resolution:
- Run
mcp-layer resources list --server <name>to retrieve valid URIs. - Copy the URI exactly; resource URIs are protocol-level identifiers and are strict.
- Ensure you are querying the same MCP server/environment where that URI is expected to exist.
- Re-run the read command with the exact URI from list output.
mcp-layer --server dev resources list
mcp-layer --server dev resources mcp://docs/changelog execUnknown template "{templateUri}".
Thrown from: cli.render
This happens on templates <uriTemplate> exec when the specified URI template is not present in the server's resources/templates/list response.
Step-by-step resolution:
- Run
mcp-layer templates list --server <name>. - Copy the URI template exactly from output (including placeholder names).
- Confirm the template is registered on the server instance you are connected to.
- Retry with the exact listed template and required parameters.
mcp-layer --server dev templates list
mcp-layer --server dev templates mcp://docs/{slug} exec --slug release-2026-02Unknown tool "{toolName}".
Thrown from: cli.render
This happens on tools <name> exec when the name is missing from the server's tools/list response.
Step-by-step resolution:
- Run
mcp-layer tools list --server <name>. - Use the exact tool name from list output.
- If missing unexpectedly, inspect server startup logs to verify tool registration completed.
- Re-run execution with the exact name and required arguments.
mcp-layer --server dev tools list
mcp-layer --server dev tools weather.get exec --city LondonInvalid integer for "{parameter}": "{value}".
Thrown from: coercenumber
This happens while validating tool/prompt/template arguments against the MCP JSON Schema. The target field is typed as integer, but the CLI received a non-integer value (for example 3.14, 1e2, or text).
Step-by-step resolution:
- Inspect the input schema (
detail.input.json) for the failing parameter and confirm it istype: "integer". - Check the CLI value source: direct flag (
--count),--json, or--inputfile. - Pass an integer literal only (no decimals, no unit suffixes).
- Add a command-level validation test covering both rejected (
3.5) and accepted (3) values.
# Wrong
mcp-layer tools batch.run exec --count 2.5
# Correct
mcp-layer tools batch.run exec --count 2Invalid number for "{parameter}": "{value}".
Thrown from: coercenumber
This happens while coercing CLI argument values for a schema field typed as number or integer; the received string cannot be parsed to a JavaScript number at all (for example ten, 10ms, or an empty string).
Step-by-step resolution:
- Identify where the value came from (
--json,--input, or CLI flags). - Ensure the payload value is numeric JSON (
42,3.14) and not decorated text ("42ms"). - If using shell flags, quote safely so the shell does not alter the token.
- Re-run with a pure numeric value and add a regression test for the bad literal.
# Wrong
mcp-layer tools stats.query exec --threshold ten
# Correct
mcp-layer tools stats.query exec --threshold 10Required parameter "{parameter}" is missing.
Thrown from: inputs
This happens after the CLI loads an MCP item's input schema and sees that a field listed in required is absent from resolved arguments.
Step-by-step resolution:
- Inspect the tool/prompt/template schema and locate
requiredfields. - Provide the missing value using the right input channel:
- Use
--<name> <value>for simple values. - Use
--json '{"name":"value"}'or--input payload.jsonfor structured payloads. - Re-run and verify all required keys are present.
# Tool schema requires "city"
mcp-layer tools weather.get exec --city ParisInvalid JSON for "{parameter}": {reason}
Thrown from: parsejson
This happens when a schema field is an object or array and the CLI attempts to parse the provided string as JSON, but parsing fails. Typical sources are single quotes, trailing commas, or shell-escaped payload corruption.
Step-by-step resolution:
- Identify which argument name the error reports in
{parameter}. - Validate the raw JSON snippet with
node -e 'JSON.parse(...)'before passing it to CLI. - Prefer
--input payload.jsonfor complex nested JSON to avoid shell escaping issues. - Re-run and keep payload examples in docs/tests for that command.
# Wrong (single quotes are not valid JSON content)
mcp-layer tools repo.search exec --filters '{tag: "api"}'
# Correct
mcp-layer tools repo.search exec --filters '{"tag":"api"}'Template parameter "{parameter}" is required but was not provided.
Thrown from: render
This happens when executing a resource template and at least one {placeholder} in uriTemplate has no matching argument in CLI input.
Step-by-step resolution:
- Inspect the template string from
templates listand enumerate placeholders. - Pass each placeholder as a flag with the exact same parameter name.
- If arguments are provided via
--json/--input, verify key names exactly match template placeholder names. - Re-run after all placeholders are bound.
# Template: mcp://docs/{team}/{slug}
mcp-layer templates mcp://docs/{team}/{slug} exec --team api --slug auth-flowTemplate URI is missing.
Thrown from: render
This happens when template execution reaches URI rendering without a uriTemplate value in the selected catalog item. In practice this indicates malformed/partial template metadata from the server.
Step-by-step resolution:
- Run
mcp-layer templates list --format jsonand inspectdetail.uriTemplatefor the failing entry. - Confirm the server returns a valid MCP resource template object, including
uriTemplate. - Fix the server-side template registration payload.
- Restart the server and verify template metadata again before exec.
{
name: 'docs-by-slug',
description: 'Read docs by slug',
uriTemplate: 'mcp://docs/{slug}'
}Multiple servers found. Provide --server .
Thrown from: select
This happens when configuration discovery yields more than one server and no --server flag (or default server setting) is provided, so CLI cannot safely infer which MCP endpoint to use.
Step-by-step resolution:
- Run
mcp-layer servers listto see discovered server names. - Pick the intended server explicitly with
--server <name>. - Optionally set a default server in CLI config to avoid repeating the flag.
- Re-run the original command with explicit server selection.
mcp-layer servers list
mcp-layer --server local-dev tools listServer "{server}" was not found.
Thrown from: select
This happens when --server <name> (or configured default) does not match any server key in the loaded MCP configuration documents.
Step-by-step resolution:
- Run
mcp-layer servers listand verify the actual configured names. - Check that
--configpoints to the config file/directory you intended. - Update command/config to use an existing server key exactly.
- If the server should exist, add it to config and rerun.
mcp-layer --config ./mcp.json servers list
mcp-layer --config ./mcp.json --server integration tools listLicense
MIT
