Skip to content

Commit 14c2878

Browse files
Update quickstart guide
- Reorganize prerequisites to emphasize MCP concepts first - Add tip linking to official MCP quickstart for newcomers - Update install command to use published npm package - Remove zod dependency and `structuredContent` usage - Use `RESOURCE_MIME_TYPE` constant for consistency - Add clearer comments explaining tool/resource registration - Simplify UI to extract time from text content 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 6f14cf3 commit 14c2878

File tree

1 file changed

+30
-22
lines changed

1 file changed

+30
-22
lines changed

docs/quickstart.md

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ A simple app that fetches the current server time and displays it in a clickable
1515
1616
## Prerequisites
1717

18-
- Node.js 18+
18+
- Familiarity with MCP concepts, especially [Tools](https://modelcontextprotocol.io/docs/learn/server-concepts#tools) and [Resources](https://modelcontextprotocol.io/docs/learn/server-concepts#resources)
1919
- Familiarity with the [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
20+
- Node.js 18+
21+
22+
> [!TIP]
23+
> New to building MCP servers? Start with the [official MCP quickstart guide](https://modelcontextprotocol.io/docs/develop/build-server) to learn the core concepts first.
2024
2125
## 1. Project Setup
2226

@@ -30,7 +34,7 @@ npm init -y
3034
Install dependencies:
3135

3236
```bash
33-
npm install github:modelcontextprotocol/ext-apps @modelcontextprotocol/sdk zod
37+
npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk
3438
npm install -D typescript vite vite-plugin-singlefile express cors @types/express @types/cors tsx
3539
```
3640

@@ -97,62 +101,66 @@ Create `server.ts`:
97101
```typescript
98102
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
99103
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
100-
import { RESOURCE_URI_META_KEY } from "@modelcontextprotocol/ext-apps";
104+
import {
105+
RESOURCE_MIME_TYPE,
106+
RESOURCE_URI_META_KEY,
107+
} from "@modelcontextprotocol/ext-apps";
101108
import cors from "cors";
102109
import express from "express";
103110
import fs from "node:fs/promises";
104111
import path from "node:path";
105-
import * as z from "zod";
106112

107113
const server = new McpServer({
108114
name: "My MCP App Server",
109115
version: "1.0.0",
110116
});
111117

112-
// Two-part registration: tool + resource
118+
// Two-part registration: tool + resource, tied together by the resource URI.
113119
const resourceUri = "ui://get-time/mcp-app.html";
114120

121+
// Register a tool with UI metadata. When the host calls this tool, it reads
122+
// `_meta[RESOURCE_URI_META_KEY]` to know which resource to fetch and render as
123+
// an interactive UI.
115124
server.registerTool(
116125
"get-time",
117126
{
118127
title: "Get Time",
119128
description: "Returns the current server time.",
120129
inputSchema: {},
121-
outputSchema: { time: z.string() },
122-
_meta: { [RESOURCE_URI_META_KEY]: resourceUri }, // Links tool to UI
130+
_meta: { [RESOURCE_URI_META_KEY]: resourceUri },
123131
},
124132
async () => {
125133
const time = new Date().toISOString();
126134
return {
127135
content: [{ type: "text", text: time }],
128-
structuredContent: { time },
129136
};
130137
},
131138
);
132139

140+
// Register the resource, which returns the bundled HTML/JavaScript for the UI.
133141
server.registerResource(
134142
resourceUri,
135143
resourceUri,
136-
{ mimeType: "text/html;profile=mcp-app" },
144+
{ mimeType: RESOURCE_MIME_TYPE },
137145
async () => {
138146
const html = await fs.readFile(
139147
path.join(import.meta.dirname, "dist", "mcp-app.html"),
140148
"utf-8",
141149
);
142150
return {
143151
contents: [
144-
{ uri: resourceUri, mimeType: "text/html;profile=mcp-app", text: html },
152+
{ uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html },
145153
],
146154
};
147155
},
148156
);
149157

150-
// Express server for MCP endpoint
151-
const app = express();
152-
app.use(cors());
153-
app.use(express.json());
158+
// Start an Express server that exposes the MCP endpoint.
159+
const expressApp = express();
160+
expressApp.use(cors());
161+
expressApp.use(express.json());
154162

155-
app.post("/mcp", async (req, res) => {
163+
expressApp.post("/mcp", async (req, res) => {
156164
const transport = new StreamableHTTPServerTransport({
157165
sessionIdGenerator: undefined,
158166
enableJsonResponse: true,
@@ -162,7 +170,7 @@ app.post("/mcp", async (req, res) => {
162170
await transport.handleRequest(req, res, req.body);
163171
});
164172

165-
app.listen(3001, (err) => {
173+
expressApp.listen(3001, (err) => {
166174
if (err) {
167175
console.error("Error starting server:", err);
168176
process.exit(1);
@@ -177,7 +185,7 @@ app.listen(3001, (err) => {
177185
Then, verify your server compiles:
178186

179187
```bash
180-
npx tsc --noEmit server.ts
188+
npx tsc --noEmit
181189
```
182190

183191
No output means success. If you see errors, check for typos in `server.ts`.
@@ -217,30 +225,30 @@ const app = new App({ name: "Get Time App", version: "1.0.0" });
217225

218226
// Register handlers BEFORE connecting
219227
app.ontoolresult = (result) => {
220-
const { time } = (result.structuredContent as { time?: string }) ?? {};
228+
const time = result.content?.find((c) => c.type === "text")?.text;
221229
serverTimeEl.textContent = time ?? "[ERROR]";
222230
};
223231

224232
// Wire up button click
225233
getTimeBtn.addEventListener("click", async () => {
226234
const result = await app.callServerTool({ name: "get-time", arguments: {} });
227-
const { time } = (result.structuredContent as { time?: string }) ?? {};
235+
const time = result.content?.find((c) => c.type === "text")?.text;
228236
serverTimeEl.textContent = time ?? "[ERROR]";
229237
});
230238

231239
// Connect to host
232240
app.connect(new PostMessageTransport(window.parent));
233241
```
234242

243+
> [!NOTE]
244+
> **Full files:** [`mcp-app.html`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/mcp-app.html), [`src/mcp-app.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/src/mcp-app.ts)
245+
235246
Build the UI:
236247

237248
```bash
238249
npm run build
239250
```
240251

241-
> [!NOTE]
242-
> **Full files:** [`mcp-app.html`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/mcp-app.html), [`src/mcp-app.ts`](https://github.com/modelcontextprotocol/ext-apps/blob/main/examples/basic-server-vanillajs/src/mcp-app.ts)
243-
244252
This produces `dist/mcp-app.html` which contains your bundled app:
245253

246254
```console

0 commit comments

Comments
 (0)