Skip to content

External Clients

Learn how to connect to external MCP servers via HTTP or STDIO and generate fully typed TypeScript clients with autocomplete support.

For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.

xmcp lets you connect to external MCP servers and generate fully typed clients. The CLI generates TypeScript clients with autocomplete for all tools exposed by HTTP or STDIO-based MCP servers. For production deployments, use the HTTP transport. STDIO is limited to local development because it cannot be deployed in production environments.

Managed connections

Use the Node.js xmcp/client entry to connect directly without generating a typed client. createClient accepts a ClientDefinition and selects HTTP or STDIO from its type. It returns the connected SDK client, with methods such as listTools, callTool, readResource, and getPrompt.

TypeScript

withClient(definition, operation, options?) closes the connection after the callback resolves or throws and returns its result. Failed connections also clean up before rejecting. Closing a STDIO client waits for the spawned server process to exit.

TypeScript

These definitions use an explicit name, type, and, for STDIO, command and args. The shorthand npm and URL entries in src/clients.ts below are resolved by the generator; pass a resolved ClientDefinition to the managed API. HTTP headers support the same static values and environment references shown below. STDIO definitions support env, cwd, and stderr. Stderr is inherited by default; passing onStderrData in the options pipes it to your callback unless the definition explicitly selects another stderr strategy. An explicitly piped stream without a callback is forwarded to the parent's stderr.

Client handlers

Pass handlers in the options to support sampling, elicitation, or roots. They are registered before connecting, and only configured capabilities are advertised. Without handlers, none of these optional capabilities are advertised.

TypeScript

handlers.sampling handles sampling/createMessage, handlers.elicitation handles elicitation/create (both form and URL modes), and handlers.roots handles roots/list. Each receives the SDK request and client context and may return a result or a promise. A sampling handler must return the SDK's sampling result, including role, content, and model; supplying one advertises basic sampling support. Roots handlers do not advertise automatic list-change notifications.

Connections negotiate the protocol automatically. You can pass versionNegotiation: { mode: "legacy" } to force the older handshake. On modern connections, the SDK also routes embedded input_required requests through these handlers and retries the operation. Legacy stateless HTTP cannot deliver server-to-client input requests.

Cancellation and connection limits

Pass SDK request options directly to client methods. The connect option applies to connection setup:

TypeScript

Call controller.abort() from your application's cancel action. Modern HTTP and STDIO propagate cancellation to the server handler. Legacy stateless HTTP cannot route cancellation notifications to an earlier request. The managed client preserves that transport limitation.

With SDK 2.0.0 servers, cancellation of numeric request ID 0 is ignored. This can affect the first operation on a modern STDIO connection. Call listTools() before starting cancellable work on those servers; this is an underlying SDK limitation.

The existing createHTTPClient, createSTDIOClient, and generated clients keep their current behavior. For a runnable managed-client script, see the external-clients example.

Inspecting servers from the CLI

Use inspect to see a server's identity, negotiated protocol version, capabilities, and instructions. Use list to discover its tools, prompts, resources, and resource templates:

Shell

Both commands also accept a client name from src/clients.ts (falling back to clients.ts). Named clients preserve configured headers, environment variables, and STDIO settings. Override the file with -c, --clients:

Shell

To launch a local server directly, use --stdio. Put the CLI's options before --stdio; all arguments after the executable are passed to the server unchanged:

Shell

inspect --json returns serverInfo, protocolVersion, capabilities, and instructions. list --json returns tools, prompts, resources, and resourceTemplates, preserving metadata and schemas and following pagination. Capabilities the server does not advertise produce empty arrays.

Stdout contains the result only; logs and errors go to stderr. Configuration, connection, and listing failures exit nonzero without printing a partial result. Connections close before output is printed, including subprocess cleanup for STDIO. These commands negotiate modern or legacy MCP automatically and discover metadata without calling tools or fetching resource contents.

The external-clients example includes pnpm inspect and pnpm list:server scripts for a local server.

Executing operations from the CLI

call, read-resource, and get-prompt use the same URL, named-client, and STDIO connections as inspect and list. Each prints the complete MCP result as JSON, including content blocks, structured content, messages, and metadata:

Shell

Define local in your client config, or replace it with a server URL. For a direct STDIO server, omit the target and put all CLI options before --stdio:

Shell

Tool --arg key=value values parse as JSON when possible, otherwise as strings. Use --arg 'id="123"' to send a numeric-looking string. Prompt --arg values stay strings; prompt JSON objects must also contain string values.

Use one argument source per command: repeated --arg, --args-file, or stdin. --stdin and --args-file - explicitly read stdin. Without argument options, piped stdin is read automatically; empty automatic stdin means no arguments. Files and nonempty stdin must contain a JSON object. Duplicate keys in repeated --arg options are rejected. read-resource takes a complete URI, including resolved resource-template parameters, and accepts no argument options.

Tool arguments are checked against the advertised JSON Schema before invocation. Prompts validate required argument names and string values. --json is optional because execution results are always JSON.

Exit codeMeaning
0Success; the full result is on stdout
1A tool returned isError: true; its full result is on stdout
1Configuration, connection, server, or unknown-component error; stdout is empty
2Invalid CLI options or arguments, including malformed JSON or an unreadable argument file; stdout is empty

Logs and diagnostics go to stderr. Connections close before printing, including on errors; STDIO subprocesses are terminated. These commands do not install sampling or elicitation handlers. Use the managed-client API when your server needs those interactions.

The external-clients example includes runnable call:server, read:server, and prompt:server scripts.

Creating the Clients File

Create a src/clients.ts file and export a ClientConnections object. The object keys become the client names:

src/clients.ts

HTTP clients connect to remote MCP servers over HTTP. This is the recommended transport for production deployments.

TypeScript

Example:

TypeScript

To make HTTP extra.clientInfo available on stateless tool calls, repeat client identity in the current request headers:

TypeScript

STDIO Clients (local servers)

STDIO clients spawn local processes that communicate via standard input/output.

TypeScript

Examples:

TypeScript

The npm package will be installed automatically if it is not already installed in your project.

Running the Generator

Run the generator from your project root:

Shell

This reads from src/clients.ts and writes generated clients to src/generated/.

Options:

  • -o, --out <path> - Output directory (default: src/generated)
  • -c, --clients <path> - Clients file path (default: src/clients.ts)

Generated Output

For each client defined in clients.ts, the CLI generates a client.{name}.ts file containing:

  • Zod schemas for each tool's arguments
  • Type exports (e.g., GreetArgs)
  • Tool metadata objects
  • createRemoteToolClient() factory function
  • Pre-instantiated client export

An index file (client.index.ts) is always generated with a unified generatedClients object for accessing all clients.

Using Generated Clients

Import generatedClients from the generated index file and call tools directly:

src/tools/get-library-docs.ts

The generated clients provide full autocomplete for all available tools and their arguments.

Example: Browser Navigation

src/tools/browser-navigate.ts

Caveats

  • Server must be available — The CLI connects over HTTP or spawns the STDIO package to fetch tool definitions. Ensure the remote server is reachable or the npm package is installed.
  • Prefer env for secrets — API keys can be provided as CLI args or via the env map. Prefer env for sensitive values.

One framework to rule them all