mrkeyoor.com_
Sat 19 Sept 15:50 UTC
npmAI / MLupdated 19 Sept 2026

@modelcontextprotocol/sdk review

Our clean Node 22 install of @modelcontextprotocol/sdk 1.30.0 finished in 3.5 seconds and occupied 27 MB, but both a CommonJS require and an ESM import failed on Node 22.23.2. This package is the first-generation TypeScript implementation of MCP for building clients and servers with stdio or Streamable HTTP. Version 1.30.0 fixes stdio buffer handling, Zod 3.25 method literals, JSON media-type validation, and SSE keep-alive behavior. The maintainers now direct new projects to the split v2 server and client packages.

50.0Mdownloads / wk
Verdict

Keep 1.30.0 for an existing v1 integration after proving its imports in your runtime. New TypeScript work belongs on the split v2 packages, and our failed import tests make this package a poor blind install today.

We installed it

Lab card: what happened when we installed @modelcontextprotocol/sdkScreenshot of @modelcontextprotocol/sdk documentation
Install✓ · 3.5s97 packages on disk · 27 MB
ImportESM import fails · require() fails · ESM package with exports map
Browsern/acould not be bundled for the browser (Node-only code, most likely)
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @modelcontextprotocol/sdk install cleanly?

Yes. In a fresh container with an empty cache, npm install @modelcontextprotocol/sdk finished in 4 seconds, leaving 97 packages and 27 MB on disk. npm audit reported no known vulnerabilities.

Can @modelcontextprotocol/sdk run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does @modelcontextprotocol/sdk work with both ESM and CommonJS?

Neither plain import nor require succeeded in our sandbox, so it needs a bundler or extra setup.

Does @modelcontextprotocol/sdk include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

@modelcontextprotocol/sdk or @modelcontextprotocol/server: which should you use?

@modelcontextprotocol/server: Choose the stable v2 server package for a new TypeScript MCP server and the 2026-07-28 protocol revision. Keep 1.30.0 for an existing v1 integration after proving its imports in your runtime.

When should you not use @modelcontextprotocol/sdk?

You are starting a new MCP project: the repository calls v2 the stable release line and publishes its server and client APIs as separate packages

API stability2/5The familiar v1 methods still cover tools, resources, prompts, clients, and transports, but this line is now legacy. The stable v2 release divides the API across server and client packages and changes schema handling. Within v1, HTTP plus SSE is deprecated, Streamable HTTP is preferred, and task APIs remain experimental. That is too much planned movement for a high stability score.
Docs4/5The verified v1 documentation URL returned HTTP 200 and separates server, client, protocol, capability, and troubleshooting material. The repository includes runnable examples for stdio, stateful and stateless Streamable HTTP, OAuth, elicitation, and progress. Readers must still watch the version marker because the main branch and its default tutorials now describe v2 imports that do not work with this package.
Maintenance4/5Release 1.30.0 shipped on July 27, 2026 with fixes for stdio limits, content-type parsing, Zod compatibility, SSE keep-alives, and a dependency advisory range. The repository was pushed on August 23, 2026 and is not archived. The maintainers promise v1 bug and security updates for at least six months after v2. That promise gives this legacy line a clear maintenance window, and it also puts an end date on guaranteed support.
Ecosystem5/5The npm download API recorded 50,691,263 downloads for August 16 through August 22, 2026, and the GitHub repository had 13,228 stars when checked. The package includes client and server sides, stdio and Streamable HTTP transports, OAuth helpers, and compatibility code for older SSE servers. Its reach is large, although new ecosystem work is moving to the v2 package family.

Use it if

  • You own a v1 MCP server that imports paths such as @modelcontextprotocol/sdk/server/mcp.js and need the July 2026 fixes without taking on a v2 migration
  • A host, template, or integration in your deployment still expects the combined v1 package instead of the separate v2 server and client packages
  • You need one Node package that contains MCP client and server APIs plus stdio, Streamable HTTP, legacy SSE, and OAuth helpers
  • Your tool and prompt schemas already use Zod 3.25 or Zod 4 raw shapes, which the v1 README documents as the supported schema contract
