npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@oribos/mcp-client

v0.9.0

Published

Oribos MCP client — bridge remote MCP tools into the Tool container (Streamable HTTP + stdio).

Readme

@oribos/mcp-client

Bridge a remote MCP server's tools into a Oribos tool container: one createMcpClient() object with a tools snapshot you spread straight into an agent, a refresh(), and a close().

import { createAgent } from '@oribos/core/agent';
import { createMcpClient } from '@oribos/mcp-client';

const client = await createMcpClient({
  transport: { type: 'http', url: 'https://example.com/mcp' },
  // or: { type: 'stdio', command: 'npx', args: ['my-mcp-server'] }
});

const agent = createAgent({
  name: 'assistant',
  instructions: 'You have remote tools.',
  model,                                     // any AI SDK provider instance
  tools: () => client.tools,                 // dynamic: every request resolves the latest snapshot
});

await client.refresh();                      // re-list remotely, swap the snapshot
await client.close();                        // tear down (idempotent)

Install

npm install @oribos/mcp-client @oribos/core

@oribos/core is a peer dependency (one core instance by design). The only direct runtime dependency is @modelcontextprotocol/client.

Transports and options

const client = await createMcpClient({
  transport:
    | { type: 'stdio', command: string, args?: string[], env?: Record<string, string> }
    | { type: 'http', url: string | URL, headers?: Record<string, string> },
  protocol?: 'auto' | 'legacy' | { pin: '2026-07-28' },  // omitted = 'auto'
  timeoutMs?: number,                                     // omitted = the SDK's 60s per request
});
  • stdio spawns the server subprocess (the SDK owns it: shutdown is stdin → SIGTERM → SIGKILL). env, when given, is the child's whole environment; omitted, the SDK applies its own safe whitelist — never your full process.env. The child's stderr is inherited, so its logs reach yours. stderr / cwd / maxBufferSize are not exposed — wire the official SDK yourself for those (the escape hatch for everything v1 doesn't expose).
  • http speaks Streamable HTTP; headers go on every request (bearer tokens and the like). OAuth flows, custom fetch, sessionId reattach: not exposed in v1.
  • protocol: 'auto' (the default — raised here; the SDK's own default is 'legacy') probes with server/discover first and falls back to the 2025 handshake. 'legacy' skips the probe. { pin: '2026-07-28' } demands the modern era and fails loudly. Note the probe's cost: on stdio it spawns a short-lived sibling process once per connect; on HTTP a probe timeout is treated as an outage and rejects.
  • timeoutMs is passed to the connect handshake and every request (the SDK has no client-level default setting). There is no per-call override — a long-running tool call is what this option is for.
  • The client identity advertised to servers is this package's own name and version, not overridable.

The snapshot and its lifecycle

client.tools is a getter over a frozen Record<string, Tool> — keys are the remote tool names. The identity is stable until the next refresh(); old references never break. There is no listChanged subscription and no auto-reconnect: a dropped connection surfaces as SDK errors on the calls in flight and after it; recovery is a new client. close() is idempotent — HTTP sends terminateSession first (failures there are silent), then tears down; in-flight requests reject with CONNECTION_CLOSED. Not calling close() on a stdio client keeps the subprocess alive — the host owns the lifetime.

prefixTools(tools, prefix, separator = '_') renames bridged tools into a new frozen record (prefix + separator + name). The prefix never reaches the wire — execute still calls the remote name. No collision detection runs (a fixed prefix is an isomorphic mapping), and whether a prefixed name is legal for your model provider is your concern.

What crosses the bridge

Schemas arrive as JSON Schema documents and are wrapped in a pass-through Standard Schema: validate always succeeds locally (validation is the remote's business — failures come back through the execute path), and jsonSchema.input returns the remote document verbatim — same reference, no rewriting between draft versions. jsonSchema.output throws: a bridged tool carries no outputSchema, and the SDK client already validates structuredContent against the remote's schema. A consequence: MCP→MCP re-exporting loses structuredContent (text only) — explicit opt-in fidelity is a future minor.

Context: of the six ToolContext pieces only signal crosses — callTool({ signal }) passthrough, no pre-checks. toolCallId / runId / traceId / spanId stay local (the protocol has no slot for them), and requestContext is not transmitted.

Results: structuredContent returns verbatim when present (any JSON value); otherwise text blocks join on newlines; image / audio / resource-link blocks degrade to placeholder text — the tool-result channel carries no multimodal parts, an honest v1 boundary. An empty result is ''. An isError result throws, so the framework turns it into a Tool 'x' failed: … error result fed back to the model — the same road the server-side failure lines take.

Errors propagate as-is: no layer added, no message rewritten. Connect-time: era negotiation failures, 401/403, probe timeouts. Runtime: request timeouts, CONNECTION_CLOSED, protocol errors, output-validation failures. A remote input_required answer (multi-round-trip) surfaces as a deterministic SdkError — this package pins inputRequired.autoFulfill: false and registers no elicitation/sampling handlers.

On modern HTTP connections the SDK must drop tools whose x-mcp-header declarations are illegal (SPEC MUST, warned in console) — the snapshot can be smaller than what the server advertises.

Install only what you use

  • dependency closure: everything @modelcontextprotocol/client pulls in — its OAuth / SSE / stdio closure — recorded in deps-budget.json, where count and unpacked size are watched in CI
  • first-party code: the minified baseline is recorded in byte-budget.json and checked on every PR — a warning, not a merge gate or a public budget
  • @oribos/core stays a peer, so there is exactly one core instance

License

Apache-2.0