[BREAKGLASS] Call MCPs via TypeScript, masquerading as simple TypeScript API. Or package them as cli. http://mcporter.dev
Go to file
Peter Steinberger 62df42ccee Release 0.2.0
2025-11-06 01:27:11 +00:00
.github/workflows Provide Linear key in CI 2025-11-05 04:41:17 +00:00
config Add Obsidian MCP server config and tighten CLI tooling 2025-11-05 22:27:37 +00:00
docs Improve hang diagnostics and termination 2025-11-06 01:02:18 +00:00
examples chore: finalize mcporter rename and isolate auth caches 2025-11-05 09:55:19 +00:00
scripts Align Biome config with Sweetistics and fix lint issues 2025-11-05 20:49:49 +00:00
src Release 0.2.0 2025-11-06 01:27:11 +00:00
tests Improve hang diagnostics and termination 2025-11-06 01:02:18 +00:00
.gitignore feat: add CLI generator and schema caching 2025-11-05 08:36:47 +00:00
AGENTS.md chore: finalize mcporter rename and isolate auth caches 2025-11-05 09:55:19 +00:00
biome.json Align Biome config with Sweetistics and fix lint issues 2025-11-05 20:49:49 +00:00
CHANGELOG.md Release 0.2.0 2025-11-06 01:27:11 +00:00
LICENSE Initial commit 2025-11-05 03:29:59 +00:00
package.json Release 0.2.0 2025-11-06 01:27:11 +00:00
pnpm-lock.yaml feat: add CLI generator and schema caching 2025-11-05 08:36:47 +00:00
README.md Add CLI regeneration workflow with metadata 2025-11-05 23:02:53 +00:00
RELEASE.md Add release checklist 2025-11-05 11:37:53 +00:00
tsconfig.build.json feat: bootstrap mcp-runtime package 2025-11-05 04:02:06 +00:00
tsconfig.json feat: bootstrap mcp-runtime package 2025-11-05 04:02:06 +00:00

mcporter 🧳

TypeScript runtime + CLI generator for the Model Context Protocol.

mcporter packages an ergonomic, composable toolkit that works equally well for command-line operators and long-running agents.

Features

  • Zero-config CLI npx mcporter list and npx mcporter call get you from install to tool execution quickly, with niceties such as --tail-log.
  • Composable runtime API createRuntime() pools connections, handles retries, and exposes a typed interface for Bun/Node agents.
  • OAuth support automatic browser launches, local callback server, and token persistence under ~/.mcporter/<server>/ (compatible with existing token_cache_dir overrides).
  • Structured configuration reads config/mcporter.json, automatically merges Cursor/Claude/Codex configs when present, and expands ${ENV} placeholders, stdio wrappers, and headers in a predictable way.
  • Integration-ready ships with unit and integration tests (including a streamable HTTP fixture) plus GitHub Actions CI, so changes remain trustworthy.

Installation

pnpm add mcporter
# or
yarn add mcporter
# or
npm install mcporter

Quick Start

import { createRuntime } from "mcporter";

const runtime = await createRuntime({ configPath: "./config/mcporter.json" });

const tools = await runtime.listTools("chrome-devtools");
const screenshot = await runtime.callTool("chrome-devtools", "take_screenshot", {
  args: { url: "https://x.com" },
});

await runtime.close();

Prefer createRuntime when you plan to issue multiple calls—the runtime caches connections, handles OAuth refreshes, and closes transports when you call runtime.close().

An end-to-end example lives in examples/context7-headlines.ts; it resolves a library via Context7, fetches documentation, and prints the markdown headings. Run it with:

pnpm exec tsx examples/context7-headlines.ts

Need a quick, single invocation?

import { callOnce } from "mcporter";

const result = await callOnce({
  server: "firecrawl",
  toolName: "crawl",
  args: { url: "https://anthropic.com" },
  configPath: "./config/mcporter.json",
});

CLI Reference

npx mcporter list                          # list all configured servers (non-blocking auth detection)
npx mcporter list vercel --schema          # show tool signatures + schemas
npx mcporter auth vercel --reset           # clear cached tokens, then walk through OAuth
npx mcporter auth vercel                   # pre-authorize OAuth flows without listing tools
npx mcporter call linear.searchIssues owner=ENG status=InProgress
npx mcporter call signoz.query --tail-log  # print the tail of returned log files
npx mcporter inspect-cli scripts/cli/vercel        # show metadata + stored generate-cli command
npx mcporter regenerate-cli scripts/cli/vercel     # rebuild a CLI artifact using saved metadata

