Skip to content

Latest commit

 

History

History
106 lines (73 loc) · 4.89 KB

File metadata and controls

106 lines (73 loc) · 4.89 KB
shape how-to

Connect to a server

A client holds one connection to one server: construct a Client, pick a transport, and connect().

Create a client and connect over HTTP

Client takes a name and a version; StreamableHTTPClientTransport takes the server's MCP endpoint URL.

import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';

const client = new Client({ name: 'my-client', version: '1.0.0' });

const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp'));

await client.connect(transport);

connect() runs the initialize handshake and resolves once it completes. The client now holds the negotiated protocol version, the server's capabilities, and its instructions.

::: info Coming from v1? Client and the transport classes keep their names — only the import paths moved, to @modelcontextprotocol/client and its /stdio subpath. Run the codemod, then see the upgrade guide. :::

Connect to a local process over stdio

For a server you run as a child process, change only the transport: StdioClientTransport, imported from @modelcontextprotocol/client/stdio, spawns the command and speaks JSON-RPC over its stdin and stdout.

const client = new Client({ name: 'my-client', version: '1.0.0' });

const transport = new StdioClientTransport({ command: 'node', args: ['server.js'] });

await client.connect(transport);

server.js runs as a child of your process. close() shuts it down in order: close stdin, then SIGTERM, then SIGKILL.

::: tip InMemoryTransport.createLinkedPair() is the third transport: it links a Client and an McpServer inside one process, no network and no child process. Test a server builds on it. :::

Fall back to SSE for servers that predate Streamable HTTP

An SSE-only server speaks the older HTTP+SSE transport instead of Streamable HTTP. Try StreamableHTTPClientTransport first; when it fails, retry with SSEClientTransport on a fresh Client.

try {
    const client = new Client({ name: 'my-client', version: '1.0.0' });
    await client.connect(new StreamableHTTPClientTransport(new URL(url)));
    return client;
} catch {
    const client = new Client({ name: 'my-client', version: '1.0.0' });
    await client.connect(new SSEClientTransport(new URL(url)));
    return client;
}

Whichever branch returns, the Client behaves the same from here on — nothing downstream depends on the transport.

::: info versionNegotiation in ClientOptions controls which protocol revision connect() negotiates — see Protocol versions. :::

Read what the server told you at connect time

Three accessors return what the server declared during the handshake; all of them return undefined until connect() resolves.

console.log(client.getServerVersion());
console.log(client.getServerCapabilities());
console.log(client.getInstructions());

Connected to a server named travel that registered one tool and set instructions, that prints:

{ name: 'travel', version: '2.1.0' }
{ tools: { listChanged: true } }
Call list-trips before book-trip. Dates are ISO 8601.

The capability object gates every verb on the next page: only ask for what the server advertised. getInstructions() is the server's usage guide for the model — put it in the system prompt.

Disconnect cleanly

Over Streamable HTTP, terminate the server-side session, then close the client.

await transport.terminateSession();
await client.close();

close() tears down the transport and rejects every request still in flight with a CONNECTION_CLOSED error. terminateSession() returns without sending anything when the server never issued a session ID. On the other transports, close() alone is the whole teardown.

Recap

  • new Client({ name, version }), a transport, and connect() are the whole setup; connect() runs the initialize handshake.
  • StreamableHTTPClientTransport connects to remote servers; StdioClientTransport, from @modelcontextprotocol/client/stdio, spawns local ones; SSEClientTransport is the fallback for SSE-only servers.
  • InMemoryTransport.createLinkedPair() links a client and a server in one process.
  • After connect(), getServerVersion(), getServerCapabilities(), and getInstructions() return what the server declared.
  • close() tears down the transport and rejects in-flight requests.
  • Protocol-revision differences live on the protocol versions page, not here.