Skip it if

Setup reality

The clean install of v1.30.0 succeeded in 3.5 seconds with no cache. It left 97 packages using 27 MB on disk. The package itself has 17 direct dependencies, 2 peer dependencies, a 6,220 KB unpacked size, an MIT license, and a Node >=18 engine rule. npm audit reported zero known vulnerabilities at every severity level.

Install Zod beside the SDK because the README calls it a required peer dependency and accepts Zod 3.25 or Zod 4. Basic stdio servers need no credentials. Remote OAuth deployments do: you supply the provider, token storage, redirect handling, and authorization policy. On localhost, use createMcpExpressApp or explicit Host validation because the v1 server guide warns about DNS rebinding.

The package has type: module and an exports map. Even so, require() and ESM import both failed under Node 22.23.2 in our sandbox. Our scan also found no TypeScript types. Treat a minimal import as an acceptance test before migrating an existing service, since the published metadata and the observed artifact did not agree in this run.

The browser-targeted esbuild bundle failed, so plan around a server-side Node runtime. Stdio reserves stdout for JSON-RPC; send diagnostics to stderr. Streamable HTTP can be stateless, while sessions and resumability require storage and cleanup that your application owns. The project docs prescribe stateless requests, shared persistent state, or message routing for multi-node deployments.

Patterns

Expose a tool over stdio create-stdio-server

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({ name: 'math-server', version: '1.0.0' });
server.registerTool(
  'add',
  { inputSchema: { a: z.number(), b: z.number() } },
  async ({ a, b }) => ({
    content: [{ type: 'text', text: String(a + b) }],
  })
);

await server.connect(new StdioServerTransport());

The v1 schema is a raw object of Zod fields. Keep stdout reserved for protocol messages when stdio is connected.

Return typed tool data return-structured-output

server.registerTool(
  'calculate-bmi',
  {
    inputSchema: { weightKg: z.number(), heightM: z.number() },
    outputSchema: { bmi: z.number() },
  },
  async ({ weightKg, heightM }) => {
    const output = { bmi: weightKg / (heightM * heightM) };
    return {
      content: [{ type: 'text', text: JSON.stringify(output) }],
      structuredContent: output,
    };
  }
);

When outputSchema is present, structuredContent must validate against it. Text content keeps the result readable to older clients.

Serve a resource by URI register-resource-template

import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';

server.registerResource(
  'user-profile',
  new ResourceTemplate('users://{userId}/profile', { list: undefined }),
  { title: 'User profile', mimeType: 'application/json' },
  async (uri, { userId }) => ({
    contents: [{
      uri: uri.href,
      text: JSON.stringify(await getUser(userId)),
    }],
  })
);

Resources should expose reference data without side effects. Use a tool when reading the value performs an action or heavy computation.

Publish a prompt template register-prompt

server.registerPrompt(
  'review-code',
  {
    title: 'Code review',
    description: 'Review submitted code',
    argsSchema: { code: z.string() },
  },
  ({ code }) => ({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `Review this code:\n\n${code}` },
    }],
  })
);

Clients list prompts for user selection. A prompt is not invoked by the model as a tool.

Handle stateless Streamable HTTP serve-stateless-http

import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';

const app = createMcpExpressApp();
app.post('/mcp', async (req, res) => {
  const server = buildServer();
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,
  });

  res.on('close', () => {
    transport.close();
    server.close();
  });
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000, '127.0.0.1');

Create a server and transport for each stateless request. Reusing one pair across concurrent requests can mix client state.

Limit accepted Host headers validate-host-header

import express from 'express';
import { hostHeaderValidation } from '@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js';

const app = express();
app.use(express.json());
app.use(hostHeaderValidation([
  'localhost',
  '127.0.0.1',
  'mcp.internal.example',
]));

createMcpExpressApp enables protection for localhost defaults. Binding to 0.0.0.0 needs an explicit allowed-host policy.

