Build Your First Client

Purpose and Scope

This page walks through the first useful MCP client in the TypeScript SDK. A client is the host-side program that connects to one MCP server, discovers what the server offers, invokes tools, reads resources, and eventually shuts the connection down. The first-client tutorial is written against the weather server from the adjacent server tutorial, so the client launches that local server as a child process rather than requiring a separately deployed service. The same sequence also introduces the mental model used by larger hosts: connect first, inspect capabilities, call only advertised features, and close the transport when the session is done.

Sources: docs/get-started/first-client.md, docs/clients/connect.md

The tutorial deliberately uses the split v2 client package instead of the server package. Installing the client package gives you the high-level Client class and transport implementations that know how to speak MCP over a particular channel. For the local quickstart, StdioClientTransport is the important transport because it starts the weather server command and exchanges JSON-RPC over the child process stdin and stdout. The client does not attach to a server you already launched; connect() owns process startup, performs the initialize handshake, and keeps the process alive until the connection is closed.

Sources: docs/get-started/first-client.md, docs/clients/connect.md

Relevant Source Files

  • docs/get-started/first-client.md — primary tutorial for installing the client package, creating src/client.ts, connecting over stdio, listing tools, calling get-alerts, adding a resource to the weather server, and continuing toward cleanup and model handoff.
  • docs/clients/connect.md — explains the reusable connection model: construct Client, choose a transport, wait for connect(), inspect server metadata, and disconnect cleanly across HTTP, stdio, and SSE transports.
  • docs/clients/calling.md — provides the general client behavior behind the quickstart calls, including listTools, callTool, automatic pagination, structured output, listResources, and readResource.
  • docs/clients/caching.md — documents how cacheable client verbs such as listing tools and reading resources can be served from the response cache when servers provide freshness hints.
  • docs/.vitepress/theme/index.ts — shows that the current documentation site wraps VitePress with the shared banner layout used for these first-party v2 docs.
  • docs/v1/.vitepress/theme/index.ts — shows the v1 documentation theme reuses the shared styling while keeping the v1 site separate from the v2 tutorial path.

Core Primitives

The minimum client program has two primitives: a Client identity and a transport. The identity has a name and version so the server can identify the connecting application during initialization. The transport determines where messages go. In the quickstart, the stdio transport runs npx tsx src/index.ts, which is the weather server entry point from the first-server guide. In remote deployments, the connect guide shows the same client shape with StreamableHTTPClientTransport, and it also documents an SSE fallback path for older servers. The downstream client calls stay the same after the connection succeeds.

Sources: docs/get-started/first-client.md, docs/clients/connect.md

import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
 
const client = new Client({ name: 'my-first-client', version: '1.0.0' });
 
const transport = new StdioClientTransport({
    command: 'npx',
    args: ['tsx', 'src/index.ts']
});
 
await client.connect(transport);

A connected client can read the server information negotiated during initialization. The connect guide calls out accessors for the server version, server capabilities, and server instructions, and also notes that they are undefined until connect() resolves. Those values matter before you ask the server to do anything: capabilities tell you which families of requests are valid, while instructions are user-facing guidance that a host can place into the model’s context. In other words, discovery is not an optional logging step; it is the guardrail that keeps a host from calling features the server did not advertise.

Sources: docs/clients/connect.md

Task Flow

Start inside the weather project and install the client package. Then create src/client.ts with the client and stdio transport shown above. Run the file from the project root with npx tsx src/client.ts. When the connection succeeds, the server banner appears because the child process writes it to stderr, and your client continues to own the live server process. If the command cannot be found, the tutorial points to the executable path rather than MCP itself: a spawn npx ENOENT style failure means the command is not on the process path used by the client.

Sources: docs/get-started/first-client.md

