MCP Connection Management in TypeScript Explained
This article explores building Model Context Protocol (MCP) clients in TypeScript, focusing on the mCP Hub
class. We will walk through connection management with MCP servers, including the logic for connecting, disconnecting, and attempting reconnections if a connection drops or fails.
Server Configuration
The process begins with an MCP configuration file, which is often based on the Claw desktop MCP configuration. Within this configuration, a nested JSON object defines the servers. Each server object contains two key properties: command
and args
.
Here is an example of what that configuration looks like:
{
"servers": {
"my-server-name": {
"command": "npx",
"args": [
"-y",
"name-of-server-package"
]
}
}
}
In this structure, command
specifies the executable to run, and args
provides the necessary arguments. The -y
flag, for instance, automatically confirms any prompts the server might present during startup.
The connectServer
Method
The core of the connection logic resides in the connectServer
method. This asynchronous method takes the server's name as an argument and returns a promise that resolves when the connection task is complete. If a connection to the server already exists, the default behavior is to reconnect.
The process involves several key steps:
Client Creation: A client instance is created. For this example, we'll name it
SourceBook
with a version of1.0.0
. It starts with an emptycapabilities
object, which will later be populated with the capabilities returned by the connected MCP servers.Configuration Retrieval: The method retrieves the configuration for the specified server by its name. It throws an error if the configuration doesn't exist.
Environment Sanitization: It filters out any undefined environment variables to ensure only valid key-value pairs are used, preventing misconfigurations.
Variable Merging: The filtered system environment variables are merged with server-specific environment variables. This allows the base environment to be clean and valid while enabling server-specific overrides or additions.
This merged environment data, along with the command
and args
from the config, is passed to a transport.
Understanding MCP Transports
To understand how connections are managed, it's crucial to know what a transport is within the Model Context Protocol. According to the official documentation, transports provide the foundation for communication between clients and servers. They handle the underlying mechanics of how messages are sent and received.
MCP uses JSON-RPC 2.0 as its wire format—the encoding method for data transmitted over a network. This ensures interoperability between different systems, as long as they can both process JSON.
There are three types of messages: - Requests - Responses - Notifications
MCP supports two transport implementations:
- Standard Input/Output (StdIO): This is the implementation used in our case. It's suitable for direct communication between a client and a server process. The transport configuration takes command
, args
, env
, and stderr
properties.
- Server-Sent Events (SSE): This is ideal for server-to-client streaming use cases, such as a server continuously pushing weather data or live notifications to a client.
Establishing the Connection
With the transport configured, the connection can be initialized.
Initialize State: The connection state is set to
connecting
. Aconnection
constant is used to manage the overall lifecycle of the connection.Store Connection: The new connection object is added to a private map of active connections, where the key is the server name.
Notify Listeners: A helper function,
notifyStatusChange
, is called to inform any registered listeners about the server's status change. This broadcasts the server name, connection status, any errors, and its capabilities.
Event handlers are registered for when the transport layer experiences an error or the connection closes.
The Connection Lifecycle and Error Handling
A comprehensive try...catch
block orchestrates the connection process and handles potential failures.
Inside the try
block:
- Log Attempt: The connection attempt is logged.
- Handle Stderr: Standard error output is handled.
- Connect Client: The client connection is established, which starts the transport.
- Initialization Request: An
initialize
request is sent to the server usingclient.request
. This request includes client info (name, version) and the protocol version. - Validate Response: The response from the server is validated against the
initializedResultSchema
from MCP. - Parse Capabilities: The capabilities reported by the MCP server are parsed from the response, and the connection's
capabilities
property is updated. A warning is logged if no capabilities are returned. - List Capabilities: The available tools and resources are conditionally listed based on the dynamic capabilities received from the server, such as
tools
,resources
, andresource_templates
.
Inside the catch
block:
If any part of the connection process fails, the error handling logic is executed:
- The error is logged to the console with the name of the problematic server.
- The connection status is set to disconnected
.
- The error object is stored in the connection's error
property.
- Listeners are notified of the status change.
- Finally, the error is re-thrown to propagate it to higher-level handlers if necessary.
Disconnecting and Reconnecting
The other two management methods are more straightforward.
disconnectServer
This method checks if a connection to the specified server exists. If it does, it retrieves the connection, closes it, and removes it from the active connections map. If no connection exists, it returns early.
reconnectServer
This method takes a server name as an argument, retrieves its configuration, and then simply calls the connectServer
method. Since connectServer
is designed to first disconnect any existing connection before establishing a new one, this single call efficiently handles both disconnection and reconnection in one step.
Join the 10xdev Community
Subscribe and get 8+ free PDFs that contain detailed roadmaps with recommended learning periods for each programming language or field, along with links to free resources such as books, YouTube tutorials, and courses with certificates.