@gnldev/mcp has 447 recorded weekly downloads on npm, ranking #1598 of 2,391 tracked listings.
About @gnldev/mcp
@gnldev/mcp
MCP client + server. Client: adapts external MCP tools to AI SDK tools → inside runDurable they become journaled and replayable: a call already recorded as done is not made again on resume (at-most-once for the effect). Server: exposes your own tools as MCP (callTool deduped against the journal, keyed by the CALLER the transport authenticated — see Who the caller is).
Install:
pnpm add @gnldev/mcp— or use it from a repo clone:pnpm install && pnpm -r build.
npm i @gnldev/mcp # peer: @gnldev/durable, ai · dep: @modelcontextprotocol/sdk, @gnldev/auth (installed for you)
import { mcpTools, createMcpServer } from '@gnldev/mcp';
// Client: connect to an MCP server with the real @modelcontextprotocol/sdk (stdio or http),
// discover via tools/list, convert to AI SDK tools. The connection is LAZY: no I/O happens
// until the first tools()/describeTools() call.
const handle = mcpTools({ transport: { kind: 'stdio', command: 'npx', args: ['-y', 'some-mcp-server'] }, prefix: 'github_' });
const tools = await handle.tools();
await runDurable({ runId: 'r1', journal, model, tools, prompt: '…' }); // MCP calls are journaled
await handle.close();
// Server: expose your own tools as MCP.
const server = createMcpServer({ tools: { lookupOrder } });
API
mcpTools(opts: { transport, prefix?, info? }) → McpToolsHandle— connects to the REAL SDK (stdio/http/custom transport), lazy connecthandle.tools() → Promise<Record<string, AISDKTool>>— discovered tools, in AI SDKtool()shapehandle.describeTools() → Promise<McpToolSummary[]>— a firewall-ready summary:{ name, description, inputSchema, descriptionHash }(consumed by W2)handle.close() → Promise<void>— closes the connection, idempotent (no-op if never connected)
createMcpTools(client, { prefix? })/connectMcp(transport, info?)— older, lower-level APIs (kept for compatibility);mcpToolsis a higher-level, lazy handle wrapping themcreateMcpServer(opts: { tools, journal?, identify?, allowTool?, rateLimit?, workKey?, workScope? })/serveMcp(server, transport, info?)— server side,callToolis idempotent (journal + the derived run id)identify(req) → Principal | undefined— WHO is calling (Identifyfrom@gnldev/auth, the same function every GNL door takes). Handed the HTTP request the call came in, or one carrying the token the transport validatedallowTool({ name, caller, principal, runsAs }) → boolean— WHAT they may call. Applied totools/listANDtools/callrateLimit: { maxCalls, windowMs } | fn— HOW OFTEN. The object form counts per caller, in this processtools: {...} | (ctx) => {...}— the function form hands the tool a sealed caller context, so it can answer "is this object theirs"workKey(req) → string | undefined— what NAMES the unit of work when the client sends no_meta.idempotencyKey. Defaults to the client's key; returning undefined means that call is not deduped
listTools(req?)is async and takes the caller (breaking from 0.5.0) — a per-caller list cannot be computed without knowing who asksmcpFirewall(opts: { server, journal, tools, allow?, deny?, maxCallsPerRun? }) → Guard— an MCP-specific Guard: allowlist/denylist + description pinning (tool-poisoning/rug-pull defense) + per-tool call limit (see the section below)composeGuards(first, second) → Guard— chains two Guards (iffirstallows,secondruns; deny/require-approval short-circuits)
How it works
Each MCP tool is wrapped with tool(); execute calls the MCP callTool. When wrapped with durableTool inside runDurable, the call is journaled → not called again on resume (see packages/durable/src/durable-tool.ts: keyed by toolCallId, if a succeeded record exists execute does NOT RUN AGAIN). Name collisions are avoided with the prefix. There is NO SEPARATE journal/dedup logic here — the durability of an MCP tool call is entirely delegated to @gnldev/durable's run/tool journal.
serveMcp uses ListToolsRequestSchema/CallToolRequestSchema (Zod schemas) when connecting to the real SDK Server — the SDK doesn't accept a plain { method: '...' } object. Raw tool outputs (string/object) are wrapped into the { content: [...] } format the SDK expects, in the bridge. A standard MCP tools/call request has no separate idempotencyKey field, but the spec defines params._meta as a free ('loose') meta carrier: mcpTools/createMcpTools (client) carries the idempotencyKey (${runId}:${toolCallId}) coming from runDurable's durableTool wrapper via params._meta.idempotencyKey; serveMcp reads it via req.params._meta?.idempotencyKey and passes it to createMcpServer.callTool — so journal-based server-side dedup — at-most-once for the effect — also works for calls coming through a real MCP Client (if _meta is absent, every call runs normally — backward compatible, and see workKey below).
Who the caller is, and why it is not the request
@gnldev/durable decided this for its own doors: workScope is read from the AGENT's configuration rather than from the call, because a per-call override "would put the dangerous half within reach of a request body". This server used to hand the request body the WHOLE identity — the dedup key was _meta.idempotencyKey, a string the client chose, and it became the journal's run id directly.
Three things were measured on a server built exactly as this README described, over the real SDK:…
Description from the npm listing.
- ai
- agent
- llm
- typescript
- ai-sdk
- durable
- exactly-once
- model-context-protocol
- tools
README badge
Show your rank in a README or on your website. The badge updates automatically.
Charts
Images: 30d · 1y · all time · 1600×900 dark
Adoption over time
7/30/90-day growth
Ranking history
Listing placement
Listing placement
Filled points are observed values; hollow points come from Internet Archive snapshots of the listing page. Dashed segments span periods with no observations — values between points are not interpolated.
Each band is one page of 20. Weekly downloads on a linear scale. Placement is projected from weekly downloads, so it can differ from the marketplace's own sort order. Hover a dot to see the listing.
Recent observations
Full history (1)Similar npm AI SDKs plugins
All 20 @gnldev/mcp alternativesData source
Weekly downloads (trailing 7 complete days) come from the official npm downloads API. Downloads include CI and mirrors and are not equivalent to installs or users. npm publishes no ratings.
Listing: https://www.npmjs.com/package/@gnldev/mcp. First seen Oct 20, 2026; last successful collection Oct 20, 2026. See the methodology.