After connecting, call listTools() and iterate the returned tools array. The tutorial emphasizes that the response contains every tool the server registered and the JSON Schema for each tool’s arguments. For the weather server, the visible output includes the get-alerts tool and its description. This is also the moment where a host prepares model metadata: names, descriptions, and input schemas are exactly the information a model needs to decide which callable action fits a user request. For larger servers, the calling guide explains that list methods automatically walk paginated responses unless you pass an explicit cursor.

Sources: docs/get-started/first-client.md, docs/clients/calling.md

const { tools } = await client.listTools();
for (const tool of tools) {
    console.log(tool.name, '—', tool.description);
}

Calling Tools and Reading Resources

The first actual invocation is callTool() with the advertised tool name and an arguments object that must satisfy the tool input schema. In the weather example, { state: 'CA' } calls the get-alerts action and returns typed content blocks. The tutorial shows reading text blocks and printing their text, while the broader calling guide explains the reliability contract: a failed tool execution or argument validation failure can still be a normal tool result with isError, but protocol-level failures such as an unknown tool name throw from the awaited call. A production host should therefore handle both result-level and exception-level failures.

Sources: docs/get-started/first-client.md, docs/clients/calling.md

const result = await client.callTool({ name: 'get-alerts', arguments: { state: 'CA' } });
 
for (const block of result.content) {
    if (block.type === 'text') console.log(block.text);
}

Resources are the read-only complement to tools. The first-client tutorial introduces a resource after the initial tool call by having the weather server register one, and the calling guide provides the reusable client pattern: list the resources the server exposes, then read a selected URI with readResource(). A resource response contains contents, where each item identifies the URI, its MIME type, and either text or base64 blob data. Use resources for context that the model should inspect, such as configuration, recent records, or static data; use tools for actions that perform work or contact external systems.

Sources: docs/get-started/first-client.md, docs/clients/calling.md

Caching, Cleanup, and Model Handoff

The quickstart can ignore caching, but it is helpful to know that the same calls participate in the SDK response cache. The caching guide says every client has a response cache, and cacheable verbs such as listTools() and readResource() consult it when the server sends freshness hints. By default, calls use the cache while entries are fresh; callers can refresh or bypass per call. This means a real host can repeatedly ask for tool metadata or resource contents without always crossing the wire, while still respecting server-provided time-to-live and scope decisions.

Sources: docs/clients/caching.md

A first client should also close what it opens. The connect guide documents clean disconnection and distinguishes transport behavior: stdio shutdown closes the child process in order, while Streamable HTTP can terminate the server-side session before closing the client. This matters because the tutorial’s early listing script does not exit by itself; the client still owns a live server process. Add cleanup once the demo has listed tools, called the weather tool, and read the resource. In real applications, put cleanup in a finally block so validation errors, network failures, or model-planning mistakes do not leave local server processes running.

Sources: docs/get-started/first-client.md, docs/clients/connect.md

The final handoff is from MCP discovery to the model runtime you control. Use getInstructions() as the server’s guidance, pass the tools metadata as the model’s callable tool catalog, and include resource contents when they are relevant context for the user request. Keep the MCP client as the execution boundary: the model chooses a tool-shaped action from names, descriptions, and schemas, but your host still calls client.callTool() and validates the returned content or error. That separation is the main benefit of the protocol: model planning, host policy, and server implementation remain distinct.

Sources: docs/get-started/first-client.md, docs/clients/connect.md, docs/clients/calling.md

Next Steps

After this tutorial, read Connect a Client for HTTP endpoints, stdio lifecycle details, SSE fallback, and connection introspection. Then read Call Tools, Resources, and Prompts for pagination, structured output, prompt retrieval, and resource templates. If your host will keep long-lived clients or serve multiple users, continue to Client Caching before adding shared stores or user partitions. The current and v1 VitePress theme entries show that the documentation maintains separate versioned sites, so make sure you are following the v2 path that uses @modelcontextprotocol/client rather than the older combined package imports.

Sources: docs/clients/connect.md, docs/clients/calling.md, docs/clients/caching.md, docs/.vitepress/theme/index.ts, docs/v1/.vitepress/theme/index.ts