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

@ingram-tech/mcp-client

v1.0.0

Published

A stateless MCP client for the tool edge: tools/list and tools/call over Streamable HTTP, with tasks, input requests and x-mcp-header. No sessions.

Downloads

220

Readme

@ingram-tech/mcp-client

A stateless MCP client: list a remote server's tools and call one.

Each operation is a self-contained JSON-RPC POST over Streamable HTTP. There is no session and no initialize handshake to keep alive. Bun and Node 20+.

npm install @ingram-tech/mcp-client
import { createMcpClient } from "@ingram-tech/mcp-client";

const mcp = createMcpClient({ clientInfo: { name: "my-host", version: "1.0" } });

const { tools, protocolVersion } = await mcp.listTools("https://mcp.example.com/mcp", {
	headers: { Authorization: `Bearer ${token}` },
});

const text = await mcp.callTool(
	"https://mcp.example.com/mcp",
	"search",
	{ query: "invoices due this week" },
	{ headers: { Authorization: `Bearer ${token}` }, protocolVersion },
);

Tool results come back as text a model can read: structuredContent as JSON, otherwise the content blocks concatenated, with isError results prefixed error: so the model can recover.

What it speaks

  • MCP 2026-07-28, the stateless revision: the _meta triple on every request, Mcp-Method / Mcp-Name mirrored into headers, ttlMs on tools/list. A server that rejects those headers (400/404/405 on tools/list) is retried once as 2025-06-18 and reported as such in protocolVersion. Hand that back to callTool, which never probes, because a tool call is not safe to send twice.

  • Tasks (io.modelcontextprotocol/tasks): a server may answer tools/call with a task handle. The call polls it to completion at the server's pollIntervalMs and returns the final result; after taskWaitMs (default 5 min) it cancels the task and throws.

  • Input requests (resultType: "input_required"): a server may need an answer from the end user before it can finish. The call throws McpInputRequired carrying inputRequests and the opaque requestState (or taskId). Ask the user, then retry the same call with answers, or resumeTask for a task:

    try {
    	await mcp.callTool(url, name, args, opts);
    } catch (e) {
    	if (e instanceof McpInputRequired) {
    		const inputResponses = await askTheUser(e.inputRequests);
    		await (e.taskId
    			? mcp.resumeTask(url, { taskId: e.taskId, inputResponses }, opts)
    			: mcp.callTool(url, name, args, {
    					...opts,
    					answers: { inputResponses, requestState: e.requestState },
    				}));
    	}
    }
  • x-mcp-header: a server may mark a primitive property of a tool's inputSchema to be mirrored into an Mcp-Param-<Name> header, so its gateway can route on it without parsing the body. The mirror is applied on every call; a tool whose annotations break the spec's constraints is returned under dropped rather than called wrong.

  • Responses as application/json or as a text/event-stream carrying one JSON-RPC frame.

Resources, prompts, sampling and roots are not implemented.

Errors

Every failure is an MCPError. transport is true when the server never handled the call (a network failure or a non-2xx status), and false for an in-protocol JSON-RPC error, which may be the model misusing a healthy tool. Error text never echoes a response body.

Options

createMcpClient({
	clientInfo: { name: "my-host", version: "1.0" },
	timeoutMs: 30_000,
	taskWaitMs: 5 * 60_000,
	fetch: mySafeFetch,
});

Requests leave through @ingram-tech/safe-fetch by default: the URL is whatever the user configured, and a client that dereferences it from inside your network needs the SSRF guard. Pass fetch to replace it.

Wire constants

The protocol version strings, the _meta keys and the =?base64?…?= header encoding are exported so a server and a client built on this package cannot drift on them.

License

MIT