@mcp-layer/connect
v1.2.1
Published
High-level client helpers for connecting to MCP servers using the official SDK.
Maintainers
Readme
@mcp-layer/connect
@mcp-layer/connect turns normalized MCP server definitions into live client connections using the official MCP SDK. It supports stdio, Streamable HTTP, and SSE transports.
This package centralizes:
- transport selection,
- stdio spawn setup (
cwd,env,stderr), - remote URL validation,
Sessionlifecycle ownership.
Table of Contents
- Installation
- Usage
- Transport Selection
- API Reference
- Behavior details
- Testing
- Related packages
- Runtime Error Reference
Installation
pnpm add @mcp-layer/connect
# or
npm install @mcp-layer/connect
# or
yarn add @mcp-layer/connectUsage
Use the configured server key with connect; the package resolves transport from that entry.
import { load } from '@mcp-layer/config';
import { connect } from '@mcp-layer/connect';
const config = await load(undefined, process.cwd());
// "demo" is the configured server name, not a transport label.
const session = await connect(config, 'demo');
await session.client.ping();
await session.close();For URL-based entries, transport is selected automatically (defaults to Streamable HTTP).
import { load } from '@mcp-layer/config';
import { connect } from '@mcp-layer/connect';
const config = await load(undefined, process.cwd());
// "remote-http" is just the server key in config.
// Transport is auto-selected from that server entry (url/endpoint => streamable-http).
const session = await connect(config, 'remote-http');
await session.client.listTools({});
await session.close();For legacy servers, force SSE with an explicit override.
import { load } from '@mcp-layer/config';
import { connect } from '@mcp-layer/connect';
const config = await load(undefined, process.cwd());
// "legacy" is the server key in config.
// Here transport is explicitly overridden to SSE.
const session = await connect(config, 'legacy', { transport: 'sse' });
await session.client.ping();
await session.close();Transport Selection
Transport is chosen automatically from server configuration unless you override it.
About configuration keys:
- MCP defines transport protocols, not a universal
.mcp.jsonschema: MCP Transports - Tool config shapes (for example
type,url,command) are client-specific: VS Code MCP config, Claude Code MCP config options.transportis an@mcp-layer/connectruntime override (not a cross-client config standard)
Automatic selection algorithm:
options.transportoverride (if provided)entry.config.type- inferred fallback from connection shape:
command=>stdiourlorendpoint=>streamable-http
Supported values:
stdiostreamable-http(httpandstreamableHttpaliases)sse
For remote transports, URL resolution order:
options.urlentry.config.urlentry.config.endpoint
API Reference
select(source, name)
Returns a server entry from a Map or any object implementing get(name).
setup(entry, options?)
Builds stdio spawn settings (command, args, cwd, env, stderr).
connect(source, name, options?)
Creates an SDK Client, instantiates the chosen transport, performs MCP handshake, and returns a Session.
Options include:
infotransporturltimeoutcwdenvstderrrequestIniteventSourceInitfetchsessionIdreconnectionOptions
Session (re-exported from @mcp-layer/session)
Properties:
.client.transport.info.name.source
Method:
.close()
Behavior details
- Stdio defaults
cwdto the config file directory. - Stdio env is merged in this order:
process.env-> entryconfig.env->options.env-> enforcedMCP_CLIENT_AGENT. opts.envoverrides entryenvvalues.- URL-based entries default to Streamable HTTP unless
options.transportis explicitlysse. - Invalid transport and URL values fail fast with
LayerError. options.timeoutfails fast when the MCP initialization handshake exceeds the configured timeout.
Testing
pnpm test --filter @mcp-layer/connectThe integration suite validates all three transports against real @mcp-layer/test-server endpoints over localhost.
Related packages
Runtime Error Reference
Expected server name to be a non-empty string.
Thrown from: connect
connect cannot look up a server entry without a valid key.
Step-by-step resolution:
- Ensure the second argument passed to
connectis a non-empty string. - Trim command-line input before forwarding it.
- Reject missing/blank values before calling
connect. - Add a regression test for blank names.
const name = typeof input.server === 'string' ? input.server.trim() : '';
if (!name) throw new Error('Missing server name.');
const session = await connect(config, name);Server "{server}" was not found in configuration.
Thrown from: connect
The requested server key is not present in the loaded config map.
Step-by-step resolution:
- List available server keys from config.
- Compare exact key spelling and casing.
- Verify the expected config file was loaded.
- Add preflight validation before connect.
const names = Array.from(config.map.keys());
if (!names.includes(target)) throw new Error(`Unknown server: ${target}`);Expected config source to support get(name).
Thrown from: select
The first argument is not a Map or map-like object.
Step-by-step resolution:
- Pass the
Configobject returned by@mcp-layer/config. - Or pass
config.mapdirectly. - Do not pass raw parsed JSON objects.
- Add a type guard in callers.
const config = await load(undefined, process.cwd());
const session = await connect(config, 'demo');Timed out while connecting to server "{server}" after {timeout}ms.
Thrown from: connect
The server accepted the transport but never completed the MCP initialization handshake before the deadline.
Step-by-step resolution:
- Verify the server process is running and logging output.
- Confirm the transport config (command/url) is correct.
- Increase
timeoutif the server requires more startup time. - Add integration coverage for slow-start servers.
Use a connection timeout to fail fast when a server never completes initialization, and expect connect to reject with a LayerError if the deadline is exceeded.
const session = await connect(config, 'demo', { timeout: 10_000 });Server "{server}" is missing a "command" property required for stdio transport.
Thrown from: setup
Stdio transport was selected but no executable command was provided.
Step-by-step resolution:
- Add
commandto the server entry. - Provide optional
argsif required. - If the server is URL-based, use remote transport instead.
- Add config tests for stdio and URL server entries.
{
"servers": {
"demo": {
"command": "node",
"args": ["./server.js"]
}
}
}Server "{server}" is missing a URL/endpoint required for remote transport.
Thrown from: connect
A remote transport (streamable-http or sse) was requested without url or endpoint.
Step-by-step resolution:
- Add
urlorendpointto the server entry. - Or pass
options.urlat connect time. - Confirm the value is not empty after env interpolation.
- Add a negative test for missing URL.
{
"servers": {
"remote": {
"url": "http://127.0.0.1:3000/mcp"
}
}
}Server "{server}" URL "{url}" is not a valid URL.
Thrown from: connect
The provided URL cannot be parsed by the runtime URL parser.
Step-by-step resolution:
- Ensure the value includes scheme (
http://orhttps://). - Remove whitespace and malformed characters.
- Validate URL formation before calling
connect. - Add a test for invalid URL rejection.
await connect(config, 'demo', { url: 'http://127.0.0.1:3000/mcp' });Transport "{transport}" is not supported. Use "stdio", "streamable-http", or "sse".
Thrown from: connect
options.transport was provided with an unsupported value.
Step-by-step resolution:
- Use one of the supported transport values.
- Prefer
streamable-httpfor modern HTTP servers. - Use
ssefor legacy HTTP+SSE servers. - Add input validation in CLI/config layers.
await connect(config, 'demo', { transport: 'streamable-http' });Server "{server}" is missing a supported transport configuration.
Thrown from: connect
The entry has no recognized connection primitive (command, url, or endpoint) and no valid transport declaration.
Step-by-step resolution:
- Add
commandfor stdio servers orurl/endpointfor remote servers. - Set a host-supported
typeor passoptions.transportat runtime when needed. - Validate config schema before connect.
- Add a failing fixture for empty server entries.
{
"servers": {
"demo": {
"command": "node",
"args": ["./server.js"]
}
}
}License
MIT
