Model Context Protocol TypeScript SDK

repository·main·Indexed 59 days ago

https://github.com/modelcontextprotocol/typescript-sdk

A standardized implementation of the Model Context Protocol (MCP) for TypeScript (version 2.0.0-alpha.0). It enables developers to build servers that expose tools, resources, and prompts, and clients that consume them across Node.js, Bun, and Deno runtimes.

Tokens
188.2K
Snippets
497
Records
709
Agent score
95%

What's inside MCP TypeScript SDK

  1. Overview of MCP TypeScript SDK packages

    main

    The SDK is a monorepo providing the following core components:

    • @modelcontextprotocol/server: Used to build MCP servers that expose tools, resources, and prompts.
    • @modelcontextprotocol/client: Used to build MCP clients that connect to servers via various transports.
    • Middleware packages: Thin adapters for integrating MCP into runtimes like Node.js HTTP, Express, Fastify, and Hono.
    • Examples: Runnable, self-verifying client/server pairs located in the examples/ directory.
  2. Overview of the stickynotes example application

    main

    The stickynotes example is a capstone application designed to demonstrate complex Model Context Protocol (MCP) interactions. It implements a sticky-notes board where:

    • Tools mutate state: Tools are used to modify the board.
    • Resources represent state: Each individual note is treated as an MCP resource.
    • Dynamic resource lists: The list of available resources changes dynamically when notes are added or removed.
    • Elicitation flows: The remove_all tool demonstrates a destructive action that requires user confirmation (elicitation).

    Note on transport compatibility:

    • stdio/legacy: Supports the full flow, including the cancel / unchecked / confirm elicitation cycle.
    • Modern/HTTP legs: Support add, list, read, and remove operations, but skip the remove_all confirmation flow due to transport limitations (e.g., the lack of a server→client request channel in certain eras or the stateless nature of HTTP handlers).
  3. Explore MCP TypeScript SDK feature stories

    main

    The repository contains numerous examples categorized by what they teach. Use these to understand specific MCP capabilities:

    Core Concepts

    • tools: Registering tools, inferring schemas, and structured output.
    • prompts: Working with prompts and argument completion.
    • resources: Static and templated resources, listing, reading, and subscriptions.
    • dual-era: How to implement a single factory that supports both protocol eras and transports.

    Advanced Features

    • subscriptions: Using client.listen() and handler.notify for real-time updates.
    • streaming: Implementing in-flight progress, logging, and cancellation.
    • elicitation: Handling user input via form or URL modes (supports both 2025 push-style and 2026 inputRequired).
    • sampling: Requesting LLM sampling from the client.
    • caching: Using cacheHints for cacheable results.
    • gateway: Implementing the gateway pattern using connect({ prior }) for zero-round-trip connections.
    • auth: Implementing Bearer token authentication and OAuth (authorization_code and client_credentials).
  4. Use bearer-auth-web for web-standard MCP servers

    main

    bearer-auth-web is a minimal Resource-Server-only implementation built using @modelcontextprotocol/server exports without an external framework. It is designed to run on web-standard runtimes like Cloudflare Workers, Deno, or Bun by providing a single fetch(request) handler. For Node.js environments, it uses toNodeHandler to bridge the handler onto node:http.

    Key features include:

    • Host and origin validation.
    • requireBearerAuth gate for createMcpHandler.
    • Composed as a single fetch(request) handler.

    Note: This implementation does not include an Authorization Server or discovery documents; for a full OAuth flow, refer to the oauth example.

    pnpm --filter @mcp-examples/bearer-auth-web server -- --http --port 3000
    pnpm --filter @mcp-examples/bearer-auth-web client -- --http http://127.0.0.1:3000/mcp
  5. Understand the cli-client architecture

    main

    The cli-client example serves as the reference MCP host. It demonstrates how to wire together connections, tool routing, resources, prompts, and sampling/elicitation handlers. Unlike simple examples, the core Client/transport construction is centralized in host/host.ts rather than being inline in the entry points.

    Project Layout:

    • cli.ts: Interactive entry point using a readline chat interface.
    • client.ts: CI entry point for scripted, self-verifying conversations.
    • server.ts: A thin shim used to run ../todos-server/server.ts for example runners.
    • host/: The core host implementation (connections, tool routing, resources, prompts, sampling, OAuth, config, and terminal UI).
    • providers/: The LLMProvider seam and specific provider mappings.
    • script/: Scripted conversation replays for CI.
    • test/: Unit tests.
  6. Understand MCP TypeScript SDK versioning and package groups

    main

    The MCP TypeScript SDK follows Semantic Versioning 2.0.0. Because it is a monorepo, packages are organized into version groups to ensure compatibility:

    • Fixed Group: The following packages always release together with the same version number:

      • @modelcontextprotocol/core
      • @modelcontextprotocol/client
      • @modelcontextprotocol/server
      • @modelcontextprotocol/server-legacy
      • @modelcontextprotocol/codemod
    • Framework Integrations: Packages like @modelcontextprotocol/node, @modelcontextprotocol/express, @modelcontextprotocol/hono, and @modelcontextprotocol/fastify are versioned alongside the fixed group. They bump when their @modelcontextprotocol/server peer dependency range changes, or independently for their own changes.

    • Internal/Private: @modelcontextprotocol/core-internal and the @modelcontextprotocol/core/internal entry point are private. They carry no compatibility promises and may change in any release without notice.

  7. Use prompt arguments as context for completions

    main

    The completion callback for a completable argument can accept an optional second parameter containing context. This context includes arguments, which are the values the client has already filled in for the prompt's other arguments. This allows you to create dependent autocompletions (e.g., selecting a branch based on a previously selected repository).

    Note: Clients are not required to send context. Your callback should handle cases where context or context.arguments is missing by returning an empty list rather than throwing an error.

    async function completeBranch(value: string, context?: { arguments?: Record<string, string> }): Promise<string[]> {
        // Access previously filled arguments
        const repo = context?.arguments?.repo;
        if (!repo) return [];
        
        // Return suggestions filtered by the context
        return (branchesByRepo[repo] ?? []).filter(branch => branch.startsWith(value));
    }
  8. How server-initiated notifications work via standalone GET

    main

    In the sessionful 2025 HTTP-only model, servers can initiate notifications/resources/list_changed notifications over a standalone GET SSE (Server-Sent Events) stream.

    Instead of the server pushing updates on an arbitrary timer, this pattern uses a tool-triggered approach to ensure determinism (useful for CI/CD). The workflow is as follows:

    1. The client opens a standalone GET stream via ClientOptions.listChanged.
    2. The client calls a specific tool (e.g., add_resource) that registers a new resource on the session's instance.
    3. The server emits the notifications/resources/list_changed notification over the existing GET stream.
    4. The client receives the notification and asserts its arrival.

    This approach replaces the older timer-driven unsolicited push model with a more predictable, tool-triggered mechanism.

  9. How to integrate MCP tools with an LLM model

    main

    To use MCP tools with a language model, follow this pattern:

    1. Call listTools() to get the tool definitions.
    2. Pass the name, description, and inputSchema of each tool to the model's tool-calling API.
    3. When the model requests a tool call, use the provided name and arguments to call client.callTool().
    4. Append the result.content from the tool call back to the conversation context so the model can process the output.
  10. Implement credential storage with issuer stamping

    main

    To support multiple authorization servers, v2 uses an issuer stamp on persisted credentials. When auth() is used, it adds an issuer field to every value passed to saveTokens() and saveClientInformation().

    Implementation Requirements for OAuthClientProvider implementers:

    1. Round-trip the issuer stamp: Your storage implementation must persist the object returned by saveTokens / saveClientInformation verbatim and return it exactly as received. If you rebuild the object field-by-field and drop the issuer, the SDK will log an [mcp-sdk] warning and the per-AS issuer check will not apply.
    2. Key storage by issuer: To hold credentials for multiple authorization servers, key your storage using ctx.issuer.
      • Note: If ctx is undefined, it signifies a request to return the most-recently-saved token set (used by the transport's per-request Authorization: Bearer calls).
    3. Handle Discovery State: Implement discoveryState() and saveDiscoveryState() so the authorization-server metadata survives restarts. This allows the callback leg to verify it is exchanging the code at the same AS that the redirect targeted.

    New Types:

    • StoredOAuthTokens and StoredOAuthClientInformation now include an optional issuer?: string field.
    // Example of implementing discovery state on OAuthClientProvider
    class MyProvider implements OAuthClientProvider {
      async saveDiscoveryState(state: OAuthDiscoveryState): Promise<void> {
        // Persist state with same durability as codeVerifier
      }
    
      async discoveryState(): Promise<OAuthDiscoveryState | undefined> {
        // Return persisted state
      }
    }
  11. How MCP hosts and servers interact via stdio

    main

    An MCP host (like VS Code, Cursor, or Claude Code) interacts with an MCP server as a child process using stdin and stdout.

    The Lifecycle of a Tool Call:

    1. Discovery: The host sends the model the names, descriptions, and input schemas of all available tools.
    2. Decision: The model selects a tool based on the user's prompt and the tool's metadata.
    3. Request: The host (acting as an MCP client) sends a tools/call request to the server over stdin.
    4. Execution: The server's SDK validates the arguments against the inputSchema and executes the handler.
    5. Response: The server sends the result back over stdout as a tools/call result.
    6. Completion: The model reads the result and generates a natural language response for the user.

    Critical Constraint: stdout is reserved strictly for the JSON-RPC protocol. Any logging to stdout will break the connection. Use stderr for all diagnostic logging.