Spawn a local MCP server connect-stdio-client

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

const transport = new StdioClientTransport({
  command: 'node',
  args: ['server.js'],
  env: { NODE_ENV: 'production' },
  stderr: 'inherit',
});
const client = new Client({ name: 'local-client', version: '1.0.0' });
await client.connect(transport);

const { tools } = await client.listTools();

connect() starts the child process. The child must write protocol traffic to stdout and ordinary logs to stderr.

Call a remote MCP tool connect-http-client

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const client = new Client({ name: 'remote-client', version: '1.0.0' });
const transport = new StreamableHTTPClientTransport(
  new URL('https://tools.example.com/mcp')
);
await client.connect(transport);

const result = await client.callTool({
  name: 'lookup-order',
  arguments: { id: 'A-1042' },
});

Streamable HTTP is the recommended remote transport in the v1 docs. SSEClientTransport exists for legacy servers.

Report a recoverable tool failure return-tool-error

server.registerTool(
  'find-user',
  { inputSchema: { id: z.string() } },
  async ({ id }) => {
    const user = await db.findUser(id);
    if (!user) {
      return {
        isError: true,
        content: [{ type: 'text', text: `No user found for ${id}` }],
      };
    }
    return { content: [{ type: 'text', text: JSON.stringify(user) }] };
  }
);

isError keeps an expected failure inside the tool result so the model can react. Thrown errors become protocol failures.

Enable a tool after authorization toggle-runtime-tool

const deleteUser = server.registerTool(
  'delete-user',
  { inputSchema: { id: z.string() } },
  async ({ id }) => {
    await removeUser(id);
    return { content: [{ type: 'text', text: `Deleted ${id}` }] };
  }
);

deleteUser.disable();

async function onRoleChanged(role) {
  if (role === 'admin') deleteUser.enable();
  else deleteUser.disable();
}

The returned tool handle sends a tools/listChanged notification when enable(), disable(), update(), or remove() changes availability.

Offer prompt argument completions complete-prompt-argument

import { completable } from '@modelcontextprotocol/sdk/server/completable.js';

server.registerPrompt(
  'greet',
  {
    argsSchema: {
      name: completable(z.string(), value =>
        ['Alice', 'Bob', 'Charlie'].filter(name =>
          name.toLowerCase().startsWith(value.toLowerCase())
        )
      ),
    },
  },
  ({ name }) => ({
    messages: [{ role: 'user', content: { type: 'text', text: `Hello, ${name}` } }],
  })
);

The client must call complete() for suggestions. Registering a completable field does not make a client UI request them automatically.

Send progress and honor cancellation send-progress

server.registerTool(
  'count',
  { inputSchema: { n: z.number().int().min(1).max(100) } },
  async ({ n }, extra) => {
    for (let i = 1; i <= n; i++) {
      if (extra.signal.aborted) {
        return { isError: true, content: [{ type: 'text', text: `Cancelled at ${i}` }] };
      }
      if (extra._meta?.progressToken !== undefined) {
        await extra.sendNotification({
          method: 'notifications/progress',
          params: { progressToken: extra._meta.progressToken, progress: i, total: n },
        });
      }
    }
    return { content: [{ type: 'text', text: `Counted to ${n}` }] };
  }
);

Send progress only when the request includes a progress token. Check extra.signal so client cancellation stops the work.

Alternatives

PackageRegistryPick it when
@modelcontextprotocol/servernpmChoose the stable v2 server package for a new TypeScript MCP server and the 2026-07-28 protocol revision
@modelcontextprotocol/clientnpmChoose the stable v2 client package when your application only consumes MCP servers
fastmcpnpmUse a higher-level TypeScript server framework when its CLI and framework conventions suit the project better than the reference SDK
mcpPyPIUse the official Python SDK when the tools and service code already live in Python

More ai / ml guides

openai · mcp · huggingface-hub · scikit-learn · tiktoken · langchain · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.