refactor: finish MCP SDK v2 migration

This commit is contained in:
Andrew Khadder 2026-07-14 17:07:30 -07:00
commit f94cb38b0b
No known key found for this signature in database

View file

@ -115,19 +115,20 @@ Create a new MCP server with tool and resource registration. This wraps the exis
### Dependencies
```bash
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/client@2.0.0-beta.4 @modelcontextprotocol/core@2.0.0-beta.4 @modelcontextprotocol/server@2.0.0-beta.4 zod@^4.2.0
npm install -D tsx vite vite-plugin-singlefile
```
Use `npm install` to add dependencies rather than manually writing version numbers. This lets npm resolve the latest compatible versions. Never specify version numbers from memory.
Use `npm install` with the exact base MCP SDK prerelease required by ext-apps.
Do not substitute unpublished local packages or guess a different prerelease.
### Server Code
Create `server.ts`:
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
import fs from "node:fs/promises";
import path from "node:path";
@ -140,7 +141,9 @@ const resourceUri = "ui://my-app/mcp-app.html";
// Register the tool — inputSchema maps to the app's data sources
registerAppTool(server, "show-app", {
description: "Displays the app with the given parameters",
inputSchema: { query: z.string().describe("The search query") },
inputSchema: z.object({
query: z.string().describe("The search query"),
}),
_meta: { ui: { resourceUri } },
}, async (args) => {
// Process args server-side if needed
@ -151,19 +154,29 @@ registerAppTool(server, "show-app", {
});
// Register the HTML resource
registerAppResource(server, {
uri: resourceUri,
name: "My App UI",
mimeType: RESOURCE_MIME_TYPE,
// Add CSP domains from Step 2 if needed:
// _meta: { ui: { connectDomains: ["api.example.com"], resourceDomains: ["cdn.example.com"] } },
}, async () => {
const html = await fs.readFile(
path.resolve(import.meta.dirname, "dist", "mcp-app.html"),
"utf-8",
);
return { contents: [{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html }] };
});
registerAppResource(
server,
"My App UI",
resourceUri,
{},
async () => {
const html = await fs.readFile(
path.resolve(import.meta.dirname, "dist", "mcp-app.html"),
"utf-8",
);
return {
contents: [
{
uri: resourceUri,
mimeType: RESOURCE_MIME_TYPE,
text: html,
// Add CSP domains from Step 2 if needed:
// _meta: { ui: { csp: { connectDomains: ["https://api.example.com"], resourceDomains: ["https://cdn.example.com"] } } },
},
],
};
},
);
// Start the server
const transport = new StdioServerTransport();
@ -310,7 +323,10 @@ const data = await fetch("/api/data").then(r => r.json());
// After (hybrid):
async function fetchData(): Promise<any> {
if (isMcpApp) {
const result = await app.callServerTool("fetch-data", {});
const result = await app.callServerTool({
name: "fetch-data",
arguments: {},
});
return result.structuredContent;
}
return fetch("/api/data").then(r => r.json());
@ -437,7 +453,8 @@ registerAppTool(server, "refresh-data", {
});
```
The UI calls these via `app.callServerTool("refresh-data", {})`.
The UI calls these via
`app.callServerTool({ name: "refresh-data", arguments: {} })`.
### Streaming Partial Input