# Local scripts for workspace automation
pnpm mcporter:list                                 # alias for mcporter list
pnpm mcporter:call chrome-devtools.getTabs --tail-log
pnpm mcporter:auth vercel --reset                  # same as mcporter auth --reset
pnpm mcporter:auth vercel                          # same as mcporter auth

Generated artifacts drop a companion <artifact>.metadata.json that records the generator version, resolved server definition, and the exact generate-cli flags that produced the file. Use mcporter inspect-cli <artifact> to review the metadata (or emit JSON with --json), and mcporter regenerate-cli <artifact> to replay the stored invocation against the latest mcporter build.

pnpm mcporter:list respects MCPORTER_LIST_TIMEOUT (milliseconds, default 30000). The aggregated view fans out in parallel and prints either the tool count or a short status (e.g., auth required — run 'mcporter auth <name>'). Export a higher timeout when you need to inspect slow-starting servers:

MCPORTER_LIST_TIMEOUT=120000 pnpm mcporter:list vercel

Common flags:

Flag Description
--config <path> Path to mcporter.json (defaults to ./config/mcporter.json).
--root <path> Working directory for stdio commands (so scripts/* resolve correctly).
--tail-log After the tool completes, print the last 20 lines of any referenced log file.

OAuth Flow

When a server entry declares "auth": "oauth", the CLI/runtime will:

  1. Launch a temporary callback server on 127.0.0.1.
  2. Open the authorization URL in your default browser (or print it if launching fails).
  3. Exchange the resulting code and persist refreshed tokens under ~/.mcporter/<server>/.

To reset credentials, delete that directory and rerun the command—mcporter will trigger a fresh login.

Generate Standalone CLIs

mcporter can mint a fully standalone CLI for any server—handy when you want a single-purpose tool with friendly flags. You do not need an on-disk config; provide --command (optionally --name) or fall back to --server '{...}' for advanced options:

Generate a single executable you can ship to agents or drop on a PATH:

npx mcporter generate-cli --command https://mcp.context7.com/mcp --compile
chmod +x context7
./context7 list-tools
./context7 resolve-library-id react

Pass --description if you want a friendly summary in the generated help, or fall back to --server '{...}' when you need headers, env vars, or stdio commands.

If you omit --name, mcporter infers one from the command URL (for example, https://mcp.context7.com/mcp becomes context7).

The command writes context7.ts alongside a compiled context7 binary. Generated CLIs embed the discovered schemas, so subsequent executions skip listTools round-trips and hit the network only for real tool calls. Use --bundle without a value to auto-name the output, and pass --timeout to raise the per-call default (30s). Add --minify to shrink bundled output. Compilation currently requires Bun; --compile [path] runs bun build --compile to emit a native executable, and when you omit the path the binary inherits the server name (context7 in the example) so you can drop it straight onto your PATH.

Tip: When Bun (or BUN_BIN) is available, mcporter defaults to --runtime bun; otherwise it falls back to Node. Pass --runtime node or --runtime bun to override explicitly.

Composable Workflows

The package exports a thin runtime that lets you compose multiple MCP calls and post-process the results entirely in TypeScript. The example in examples/context7-headlines.ts demonstrates how to:

  1. Resolve a library ID with context7.resolve-library-id
  2. Fetch the docs via context7.get-library-docs
  3. Derive a summary (markdown headings) locally

Use the pattern to build richer automations—batch fetch docs, search with Context7, or pass results into another MCP server without shelling out to the CLI.

Prefer the createServerProxy helper when you want an ergonomic proxy object for a server:

import { createRuntime, createServerProxy } from "mcporter";

const mcpRuntime = await createRuntime({
	servers: [
		{
			name: "context7",
			description: "Context7 docs MCP",
			command: {
				kind: "http",
				url: new URL("https://mcp.context7.com/mcp"),
				headers: process.env.CONTEXT7_API_KEY
					? { Authorization: `Bearer ${process.env.CONTEXT7_API_KEY}` }
					: undefined,
			},
		},
	],
});
// Inline definitions work at runtime; move this block to config/mcporter.json if you prefer static config.

const context7 = createServerProxy(mcpRuntime, "context7");

const search = await context7.resolveLibraryId("react");
const docs = await context7.getLibraryDocs("react"); // maps to required schema fields

console.log(search.text()); // "Available Libraries ..."
console.log(docs.markdown()); // markdown excerpt

await mcpRuntime.close();

Every property access maps from camelCase to the underlying tool name automatically (resolveLibraryIdresolve-library-id). Beyond method names, the proxy:

  • merges JSON-schema defaults so you only specify overrides;
  • validates required arguments and throws helpful errors when fields are missing;
  • returns a CallResult wrapper with .raw, .text(), .markdown(), .json(), and other helpers for quick post-processing.
  • accepts primitives, tuples, or plain objects and routes them onto required schema fields in order (multi-argument tools like Firecrawls scrape work with positional calls);
const firecrawl = createServerProxy(mcpRuntime, "firecrawl");
await firecrawl.firecrawlScrape(
	"https://example.com/docs",
	["markdown", "html"], // 2nd required/optional field from schema
	{ waitFor: 5000 }, // merged as args
	{ tailLog: true }, // treated as call options
);

You can still drop down to context7.call("resolve-library-id", { args: { ... } }) when you need explicit control.

Compose higher-level flows

Because the proxy already maps positional arguments to schema fields, you can layer custom helpers with plain JavaScript:

const context7 = createServerProxy(mcpRuntime, "context7");

async function getDocs(libraryName: string) {
	const resolved = await context7.resolveLibraryId(libraryName);
	const id =
		resolved
			.json<{ candidates?: Array<{ context7CompatibleLibraryID?: string }> }>()
			?.candidates?.find((candidate) => candidate?.context7CompatibleLibraryID)
			?.context7CompatibleLibraryID ??
		resolved.text()?.match(/Context7-compatible library ID:\s*([^\s]+)/)?.[1];
	if (!id) {
		throw new Error(`Context7 library "${libraryName}" not found.`);
	}
	return context7.getLibraryDocs(id);
}

const docs = await getDocs("react");
console.log(docs.markdown());

The return value is still a CallResult, so you retain .text(), .markdown(), .json(), and friends.

Configuration

Define your servers in config/mcporter.json using the same shape Cursor and Claude Code expect:

{
	"mcpServers": {
		"context7": {
			"description": "Context7 docs MCP",
			"baseUrl": "https://mcp.context7.com/mcp",
			"headers": {
				"Authorization": "$env:CONTEXT7_API_KEY"
			}
		},
		"chrome-devtools": {
			"command": "bash",
			"args": ["scripts/mcp_stdio_wrapper.sh", "env", "npx", "-y", "chrome-devtools-mcp@latest"]
		}
	},
	"imports": ["cursor", "claude-code", "claude-desktop", "codex"]
}

Fields you can use:

  • baseUrl for HTTP/SSE servers.
  • command + optional args for stdio servers.
  • Optional metadata such as description, headers, env, auth, tokenCacheDir, and clientName.
  • Convenience helpers bearerToken or bearerTokenEnv populate Authorization headers automatically.

If you omit the optional imports array, mcporter automatically merges Cursor, Claude Code, Claude Desktop, and Codex configs (first entry wins on conflicts). Set "imports": [] to disable or provide a custom order such as "imports": ["cursor", "codex"].

Pass a different path via createRuntime({ configPath }) when you need multiple configs side by side.

Testing & CI

Command Purpose
pnpm check Biome lint/format check.
pnpm build TypeScript compilation (emits dist/).
pnpm test Vitest unit + integration suites (includes a streamable HTTP MCP fixture).

GitHub Actions (.github/workflows/ci.yml) runs the same trio on every push and pull request.

Roadmap

  • Smoother OAuth UX (mcporter auth <server>, timeout warnings).
  • Tailing for streaming structuredContent, not just file paths.
  • Optional code generation for high-frequency tool schemas.
  • Automated release tooling (changelog, tagged publishes).

For deeper architectural notes, see docs/spec.md.

License

MIT — see LICENSE.