diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6848e4c..fb41938 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -114,21 +114,6 @@ jobs:
grep -q "Ready" /tmp/mcpb-smoke.log
working-directory: examples/pdf-server
- dependency-isolation:
- name: Dependency isolation
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - uses: actions/setup-node@v6
- with:
- node-version: "22"
- cache: npm
-
- - run: npm ci
-
- - run: npm run test:dependency-isolation
-
e2e:
runs-on: ubuntu-latest
steps:
@@ -147,10 +132,8 @@ jobs:
- run: npm ci
- # Headless runs without a channel use chromium-headless-shell, so the
- # full Chromium build (about 165 MB) would only be downloaded, never run.
- name: Install Playwright browsers
- run: npx playwright install --with-deps --only-shell chromium
+ run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npx playwright test --reporter=list
@@ -182,14 +165,9 @@ jobs:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
- # The checkout lives on the Windows drive, which WSL reaches over 9P;
- # writing node_modules there is what makes this job slow (npm ci alone
- # took about 3 minutes). Copy the tree onto the distro's ext4 filesystem
- # first, as Microsoft's WSL guidance recommends for anything I/O heavy.
- name: Build and test in WSL
shell: wsl-bash {0}
run: |
- cp -r "$(pwd)" ~/ext-apps && cd ~/ext-apps
npm ci
npm run build
npm run examples:build
diff --git a/AGENTS.md b/AGENTS.md
index 219b947..6bfc8cd 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -60,8 +60,8 @@ rm -fR package-lock.json node_modules && \
### Key Source Files
-- `src/app.ts` - `App` subclasses `Protocol` from `@modelcontextprotocol/client`, handles View initialization, tool calls, and messaging
-- `src/app-bridge.ts` - `AppBridge` subclasses `Protocol` from `@modelcontextprotocol/client` for the iframe channel and proxies through a separate outer `Client`
+- `src/app.ts` - `App` class extends MCP Protocol, handles guest initialization, tool calls, messaging
+- `src/app-bridge.ts` - `AppBridge` class for hosts, proxies MCP requests, sends tool input/results to guests
- `src/server/index.ts` - Helpers for MCP servers to register tools/resources with UI metadata
- `src/types.ts` - Protocol types re-exported from `spec.types.ts` and Zod schemas from `generated/schema.ts` (auto-generated during build)
- `src/message-transport.ts` - `PostMessageTransport` for iframe communication
@@ -70,12 +70,12 @@ rm -fR package-lock.json node_modules && \
### Protocol Flow
```
-View (App/Protocol) <--PostMessageTransport--> Host (AppBridge/Protocol) <--separate MCP Client--> MCP Server
+View (App) <--PostMessageTransport--> Host (AppBridge) <--MCP Client--> MCP Server
```
1. Host creates iframe with view HTML
2. View creates `App` instance and calls `connect()` with `PostMessageTransport`
-3. View sends `ui/initialize` (the only handshake on the iframe channel) and receives authoritative Apps capabilities and context
+3. View sends `ui/initialize` request, receives host capabilities and context
4. Host sends `sendToolInput()` with tool arguments after initialization
5. View can call server tools via `app.callServerTool()` or send messages via `app.sendMessage()`
6. Host sends `sendToolResult()` when tool execution completes
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e28dcd1..6592106 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -502,7 +502,7 @@ Alternatively, use the [workflow dispatch](https://github.com/modelcontextprotoc
## Code of Conduct
-This project follows our [Code of Conduct](https://github.com/modelcontextprotocol/.github/blob/main/CODE_OF_CONDUCT.md). Please review it before contributing.
+This project follows our [Code of Conduct](CODE_OF_CONDUCT.md). Please review it before contributing.
## Reporting Issues
diff --git a/README.md b/README.md
index ab4004d..7348e15 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
Why
·
- Quickstart
+ Quickstart
·
API Docs
·
@@ -89,8 +89,6 @@ it will guide you through the rest.
-
-
> [!NOTE]
@@ -122,36 +120,12 @@ resources:
## Getting Started
-Requires Node.js 20+. The base MCP SDK packages are `^2.0.0` peers of
-`ext-apps` (`@modelcontextprotocol/core` is a required peer that `client` already depends on, so npm installs it for you).
-
-For a View or host:
-
```bash
-npm install -S @modelcontextprotocol/ext-apps \
- @modelcontextprotocol/client@^2.0.0 \
- zod@^4.2.0
+npm install -S @modelcontextprotocol/ext-apps
```
-For an MCP server, add the server package (and, for HTTP transports, the Node
-and Express adapters):
-
-```bash
-npm install -S @modelcontextprotocol/ext-apps \
- @modelcontextprotocol/client@^2.0.0 \
- @modelcontextprotocol/server@^2.0.0 \
- @modelcontextprotocol/node@^2.0.0 \
- @modelcontextprotocol/express@^2.0.0 \
- zod@^4.2.0
-```
-
-The wire protocol is unchanged between `ext-apps` 1.x and 2.x: a 2.x View works
-in a 1.x host and a 2.x host renders 1.x Views. See the
-[migration guide](https://apps.extensions.modelcontextprotocol.io/api/documents/migrate-to-v2.html)
-when upgrading from 1.x.
-
**New here?** Start with the
-[Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html)
+[Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html)
to build your first MCP App.
## Using the SDK
@@ -165,7 +139,7 @@ UI metadata.
| `@modelcontextprotocol/ext-apps` | Build interactive Views (App class, PostMessageTransport) | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/app.html) |
| `@modelcontextprotocol/ext-apps/react` | React hooks for Views (useApp, useHostStyles, etc.) | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/_modelcontextprotocol_ext-apps_react.html) |
| `@modelcontextprotocol/ext-apps/app-bridge` | Embed and communicate with Views in your chat client | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/app-bridge.html) |
-| `@modelcontextprotocol/ext-apps/server` | Register tools and resources on your MCP server | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/server-helpers.html) |
+| `@modelcontextprotocol/ext-apps/server` | Register tools and resources on your MCP server | [API Docs →](https://apps.extensions.modelcontextprotocol.io/api/modules/server.html) |
There's no _supported_ host implementation in this repo (beyond the
[examples/basic-host](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host)
@@ -272,7 +246,7 @@ point your client at a local build:
## Resources
-- [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/quickstart.html)
+- [Quickstart Guide](https://apps.extensions.modelcontextprotocol.io/api/documents/Quickstart.html)
- [API Documentation](https://apps.extensions.modelcontextprotocol.io/api/)
- [Specification (2026-01-26)](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/2026-01-26/apps.mdx)
([Draft](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx))
diff --git a/build.bun.ts b/build.bun.ts
index 61361e6..a129d5d 100644
--- a/build.bun.ts
+++ b/build.bun.ts
@@ -1,9 +1,6 @@
#!/usr/bin/env bun
import { $ } from "bun";
-import { cpSync, mkdirSync, rmSync } from "node:fs";
-
-// Avoid publishing artifacts left behind by an earlier build or branch.
-rmSync("dist", { recursive: true, force: true });
+import { cpSync, mkdirSync } from "node:fs";
// Run TypeScript compiler for type declarations
await $`tsc`;
@@ -34,15 +31,9 @@ function buildJs(
});
}
-// Peer dependencies stay external in the standard entry points so consumers
-// share one base MCP SDK and Zod instance. The *-with-deps entry points keep
-// bundling these dependencies for standalone browser use.
-const PEER_EXTERNALS = [
- "@modelcontextprotocol/client",
- "@modelcontextprotocol/core",
- "@modelcontextprotocol/server",
- "zod",
-];
+// zod is a peerDependency — keep it external so consumers share a single
+// zod instance (instanceof ZodError / schema.extend() break with duplicate copies).
+const PEER_EXTERNALS = ["@modelcontextprotocol/sdk", "zod"];
await Promise.all([
buildJs("src/app.ts", {
diff --git a/docs/authorization.md b/docs/authorization.md
index df7adc6..59fc26c 100644
--- a/docs/authorization.md
+++ b/docs/authorization.md
@@ -150,7 +150,7 @@ registerAppTool(
"get_account_balance",
{
description: "Get account balance",
- inputSchema: z.object({ accountId: z.string() }),
+ inputSchema: { accountId: z.string() },
},
async ({ accountId }) => {
if (!authInfo) {
diff --git a/docs/migrate-to-2.md b/docs/migrate-to-2.md
deleted file mode 100644
index f9341d0..0000000
--- a/docs/migrate-to-2.md
+++ /dev/null
@@ -1,130 +0,0 @@
----
-title: Migrate to v2
-group: Getting Started
-description: Upgrade from ext-apps 1.x to 2.x — new base MCP SDK peer packages, API changes for Views and hosts, and what stays wire-compatible.
----
-
-# Migrating from ext-apps 1.x to 2.x
-
-ext-apps 2.x is built on the base MCP TypeScript SDK 2.x, which replaced the
-single `@modelcontextprotocol/sdk` package with `@modelcontextprotocol/client`,
-`server`, `core`, `node` and `express`. The MCP Apps wire protocol did not
-change; the breaking changes are in dependencies and in the TypeScript API.
-
-## Host compatibility
-
-The `ui/*` messages exchanged over the iframe channel are byte-identical to
-1.x. A 2.x View runs in a 1.x host and a 2.x host renders 1.x Views. The only
-host-side deltas are in error responses (see below).
-
-## What stays the same
-
-`App` / `AppBridge` constructors, the `on*` setters, `addEventListener`,
-`callServerTool`, `readServerResource`, `registerAppResource`, the React hooks
-and the `_meta.ui.resourceUri` handling are unchanged. Published `.d.ts` files
-now resolve under `moduleResolution: NodeNext` / `Node16` without workarounds
-(#705).
-
-## Peer dependencies by role
-
-| Role | Install |
-| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| View author | `@modelcontextprotocol/ext-apps`, `@modelcontextprotocol/client@^2.0.0`, `zod@^4.2.0` (+ `react`/`react-dom` for `./react`) |
-| Host author | same as View author |
-| MCP server author | View author packages + `@modelcontextprotocol/server@^2.0.0`; for HTTP transports also `@modelcontextprotocol/node@^2.0.0` (`NodeStreamableHTTPServerTransport`) and `@modelcontextprotocol/express@^2.0.0` (`createMcpExpressApp`, which peers on `express`) |
-| CDN / `*-with-deps` | nothing extra: `./app-with-deps` and `./react-with-deps` bundle client, core and zod (about 25% larger than the 1.x bundles) |
-
-`@modelcontextprotocol/client` is a required peer (`App` and `AppBridge` extend
-its `Protocol` class); `@modelcontextprotocol/core` is a required peer that `client` already depends on, so npm installs it without you listing it;
-`@modelcontextprotocol/server` stays optional and is only needed for the
-`./server` helpers. Node.js 20+ is required.
-
-## Breaking changes
-
-- **Peer packages.** `@modelcontextprotocol/sdk@^1` is replaced by the split
- 2.x packages above, all at `^2.0.0`. Remove the 1.x package from your
- project: the two SDKs share no classes or types (a 1.x `McpError` or
- `Client` cannot be passed to 2.x code), even though they speak the same
- wire protocol.
-- **zod 3 dropped.** The peer range is `zod@^4.2.0`. Tool schemas must
- implement Standard JSON Schema (`~standard.jsonSchema`): zod 4.2+, ArkType,
- Valibot. zod 4.0 and 4.1 do not expose `~standard.jsonSchema` and are
- rejected too. Raw zod shapes (`{ q: z.string() }`) still work with
- `registerAppTool` but are deprecated; wrap them with `z.object({...})`.
-- **`App` / `AppBridge` extend `Protocol` from `@modelcontextprotocol/client`.**
- `ProtocolWithEvents` is gone; use the SDK's `Protocol`. The `AppRequest`,
- `AppNotification` and `AppResult` unions are kept as deprecated type
- aliases (nothing in 2.x consumes them) and will be removed in 3.0.
-- **Handler context.** Custom request handlers, `registerAppTool` callbacks
- (server) and `app.registerTool` callbacks (View) receive the SDK 2.x
- context: `extra.signal` → `extra.mcpReq.signal`, `extra.requestId` →
- `extra.mcpReq.id`; `extra.sessionId` is unchanged and `extra.authInfo`
- moved to `extra.http?.authInfo`.
-- **`setRequestHandler` / `setNotificationHandler` take method names.**
- `app.setRequestHandler(SomeRequestSchema, handler)` becomes
- `app.setRequestHandler("some/method", { params: SomeParamsSchema }, (params, ctx) => …)`
- for custom methods (the handler receives the parsed params); the two-argument
- `setRequestHandler("tools/call", handler)` form exists only for spec-defined
- method names. The 1.x `(Schema, handler)` form still works on `App` and
- `AppBridge` as a deprecated overload: it logs a one-time warning, hands the
- handler the whole `{ method, params }` message as before, and gives request
- handlers a 1.x-shaped `extra` (`signal`, `requestId`, `sessionId`, `_meta`,
- `sendRequest`, `sendNotification`, `authInfo`; exported as
- `LegacyRequestHandlerExtra`). It will be removed in 3.0.
-- **Double-registration guard covers the SDK's own handlers.** Registering a
- handler for `notifications/progress` or `notifications/cancelled` (which
- the base `Protocol` already owns) now throws `already registered`, as
- `ping` and the `on*`-owned methods did in 1.x. Use `addEventListener` or
- the SDK's progress callbacks instead of replacing these.
-- **Errors.** Remote JSON-RPC errors are `ProtocolError` (numeric `code`);
- local conditions are `SdkError` with a string `code`: request timeout →
- `"REQUEST_TIMEOUT"`, connection closed → `"CONNECTION_CLOSED"`. Cancelling a
- request with an `AbortSignal` also rejects with `"REQUEST_TIMEOUT"` (the
- message is the abort reason). Messages no longer carry the `MCP error N:`
- prefix.
- A resource-not-found reply that carries `data.uri` arrives as
- `ResourceNotFoundError` (a `ProtocolError` subclass, `code` `-32602`, with a
- `.uri` getter).
-
-## Host-side wire deltas
-
-Observed when a 2.x `AppBridge` answers a View; a 1.x View sees these too.
-
-| Situation | 1.x host | 2.x host |
-| --------------------------------------------------------- | ------------------------------ | --------------------------------------------------- |
-| A handler throws `-32002` (resource not found) | `error.code: -32002` | `error.code: -32602` (the SDK never emits `-32002`) |
-| Invalid params on a `ui/*` request | `-32603` with a zod issue dump | `-32602 Invalid params for : …` |
-| Error message text | `MCP error -32602: …` prefix | plain message |
-| `tools/call` to an unknown tool through a 2.x `McpServer` | `result.isError: true` | JSON-RPC error `-32602` (`callServerTool` rejects) |
-
-## `schema.json`
-
-The published `./schema.json` export is regenerated from the 2.x core schemas:
-
-- `McpUiToolResultNotification.params.structuredContent` is any JSON value (was
- `type: "object"`).
-- `McpUiToolResultNotification.params._meta` documents
- `io.modelcontextprotocol/serverInfo` and no longer lists `progressToken` /
- `related-task` (both still pass through).
-- `McpUiHostContext.toolInfo.tool.outputSchema` is a loose object (only
- `$schema` is documented); `inputSchema.properties` values are now typed as
- JSON values.
-- A recursive JSON-value definition (`__schema0`) is added under the `$defs` of
- `McpUiHostContext`, `McpUiHostContextChangedNotification` and
- `McpUiInitializeResult`.
-
-## Checklist
-
-1. `npm uninstall @modelcontextprotocol/sdk` and install the packages for your
- role from the table above.
-2. Replace `sdk/...` imports with the split packages (`sdk/server/mcp.js` →
- `@modelcontextprotocol/server`, `sdk/server/streamableHttp.js` →
- `NodeStreamableHTTPServerTransport` from `@modelcontextprotocol/node`,
- `sdk/server/stdio.js` → `@modelcontextprotocol/server/stdio`, hand-written
- Express routing → `createMcpExpressApp` from `@modelcontextprotocol/express`,
- `sdk/types.js` → `@modelcontextprotocol/client` or `server` for the types,
- `@modelcontextprotocol/core` for the zod schemas).
-3. Wrap raw zod shapes with `z.object({...})`.
-4. Update custom handlers to the `extra.mcpReq.*` context and method-keyed
- `setRequestHandler` calls.
-5. Replace `McpError` / numeric-code checks with `ProtocolError` / `SdkError`.
diff --git a/docs/migrate_from_openai_apps.md b/docs/migrate_from_openai_apps.md
index 22d5d9f..d91281b 100644
--- a/docs/migrate_from_openai_apps.md
+++ b/docs/migrate_from_openai_apps.md
@@ -66,7 +66,7 @@ The server-side changes involve updating metadata structure and using helper fun
#### Before (OpenAI)
```typescript
-import { McpServer } from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
function createServer() {
@@ -78,7 +78,7 @@ function createServer() {
{
title: "Shopping Cart",
description: "Display the user's shopping cart",
- inputSchema: z.object({ userId: z.string() }),
+ inputSchema: { userId: z.string() },
annotations: { readOnlyHint: true },
_meta: {
"openai/outputTemplate": "ui://view/cart.html",
@@ -126,7 +126,7 @@ function createServer() {
#### After (MCP Apps)
```typescript
-import { McpServer } from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
registerAppTool,
registerAppResource,
@@ -144,7 +144,7 @@ function createServer() {
{
title: "Shopping Cart",
description: "Display the user's shopping cart",
- inputSchema: z.object({ userId: z.string() }),
+ inputSchema: { userId: z.string() },
annotations: { readOnlyHint: true },
_meta: { ui: { resourceUri: "ui://view/cart.html" } },
},
diff --git a/docs/overview.md b/docs/overview.md
index 8eda95d..28f5b23 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -56,11 +56,7 @@ flowchart LR
- **Host** — The chat client (e.g., Claude Desktop) that connects to servers, embeds Views in iframes, and proxies communication between them.
- **View** — The UI running inside a sandboxed iframe. It receives tool data from the Host and can call server tools or send messages back to the chat.
-The View's `App` and the Host's `AppBridge` both subclass the `Protocol` class
-exported by `@modelcontextprotocol/client` for the iframe channel. A separate outer `Client` connects
-the Host to the actual MCP Server. Keeping those two connections separate
-preserves the Apps-only iframe handshake and prevents iframe capabilities from
-leaking into the server connection.
+The View acts as an MCP client, the Host acts as a proxy, and the Server is a standard MCP server.
## Lifecycle
@@ -97,7 +93,7 @@ sequenceDiagram
```
1. **Discovery** — The Host learns about tools and their UI resources when connecting to the server.
-2. **Initialization** — When a UI tool is called, the Host renders the iframe. The View sends `ui/initialize` (the only handshake on the iframe channel) and receives host context (theme, capabilities, container dimensions). `ui/initialize` is authoritative for Apps capabilities and identity, and `ui/notifications/initialized` is the public View-ready signal.
+2. **Initialization** — When a UI tool is called, the Host renders the iframe. The View sends `ui/initialize` and receives host context (theme, capabilities, container dimensions). This handshake ensures the View is ready before receiving data.
3. **Data delivery** — The Host sends tool arguments and, once available, tool results to the View. Results include both `content` (text for the model's context) and optionally `structuredContent` (data optimized for UI rendering). This separation lets servers provide rich data to the UI without bloating the model's context.
4. **Interactive phase** — The user interacts with the View. The View can call tools, send messages, or update context.
5. **Teardown** — Before unmounting, the Host notifies the View so it can save state or release resources.
diff --git a/docs/patterns.md b/docs/patterns.md
index 884de26..87a6d82 100644
--- a/docs/patterns.md
+++ b/docs/patterns.md
@@ -19,7 +19,7 @@ registerAppTool(
"update-quantity",
{
description: "Update item quantity in cart",
- inputSchema: z.object({ itemId: z.string(), quantity: z.number() }),
+ inputSchema: { itemId: z.string(), quantity: z.number() },
_meta: {
ui: {
resourceUri: "ui://shop/cart.html",
@@ -127,14 +127,14 @@ registerAppTool(
{
title: "Read Data Bytes",
description: "Load binary data in chunks",
- inputSchema: z.object({
+ inputSchema: {
id: z.string().describe("Resource identifier"),
offset: z.number().min(0).default(0).describe("Byte offset"),
byteCount: z
.number()
.default(MAX_CHUNK_BYTES)
.describe("Bytes to read"),
- }),
+ },
outputSchema: DataChunkSchema,
// Hidden from model - only callable by the App
_meta: { ui: { visibility: ["app"] } },
@@ -281,7 +281,10 @@ server.registerResource(
```ts source="./patterns.tsx#binaryBlobResourceClient"
-const result = await app.readServerResource({ uri: `video://${videoId}` });
+const result = await app.request(
+ { method: "resources/read", params: { uri: `video://${videoId}` } },
+ ReadResourceResultSchema,
+);
const content = result.contents[0];
if (!content || !("blob" in content)) {
diff --git a/docs/patterns.tsx b/docs/patterns.tsx
index bb9eaa4..6b87692 100644
--- a/docs/patterns.tsx
+++ b/docs/patterns.tsx
@@ -17,12 +17,16 @@ import { randomUUID } from "node:crypto";
import type {
CallToolResult,
ReadResourceResult,
-} from "@modelcontextprotocol/server";
+} from "@modelcontextprotocol/sdk/types.js";
+import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js";
import type { McpUiHostContext } from "../src/types.js";
import { useEffect, useState } from "react";
import { useApp } from "../src/react/index.js";
import { registerAppTool } from "../src/server/index.js";
-import { McpServer, ResourceTemplate } from "@modelcontextprotocol/server";
+import {
+ McpServer,
+ ResourceTemplate,
+} from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
/**
@@ -113,14 +117,14 @@ function chunkedDataServer(server: McpServer) {
{
title: "Read Data Bytes",
description: "Load binary data in chunks",
- inputSchema: z.object({
+ inputSchema: {
id: z.string().describe("Resource identifier"),
offset: z.number().min(0).default(0).describe("Byte offset"),
byteCount: z
.number()
.default(MAX_CHUNK_BYTES)
.describe("Bytes to read"),
- }),
+ },
outputSchema: DataChunkSchema,
// Hidden from model - only callable by the App
_meta: { ui: { visibility: ["app"] } },
@@ -248,7 +252,10 @@ function binaryBlobResourceServer(
*/
async function binaryBlobResourceClient(app: App, videoId: string) {
//#region binaryBlobResourceClient
- const result = await app.readServerResource({ uri: `video://${videoId}` });
+ const result = await app.request(
+ { method: "resources/read", params: { uri: `video://${videoId}` } },
+ ReadResourceResultSchema,
+ );
const content = result.contents[0];
if (!content || !("blob" in content)) {
diff --git a/docs/quickstart.md b/docs/quickstart.md
index a021912..3e00796 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -21,7 +21,7 @@ This tutorial assumes you've built an MCP server before and are comfortable with
We'll use the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) to build the server.
-You'll also need Node.js 20+.
+You'll also need Node.js 18+.
## 1. Set up the project
@@ -37,7 +37,7 @@ Install the dependencies you'll need:
```bash
npm init -y
-npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@^2.0.0 @modelcontextprotocol/server@^2.0.0 @modelcontextprotocol/node@^2.0.0 @modelcontextprotocol/express@^2.0.0 zod@^4.2.0 express cors
+npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk express cors
npm install -D typescript vite vite-plugin-singlefile @types/express @types/cors @types/node tsx concurrently cross-env
```
@@ -59,7 +59,6 @@ npm pkg set scripts.start='concurrently --raw "cross-env NODE_ENV=development IN
```json source="../examples/quickstart/tsconfig.json"
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
@@ -88,7 +87,6 @@ npm pkg set scripts.start='concurrently --raw "cross-env NODE_ENV=development IN
```json source="../examples/quickstart/tsconfig.server.json"
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
@@ -180,10 +178,9 @@ import {
registerAppTool,
RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
-import { McpServer } from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import fs from "node:fs/promises";
import path from "node:path";
-import { z } from "zod";
const DIST_DIR = path.join(import.meta.dirname, "dist");
@@ -208,7 +205,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time.",
- inputSchema: z.object({}),
+ inputSchema: {},
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
},
async () => {
@@ -243,10 +240,10 @@ export function createServer(): McpServer {
```ts source="../examples/quickstart/main.ts"
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -266,7 +263,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/docs/testing-mcp-apps.md b/docs/testing-mcp-apps.md
index a8eb333..df1396c 100644
--- a/docs/testing-mcp-apps.md
+++ b/docs/testing-mcp-apps.md
@@ -28,10 +28,6 @@ The [`basic-host`](https://github.com/modelcontextprotocol/ext-apps/tree/main/ex
cd examples/basic-host
```
- The root install runs the package build, including type-checking the
- documentation snippets against the pinned base MCP SDK 2.x packages. Do
- not bypass a failed build before testing the host.
-
2. Start basic-host, pointing it to your MCP server:
```bash
diff --git a/examples/basic-host/package.json b/examples/basic-host/package.json
index cefe9d5..117beca 100644
--- a/examples/basic-host/package.json
+++ b/examples/basic-host/package.json
@@ -1,7 +1,7 @@
{
"homepage": "https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host",
"name": "@modelcontextprotocol/ext-apps-basic-host",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"scripts": {
"build": "tsc --noEmit && concurrently \"cross-env INPUT=index.html vite build\" \"cross-env INPUT=sandbox.html vite build\"",
@@ -11,11 +11,11 @@
"dev": "cross-env NODE_ENV=development concurrently \"npm run watch\" \"npm run serve\""
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/express": "^5.0.0",
@@ -29,6 +29,7 @@
"prettier": "^3.6.2",
"typescript": "^5.9.3",
"vite": "^6.0.0",
- "vite-plugin-singlefile": "^2.3.0"
+ "vite-plugin-singlefile": "^2.3.0",
+ "vitest": "^3.2.4"
}
}
diff --git a/examples/basic-host/src/implementation.ts b/examples/basic-host/src/implementation.ts
index 5f680ff..31e3698 100644
--- a/examples/basic-host/src/implementation.ts
+++ b/examples/basic-host/src/implementation.ts
@@ -1,12 +1,8 @@
import { RESOURCE_MIME_TYPE, getToolUiResourceUri, type McpUiSandboxProxyReadyNotification, AppBridge, PostMessageTransport, type McpUiResourceCsp, type McpUiResourcePermissions, buildAllowAttribute, type McpUiUpdateModelContextRequest, type McpUiMessageRequest } from "@modelcontextprotocol/ext-apps/app-bridge";
-import {
- Client,
- SSEClientTransport,
- StreamableHTTPClientTransport,
- type CallToolResult,
- type Resource,
- type Tool,
-} from "@modelcontextprotocol/client";
+import { Client } from "@modelcontextprotocol/sdk/client/index.js";
+import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
+import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
+import type { CallToolResult, Resource, Tool } from "@modelcontextprotocol/sdk/types.js";
import { getTheme, onThemeChange } from "./theme";
import { HOST_STYLE_VARIABLES } from "./host-styles";
diff --git a/examples/basic-host/src/index.tsx b/examples/basic-host/src/index.tsx
index 00c0d52..3d488a7 100644
--- a/examples/basic-host/src/index.tsx
+++ b/examples/basic-host/src/index.tsx
@@ -1,5 +1,5 @@
import { getToolUiResourceUri, McpUiToolMetaSchema } from "@modelcontextprotocol/ext-apps/app-bridge";
-import type { Tool } from "@modelcontextprotocol/client";
+import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { Component, type ErrorInfo, type ReactNode, StrictMode, Suspense, use, useEffect, useMemo, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import { callTool, connectToServer, hasAppHtml, initializeApp, loadSandboxProxy, log, newAppBridge, type ServerInfo, type ToolCallInfo, type ModelContext, type AppMessage } from "./implementation";
diff --git a/examples/basic-host/tsconfig.json b/examples/basic-host/tsconfig.json
index 3c143ca..9d7c3bc 100644
--- a/examples/basic-host/tsconfig.json
+++ b/examples/basic-host/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-preact/main.ts b/examples/basic-server-preact/main.ts
index ffdff52..7642632 100644
--- a/examples/basic-server-preact/main.ts
+++ b/examples/basic-server-preact/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/basic-server-preact/package.json b/examples/basic-server-preact/package.json
index 60aa40d..353d5f0 100644
--- a/examples/basic-server-preact/package.json
+++ b/examples/basic-server-preact/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-basic-preact",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Basic MCP App Server example using Preact",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-preact"
},
"license": "MIT",
@@ -24,15 +24,12 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"preact": "^10.0.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@preact/preset-vite": "^2.0.0",
diff --git a/examples/basic-server-preact/server.ts b/examples/basic-server-preact/server.ts
index dfec94a..bf14ea5 100644
--- a/examples/basic-server-preact/server.ts
+++ b/examples/basic-server-preact/server.ts
@@ -1,12 +1,8 @@
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
-import { z } from "zod";
// Works both from source (server.ts) and compiled (dist/server.js)
const DIST_DIR = import.meta.filename.endsWith(".ts")
? path.join(import.meta.dirname, "dist")
@@ -32,7 +28,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
- inputSchema: z.object({}),
+ inputSchema: {},
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
},
async (): Promise => {
diff --git a/examples/basic-server-preact/src/mcp-app.tsx b/examples/basic-server-preact/src/mcp-app.tsx
index 351a8ec..a8bdc68 100644
--- a/examples/basic-server-preact/src/mcp-app.tsx
+++ b/examples/basic-server-preact/src/mcp-app.tsx
@@ -8,7 +8,7 @@ import {
applyHostStyleVariables,
type McpUiHostContext,
} from "@modelcontextprotocol/ext-apps";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { useCallback, useEffect, useState } from "preact/hooks";
import { render } from "preact";
import styles from "./mcp-app.module.css";
diff --git a/examples/basic-server-preact/tsconfig.json b/examples/basic-server-preact/tsconfig.json
index 9bf9389..32182d9 100644
--- a/examples/basic-server-preact/tsconfig.json
+++ b/examples/basic-server-preact/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-preact/tsconfig.server.json b/examples/basic-server-preact/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/basic-server-preact/tsconfig.server.json
+++ b/examples/basic-server-preact/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/basic-server-react/main.ts b/examples/basic-server-react/main.ts
index 21c5e18..ec187b6 100644
--- a/examples/basic-server-react/main.ts
+++ b/examples/basic-server-react/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/basic-server-react/package.json b/examples/basic-server-react/package.json
index ea51a8a..a8bdfa8 100644
--- a/examples/basic-server-react/package.json
+++ b/examples/basic-server-react/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-basic-react",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Basic MCP App Server example using React",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-react"
},
"license": "MIT",
@@ -34,16 +34,13 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/basic-server-react/server.ts b/examples/basic-server-react/server.ts
index d77c908..23f6dda 100644
--- a/examples/basic-server-react/server.ts
+++ b/examples/basic-server-react/server.ts
@@ -1,12 +1,8 @@
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
-import { z } from "zod";
// Works both from source (server.ts) and compiled (dist/server.js)
const DIST_DIR = import.meta.filename.endsWith(".ts")
@@ -33,7 +29,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
- inputSchema: z.object({}),
+ inputSchema: {},
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
},
async (): Promise => {
diff --git a/examples/basic-server-react/src/mcp-app.tsx b/examples/basic-server-react/src/mcp-app.tsx
index 2b5fbef..6bb5540 100644
--- a/examples/basic-server-react/src/mcp-app.tsx
+++ b/examples/basic-server-react/src/mcp-app.tsx
@@ -3,7 +3,7 @@
*/
import type { App, McpUiHostContext } from "@modelcontextprotocol/ext-apps";
import { useApp } from "@modelcontextprotocol/ext-apps/react";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { StrictMode, useCallback, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import styles from "./mcp-app.module.css";
diff --git a/examples/basic-server-react/tsconfig.json b/examples/basic-server-react/tsconfig.json
index 3ecbdad..fc3c210 100644
--- a/examples/basic-server-react/tsconfig.json
+++ b/examples/basic-server-react/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-react/tsconfig.server.json b/examples/basic-server-react/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/basic-server-react/tsconfig.server.json
+++ b/examples/basic-server-react/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/basic-server-solid/main.ts b/examples/basic-server-solid/main.ts
index 095ef8d..c8d9de2 100644
--- a/examples/basic-server-solid/main.ts
+++ b/examples/basic-server-solid/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/basic-server-solid/package.json b/examples/basic-server-solid/package.json
index b3ca909..1925ae1 100644
--- a/examples/basic-server-solid/package.json
+++ b/examples/basic-server-solid/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-basic-solid",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Basic MCP App Server example using Solid",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-solid"
},
"license": "MIT",
@@ -24,15 +24,12 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"solid-js": "1.9.10",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/basic-server-solid/server.ts b/examples/basic-server-solid/server.ts
index addb522..2ed2d93 100644
--- a/examples/basic-server-solid/server.ts
+++ b/examples/basic-server-solid/server.ts
@@ -1,12 +1,8 @@
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
-import { z } from "zod";
// Works both from source (server.ts) and compiled (dist/server.js)
const DIST_DIR = import.meta.filename.endsWith(".ts")
? path.join(import.meta.dirname, "dist")
@@ -32,7 +28,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
- inputSchema: z.object({}),
+ inputSchema: {},
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
},
async (): Promise => {
diff --git a/examples/basic-server-solid/src/mcp-app.tsx b/examples/basic-server-solid/src/mcp-app.tsx
index 2d78b7b..66d47f3 100644
--- a/examples/basic-server-solid/src/mcp-app.tsx
+++ b/examples/basic-server-solid/src/mcp-app.tsx
@@ -8,7 +8,7 @@ import {
applyHostStyleVariables,
type McpUiHostContext,
} from "@modelcontextprotocol/ext-apps";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { createEffect, createSignal, onMount, Show } from "solid-js";
import { render } from "solid-js/web";
import styles from "./mcp-app.module.css";
diff --git a/examples/basic-server-solid/tsconfig.json b/examples/basic-server-solid/tsconfig.json
index bb13a01..84f0efe 100644
--- a/examples/basic-server-solid/tsconfig.json
+++ b/examples/basic-server-solid/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-solid/tsconfig.server.json b/examples/basic-server-solid/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/basic-server-solid/tsconfig.server.json
+++ b/examples/basic-server-solid/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/basic-server-svelte/main.ts b/examples/basic-server-svelte/main.ts
index 734ec0c..6c50a25 100644
--- a/examples/basic-server-svelte/main.ts
+++ b/examples/basic-server-svelte/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/basic-server-svelte/package.json b/examples/basic-server-svelte/package.json
index 19fe4c9..c7fef6b 100644
--- a/examples/basic-server-svelte/package.json
+++ b/examples/basic-server-svelte/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-basic-svelte",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Basic MCP App Server example using Svelte",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-svelte"
},
"license": "MIT",
@@ -24,15 +24,12 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"svelte": "^5.0.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^5.0.0",
diff --git a/examples/basic-server-svelte/server.ts b/examples/basic-server-svelte/server.ts
index 9414285..da603ca 100644
--- a/examples/basic-server-svelte/server.ts
+++ b/examples/basic-server-svelte/server.ts
@@ -1,12 +1,8 @@
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
-import { z } from "zod";
// Works both from source (server.ts) and compiled (dist/server.js)
const DIST_DIR = import.meta.filename.endsWith(".ts")
? path.join(import.meta.dirname, "dist")
@@ -32,7 +28,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
- inputSchema: z.object({}),
+ inputSchema: {},
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
},
async (): Promise => {
diff --git a/examples/basic-server-svelte/src/App.svelte b/examples/basic-server-svelte/src/App.svelte
index cabcc31..fde128d 100644
--- a/examples/basic-server-svelte/src/App.svelte
+++ b/examples/basic-server-svelte/src/App.svelte
@@ -7,7 +7,7 @@ import {
applyHostStyleVariables,
type McpUiHostContext,
} from "@modelcontextprotocol/ext-apps";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
function extractTime(result: CallToolResult): string {
const { text } = result.content?.find((c) => c.type === "text")!;
diff --git a/examples/basic-server-svelte/tsconfig.json b/examples/basic-server-svelte/tsconfig.json
index 4fd3cd7..5edb66a 100644
--- a/examples/basic-server-svelte/tsconfig.json
+++ b/examples/basic-server-svelte/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-svelte/tsconfig.server.json b/examples/basic-server-svelte/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/basic-server-svelte/tsconfig.server.json
+++ b/examples/basic-server-svelte/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/basic-server-vanillajs/main.ts b/examples/basic-server-vanillajs/main.ts
index 4de7331..286fa34 100644
--- a/examples/basic-server-vanillajs/main.ts
+++ b/examples/basic-server-vanillajs/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/basic-server-vanillajs/package.json b/examples/basic-server-vanillajs/package.json
index ebfebbb..72b6ed2 100644
--- a/examples/basic-server-vanillajs/package.json
+++ b/examples/basic-server-vanillajs/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-basic-vanillajs",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Basic MCP App Server example using vanilla JavaScript",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-vanillajs"
},
"license": "MIT",
@@ -24,14 +24,11 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/basic-server-vanillajs/server.ts b/examples/basic-server-vanillajs/server.ts
index 693d861..5b2daf7 100644
--- a/examples/basic-server-vanillajs/server.ts
+++ b/examples/basic-server-vanillajs/server.ts
@@ -1,9 +1,6 @@
import { registerAppResource, registerAppTool, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
@@ -32,7 +29,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
- inputSchema: z.object({}),
+ inputSchema: {},
outputSchema: z.object({
time: z.string(),
}),
diff --git a/examples/basic-server-vanillajs/src/mcp-app.ts b/examples/basic-server-vanillajs/src/mcp-app.ts
index 6b5084a..c3168c2 100644
--- a/examples/basic-server-vanillajs/src/mcp-app.ts
+++ b/examples/basic-server-vanillajs/src/mcp-app.ts
@@ -8,7 +8,7 @@ import {
applyHostStyleVariables,
type McpUiHostContext,
} from "@modelcontextprotocol/ext-apps";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import "./global.css";
import "./mcp-app.css";
diff --git a/examples/basic-server-vanillajs/tsconfig.json b/examples/basic-server-vanillajs/tsconfig.json
index b028ff5..535267b 100644
--- a/examples/basic-server-vanillajs/tsconfig.json
+++ b/examples/basic-server-vanillajs/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-vanillajs/tsconfig.server.json b/examples/basic-server-vanillajs/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/basic-server-vanillajs/tsconfig.server.json
+++ b/examples/basic-server-vanillajs/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/basic-server-vue/main.ts b/examples/basic-server-vue/main.ts
index d774269..669a071 100644
--- a/examples/basic-server-vue/main.ts
+++ b/examples/basic-server-vue/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/basic-server-vue/package.json b/examples/basic-server-vue/package.json
index 858dd89..cb924b4 100644
--- a/examples/basic-server-vue/package.json
+++ b/examples/basic-server-vue/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-basic-vue",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Basic MCP App Server example using Vue",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/basic-server-vue"
},
"license": "MIT",
@@ -24,15 +24,12 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"vue": "^3.5.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/basic-server-vue/server.ts b/examples/basic-server-vue/server.ts
index 9adb911..5aa72de 100644
--- a/examples/basic-server-vue/server.ts
+++ b/examples/basic-server-vue/server.ts
@@ -1,12 +1,8 @@
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { CallToolResult, ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
-import { z } from "zod";
// Works both from source (server.ts) and compiled (dist/server.js)
const DIST_DIR = import.meta.filename.endsWith(".ts")
? path.join(import.meta.dirname, "dist")
@@ -32,7 +28,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time as an ISO 8601 string.",
- inputSchema: z.object({}),
+ inputSchema: {},
_meta: { ui: { resourceUri } }, // Links this tool to its UI resource
},
async (): Promise => {
diff --git a/examples/basic-server-vue/src/App.vue b/examples/basic-server-vue/src/App.vue
index a45c262..196ad64 100644
--- a/examples/basic-server-vue/src/App.vue
+++ b/examples/basic-server-vue/src/App.vue
@@ -7,7 +7,7 @@ import {
applyHostStyleVariables,
type McpUiHostContext,
} from "@modelcontextprotocol/ext-apps";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
function extractTime(result: CallToolResult): string {
const { text } = result.content?.find((c) => c.type === "text")!;
diff --git a/examples/basic-server-vue/tsconfig.json b/examples/basic-server-vue/tsconfig.json
index 4fd3cd7..5edb66a 100644
--- a/examples/basic-server-vue/tsconfig.json
+++ b/examples/basic-server-vue/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/basic-server-vue/tsconfig.server.json b/examples/basic-server-vue/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/basic-server-vue/tsconfig.server.json
+++ b/examples/basic-server-vue/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/budget-allocator-server/main.ts b/examples/budget-allocator-server/main.ts
index 02c03cb..cc488f0 100644
--- a/examples/budget-allocator-server/main.ts
+++ b/examples/budget-allocator-server/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/budget-allocator-server/package.json b/examples/budget-allocator-server/package.json
index 6e41622..ded5814 100644
--- a/examples/budget-allocator-server/package.json
+++ b/examples/budget-allocator-server/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-budget-allocator",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Budget allocator MCP App Server with interactive visualization",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/budget-allocator-server"
},
"license": "MIT",
@@ -26,14 +26,12 @@
"serve": "bun --watch main.ts"
},
"dependencies": {
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"chart.js": "^4.4.0",
"cors": "^2.8.5",
"express": "^5.1.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/budget-allocator-server/server.ts b/examples/budget-allocator-server/server.ts
index 01f5dbf..04fba11 100755
--- a/examples/budget-allocator-server/server.ts
+++ b/examples/budget-allocator-server/server.ts
@@ -4,11 +4,11 @@
* Provides budget configuration, 24 months of historical allocation data,
* and industry benchmarks by company stage.
*/
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type {
+ CallToolResult,
+ ReadResourceResult,
+} from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
@@ -270,7 +270,7 @@ export function createServer(): McpServer {
title: "Get Budget Data",
description:
"Returns budget configuration with 24 months of historical allocations and industry benchmarks by company stage. The widget is interactive and exposes tools for reading/modifying allocations, adjusting budgets, and comparing against industry benchmarks.",
- inputSchema: z.object({}),
+ inputSchema: {},
outputSchema: BudgetDataResponseSchema,
_meta: { ui: { resourceUri } },
},
diff --git a/examples/budget-allocator-server/tsconfig.json b/examples/budget-allocator-server/tsconfig.json
index b028ff5..535267b 100644
--- a/examples/budget-allocator-server/tsconfig.json
+++ b/examples/budget-allocator-server/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/budget-allocator-server/tsconfig.server.json b/examples/budget-allocator-server/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/budget-allocator-server/tsconfig.server.json
+++ b/examples/budget-allocator-server/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/cohort-heatmap-server/main.ts b/examples/cohort-heatmap-server/main.ts
index 8b33603..d59903b 100644
--- a/examples/cohort-heatmap-server/main.ts
+++ b/examples/cohort-heatmap-server/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/cohort-heatmap-server/package.json b/examples/cohort-heatmap-server/package.json
index 7e2d2e2..9f82c64 100644
--- a/examples/cohort-heatmap-server/package.json
+++ b/examples/cohort-heatmap-server/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-cohort-heatmap",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Cohort heatmap MCP App Server for retention analysis",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/cohort-heatmap-server"
},
"license": "MIT",
@@ -26,15 +26,13 @@
"serve": "bun --watch main.ts"
},
"dependencies": {
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/cohort-heatmap-server/server.ts b/examples/cohort-heatmap-server/server.ts
index 129e2dc..6d07144 100644
--- a/examples/cohort-heatmap-server/server.ts
+++ b/examples/cohort-heatmap-server/server.ts
@@ -1,7 +1,5 @@
-import {
- McpServer,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type { ReadResourceResult } from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
@@ -180,8 +178,8 @@ export function createServer(): McpServer {
title: "Get Cohort Retention Data",
description:
"Returns cohort retention heatmap data showing customer retention over time by signup month",
- inputSchema: GetCohortDataInputSchema,
- outputSchema: CohortDataSchema,
+ inputSchema: GetCohortDataInputSchema.shape,
+ outputSchema: CohortDataSchema.shape,
_meta: { ui: { resourceUri } },
},
async ({ metric, periodType, cohortCount, maxPeriods }) => {
diff --git a/examples/cohort-heatmap-server/tsconfig.json b/examples/cohort-heatmap-server/tsconfig.json
index 3ecbdad..fc3c210 100644
--- a/examples/cohort-heatmap-server/tsconfig.json
+++ b/examples/cohort-heatmap-server/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/cohort-heatmap-server/tsconfig.server.json b/examples/cohort-heatmap-server/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/cohort-heatmap-server/tsconfig.server.json
+++ b/examples/cohort-heatmap-server/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/customer-segmentation-server/main.ts b/examples/customer-segmentation-server/main.ts
index c788757..130076c 100644
--- a/examples/customer-segmentation-server/main.ts
+++ b/examples/customer-segmentation-server/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/customer-segmentation-server/package.json b/examples/customer-segmentation-server/package.json
index bac3b89..aa52b35 100644
--- a/examples/customer-segmentation-server/package.json
+++ b/examples/customer-segmentation-server/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-customer-segmentation",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Customer segmentation MCP App Server with filtering",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/customer-segmentation-server"
},
"license": "MIT",
@@ -26,14 +26,12 @@
"serve": "bun --watch main.ts"
},
"dependencies": {
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"chart.js": "^4.4.0",
"cors": "^2.8.5",
"express": "^5.1.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/customer-segmentation-server/server.ts b/examples/customer-segmentation-server/server.ts
index 7cc1c1f..244415d 100644
--- a/examples/customer-segmentation-server/server.ts
+++ b/examples/customer-segmentation-server/server.ts
@@ -1,8 +1,8 @@
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type {
+ CallToolResult,
+ ReadResourceResult,
+} from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
@@ -100,8 +100,8 @@ export function createServer(): McpServer {
title: "Get Customer Data",
description:
"Returns customer data with segment information for visualization. Optionally filter by segment.",
- inputSchema: GetCustomerDataInputSchema,
- outputSchema: GetCustomerDataOutputSchema,
+ inputSchema: GetCustomerDataInputSchema.shape,
+ outputSchema: GetCustomerDataOutputSchema.shape,
_meta: { ui: { resourceUri } },
},
async ({ segment }): Promise => {
diff --git a/examples/customer-segmentation-server/tsconfig.json b/examples/customer-segmentation-server/tsconfig.json
index b028ff5..535267b 100644
--- a/examples/customer-segmentation-server/tsconfig.json
+++ b/examples/customer-segmentation-server/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/customer-segmentation-server/tsconfig.server.json b/examples/customer-segmentation-server/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/customer-segmentation-server/tsconfig.server.json
+++ b/examples/customer-segmentation-server/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/debug-server/main.ts b/examples/debug-server/main.ts
index 9fb0825..3c89a97 100644
--- a/examples/debug-server/main.ts
+++ b/examples/debug-server/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/debug-server/package.json b/examples/debug-server/package.json
index e28f4d3..1c505e2 100644
--- a/examples/debug-server/package.json
+++ b/examples/debug-server/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-debug",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "Debug MCP App Server for testing all SDK capabilities",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/debug-server"
},
"license": "MIT",
@@ -34,11 +34,9 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
- "zod": "^4.2.0"
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/debug-server/server.ts b/examples/debug-server/server.ts
index 068f94d..fd3452a 100644
--- a/examples/debug-server/server.ts
+++ b/examples/debug-server/server.ts
@@ -3,11 +3,11 @@ import {
registerAppTool,
RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type {
+ CallToolResult,
+ ReadResourceResult,
+} from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import { appendFileSync } from "node:fs";
import path from "node:path";
diff --git a/examples/debug-server/tsconfig.json b/examples/debug-server/tsconfig.json
index b028ff5..535267b 100644
--- a/examples/debug-server/tsconfig.json
+++ b/examples/debug-server/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/debug-server/tsconfig.server.json b/examples/debug-server/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/debug-server/tsconfig.server.json
+++ b/examples/debug-server/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/integration-server/main.ts b/examples/integration-server/main.ts
index 9a42842..c45787e 100644
--- a/examples/integration-server/main.ts
+++ b/examples/integration-server/main.ts
@@ -4,10 +4,10 @@
* Or: node dist/index.js [--stdio]
*/
-import { createMcpExpressApp } from "@modelcontextprotocol/express";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import type { McpServer } from "@modelcontextprotocol/server";
-import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
+import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import cors from "cors";
import type { Request, Response } from "express";
import { createServer } from "./server.js";
@@ -27,7 +27,7 @@ export async function startStreamableHTTPServer(
app.all("/mcp", async (req: Request, res: Response) => {
const server = createServer();
- const transport = new NodeStreamableHTTPServerTransport({
+ const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
diff --git a/examples/integration-server/package.json b/examples/integration-server/package.json
index 7e04f4a..2104382 100644
--- a/examples/integration-server/package.json
+++ b/examples/integration-server/package.json
@@ -1,6 +1,6 @@
{
"name": "integration-server",
- "version": "2.0.0",
+ "version": "1.7.3",
"private": true,
"type": "module",
"scripts": {
@@ -15,16 +15,13 @@
"serve": "bun --watch main.ts"
},
"dependencies": {
- "@modelcontextprotocol/client": "2.0.0",
- "@modelcontextprotocol/express": "2.0.0",
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
- "zod": "^4.2.0"
+ "zod": "^4.1.13"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/integration-server/server.ts b/examples/integration-server/server.ts
index 346f4f7..e44689d 100644
--- a/examples/integration-server/server.ts
+++ b/examples/integration-server/server.ts
@@ -3,11 +3,11 @@ import {
registerAppTool,
RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
-import {
- McpServer,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import type {
+ CallToolResult,
+ ReadResourceResult,
+} from "@modelcontextprotocol/sdk/types.js";
import fs from "node:fs/promises";
import path from "node:path";
import { z } from "zod";
@@ -33,7 +33,7 @@ export function createServer(): McpServer {
{
title: "Get Time",
description: "Returns the current server time.",
- inputSchema: z.object({}),
+ inputSchema: {},
outputSchema: z.object({
time: z.string(),
}),
@@ -72,7 +72,7 @@ export function createServer(): McpServer {
);
// Sample downloadable resource — used to demo ResourceLink in ui/download-file
- server.registerResource(
+ server.resource(
SAMPLE_DOWNLOAD_URI,
SAMPLE_DOWNLOAD_URI,
{
diff --git a/examples/integration-server/src/mcp-app.tsx b/examples/integration-server/src/mcp-app.tsx
index 281a356..7f0b18c 100644
--- a/examples/integration-server/src/mcp-app.tsx
+++ b/examples/integration-server/src/mcp-app.tsx
@@ -3,7 +3,7 @@
*/
import type { App, McpUiHostContext } from "@modelcontextprotocol/ext-apps";
import { useApp } from "@modelcontextprotocol/ext-apps/react";
-import type { CallToolResult } from "@modelcontextprotocol/client";
+import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { StrictMode, useCallback, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import styles from "./mcp-app.module.css";
diff --git a/examples/integration-server/tsconfig.json b/examples/integration-server/tsconfig.json
index 3ecbdad..fc3c210 100644
--- a/examples/integration-server/tsconfig.json
+++ b/examples/integration-server/tsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node", "vite/client"],
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
diff --git a/examples/integration-server/tsconfig.server.json b/examples/integration-server/tsconfig.server.json
index 27eb28f..05ddd8e 100644
--- a/examples/integration-server/tsconfig.server.json
+++ b/examples/integration-server/tsconfig.server.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "types": ["node"],
"target": "ES2022",
"lib": ["ES2022"],
"module": "NodeNext",
diff --git a/examples/lazy-auth-server/README.md b/examples/lazy-auth-server/README.md
index f2e7545..769ac18 100644
--- a/examples/lazy-auth-server/README.md
+++ b/examples/lazy-auth-server/README.md
@@ -51,33 +51,6 @@ https:///ttl/3600/mcp ← tokens for this connection live 1 hour
This works through [RFC 8707 resource indicators](https://www.rfc-editor.org/rfc/rfc8707): MCP hosts send the MCP server URL as the `resource` parameter in OAuth authorization and token requests, and this server issues tokens for that grant with the lifetime encoded in the path (refresh tokens are extended to at least match). The TTL is a _path_ segment rather than a query param because hosts canonicalize resource indicators and strip query strings. Each TTL endpoint also enforces its value as a maximum token age, so connecting to a path with a _lower_ TTL than a token's issued lifetime forces the refresh flow. To exercise the full **re-auth** flow, call the `revoke_auth_token` tool.
-## Mounting under a base path
-
-`createApp()` can also be mounted inside another Express app, so an existing server can host this example at a sub-path of its own origin:
-
-```ts
-import { createApp } from "@modelcontextprotocol/server-lazy-auth";
-
-hostApp.use("/lazy-auth", createApp());
-// → MCP endpoint at https:///lazy-auth/mcp
-```
-
-All advertised URLs (OAuth metadata, `WWW-Authenticate` `resource_metadata`, PRM `resource`, elicitation callbacks) include the mount path automatically, derived from Express's `req.baseUrl`. When `PUBLIC_URL` is set, it must include the mount path (e.g. `https://example.com/lazy-auth`).
-
-One thing the mounted app cannot do for itself: [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414#section-3) / [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728) put well-known discovery documents at the _root_ of the origin with the path inserted after the well-known prefix (`/.well-known/oauth-authorization-server/lazy-auth`), and MCP SDK clients only try that insertion form. The host app must rewrite those root paths into the mount before its other routes:
-
-```ts
-hostApp.use((req, _res, next) => {
- const m = req.url.match(
- /^\/\.well-known\/(oauth-authorization-server|oauth-protected-resource)\/lazy-auth(\/.*)?$/,
- );
- if (m) req.url = `/lazy-auth/.well-known/${m[1]}${m[2] ?? ""}`;
- next();
-});
-```
-
-Rewriting into the mount (rather than calling the sub-app directly) keeps `req.baseUrl` — and therefore every advertised URL — consistent.
-
## How It Works
1. **Connect without auth** — `initialize`, `tools/list`, and public tool calls succeed with no `Authorization` header.
diff --git a/examples/lazy-auth-server/package.json b/examples/lazy-auth-server/package.json
index dbd4601..8b7cb3f 100644
--- a/examples/lazy-auth-server/package.json
+++ b/examples/lazy-auth-server/package.json
@@ -1,11 +1,11 @@
{
"name": "@modelcontextprotocol/server-lazy-auth",
- "version": "2.0.0",
+ "version": "1.7.3",
"type": "module",
"description": "MCP App example demonstrating lazy (on-demand) OAuth: public tools work unauthenticated, protected tools return 401 + WWW-Authenticate so the host runs the OAuth flow only when needed",
"repository": {
"type": "git",
- "url": "git+https://github.com/modelcontextprotocol/ext-apps.git",
+ "url": "https://github.com/modelcontextprotocol/ext-apps",
"directory": "examples/lazy-auth-server"
},
"license": "MIT",
@@ -22,13 +22,11 @@
"prepublishOnly": "npm run build"
},
"dependencies": {
- "@modelcontextprotocol/ext-apps": "^2.0.0",
- "@modelcontextprotocol/node": "2.0.0",
- "@modelcontextprotocol/server": "2.0.0",
+ "@modelcontextprotocol/ext-apps": "^1.7.0",
+ "@modelcontextprotocol/sdk": "^1.29.0",
"cors": "^2.8.5",
"express": "^5.1.0",
- "jose": "^6.0.0",
- "zod": "^4.2.0"
+ "jose": "^6.0.0"
},
"devDependencies": {
"@types/cors": "^2.8.19",
diff --git a/examples/lazy-auth-server/server.ts b/examples/lazy-auth-server/server.ts
index 5240008..fa46d67 100644
--- a/examples/lazy-auth-server/server.ts
+++ b/examples/lazy-auth-server/server.ts
@@ -21,30 +21,24 @@ import {
registerAppTool,
RESOURCE_MIME_TYPE,
} from "@modelcontextprotocol/ext-apps/server";
-import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
-import {
- McpServer,
- UrlElicitationRequiredError,
- type CallToolResult,
- type ReadResourceResult,
-} from "@modelcontextprotocol/server";
+import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
+import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
+import { UrlElicitationRequiredError } from "@modelcontextprotocol/sdk/types.js";
+import type {
+ CallToolResult,
+ ReadResourceResult,
+} from "@modelcontextprotocol/sdk/types.js";
import cors from "cors";
import express, { type Express, type Request, type Response } from "express";
import { SignJWT, jwtVerify } from "jose";
import crypto from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
-import { fileURLToPath } from "node:url";
-import { z } from "zod";
-// Works both from source (server.ts) and compiled (dist/server.js). Derived
-// from import.meta.url rather than import.meta.filename/dirname, which are
-// undefined in some module-VM contexts (e.g. importing this package from
-// jest).
-const SERVER_FILE = fileURLToPath(import.meta.url);
-const DIST_DIR = SERVER_FILE.endsWith(".ts")
- ? path.join(path.dirname(SERVER_FILE), "dist")
- : path.dirname(SERVER_FILE);
+// Works both from source (server.ts) and compiled (dist/server.js)
+const DIST_DIR = import.meta.filename.endsWith(".ts")
+ ? path.join(import.meta.dirname, "dist")
+ : import.meta.dirname;
// ─── Config ──────────────────────────────────────────────────────────────────
@@ -94,35 +88,25 @@ function isLoopbackHostname(hostname: string): boolean {
* explicitly.
*/
function resolvePublicUrl(req?: Request): URL {
- // Mount path when this app is mounted inside another Express app
- // (e.g. app.use("/lazy-auth", createApp())). Empty when standalone.
- // PUBLIC_URL, when set, must already include any mount path.
- const basePath = req?.baseUrl ?? "";
const envUrl = process.env.PUBLIC_URL;
if (envUrl) return new URL(envUrl.endsWith("/") ? envUrl : envUrl + "/");
const host = req?.headers.host;
if (host) {
try {
- const url = new URL(`http://${host}${basePath}/`);
+ const url = new URL(`http://${host}/`);
if (isLoopbackHostname(url.hostname)) return url;
} catch {
// Malformed Host header → fall through to the localhost default.
}
}
- return new URL(`http://localhost:${PORT}${basePath}/`);
-}
-
-/** Public base URL as a string with no trailing slash (may include a base path). */
-function publicBaseHref(req?: Request): string {
- const href = resolvePublicUrl(req).href;
- return href.endsWith("/") ? href.slice(0, -1) : href;
+ return new URL(`http://localhost:${PORT}/`);
}
/** OAuth issuer. In REACTIVE_AUTH_ONLY mode, uses a /auth subpath so root well-known 404s.
- * Otherwise uses the public base URL (origin + any mount path). */
+ * Otherwise uses the root origin (standard). */
const ISSUER_SUFFIX = REACTIVE_AUTH_ONLY ? "/auth" : "";
function resolveIssuer(req?: Request): string {
- return publicBaseHref(req) + ISSUER_SUFFIX;
+ return resolvePublicUrl(req).origin + ISSUER_SUFFIX;
}
// ─── Mock OAuth (HS256, stateless codes) ─────────────────────────────────────
@@ -289,28 +273,18 @@ async function signAuthCode(
return new SignJWT({ ...payload, typ: "code" })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
- .setJti(crypto.randomUUID())
.setIssuer(issuer)
.setExpirationTime("5m")
.sign(JWT_SECRET);
}
-// Authorization codes are single-use (RFC 6749 §4.1.2): remember redeemed code
-// IDs until the code's own 5-minute expiry makes replay impossible anyway.
-const redeemedCodeJtis = new Map(); // jti → unix GC time
-
async function verifyAuthCode(
code: string,
issuer: string,
): Promise {
try {
const { payload } = await jwtVerify(code, JWT_SECRET, { issuer });
- if (payload.typ !== "code" || !payload.jti) return undefined;
- const now = Math.floor(Date.now() / 1000);
- for (const [k, gc] of redeemedCodeJtis)
- if (gc < now) redeemedCodeJtis.delete(k);
- if (redeemedCodeJtis.has(payload.jti)) return undefined; // already redeemed
- redeemedCodeJtis.set(payload.jti, now + 5 * 60 + 10);
+ if (payload.typ !== "code") return undefined;
return payload as unknown as CodePayload;
} catch {
return undefined;
@@ -392,21 +366,11 @@ async function handleAuthorize(req: Request, res: Response) {
});
return;
}
- // PKCE is mandatory (the MCP auth spec requires it of clients, and this AS
- // only advertises S256). Rejecting up front keeps stolen-code attacks out of
- // the demo even though it has no real data to protect.
- if (!code_challenge || code_challenge_method !== "S256") {
- res.status(400).json({
- error: "invalid_request",
- error_description: "PKCE with S256 code_challenge is required",
- });
- return;
- }
const issuer = resolveIssuer(req);
if (approved !== "1") {
// Show consent page. Keeps the OAuth popup visible so users can see the flow.
- const approveUrl = new URL(publicBaseHref(req) + "/authorize");
+ const approveUrl = new URL(resolvePublicUrl(req).origin + "/authorize");
for (const [k, v] of Object.entries(req.query))
if (v) approveUrl.searchParams.set(k, String(v));
approveUrl.searchParams.set("approved", "1");
@@ -415,36 +379,20 @@ async function handleAuthorize(req: Request, res: Response) {
if (state) denyUrl.searchParams.set("state", state);
res.type("text/html").send(/*html*/ `
Authorize
-
+button{padding:10px 20px;margin:4px;font-size:15px;font-weight:bold;border:none;border-radius:6px;cursor:pointer}
+.approve{background:#1a7a3e;color:#fff}.deny{background:#b91c1c;color:#fff}
🔑 Mock Authorization
An application is requesting access:
- Client
- ${escapeHtml(client_id ?? "(none)")}
- Scope
- ${escapeHtml(scope ?? "(default)")}
- Redirect
- ${escapeHtml(redirect_uri)}
-
-
-
-