Migrate from deprecated Server to McpServer API#11
Conversation
The @modelcontextprotocol/sdk has deprecated the low-level `Server` class
in favor of the high-level `McpServer` class. This commit migrates the
entire codebase to use the new API patterns.
## Why This Migration Was Needed
The MCP TypeScript SDK deprecated several APIs:
1. **`Server` class** - The low-level server class from
`@modelcontextprotocol/sdk/server/index.js` is deprecated in favor of
`McpServer` from `@modelcontextprotocol/sdk/server/mcp.js`
2. **`setRequestHandler()` pattern** - The centralized request handler
pattern with schema imports (CallToolRequestSchema, ListResourcesRequestSchema,
etc.) is deprecated in favor of individual registration methods
3. **`server.tool()`, `server.resource()`, `server.prompt()`** - The
3-argument shorthand signatures are deprecated in favor of the
`register*` methods with explicit metadata objects
## What Changed
### Import Changes
- Removed: `Server` from `@modelcontextprotocol/sdk/server/index.js`
- Removed: All `*Schema` imports from `@modelcontextprotocol/sdk/types.js`
- Added: `McpServer` from `@modelcontextprotocol/sdk/server/mcp.js`
- Added: `zod` for schema definitions (required by new API)
### Constructor Changes
- Old: `new Server({ name, version }, { capabilities: {...} })`
- New: `new McpServer({ name, version })`
### Tool Registration
- Old: Centralized `setRequestHandler(CallToolRequestSchema, ...)` with
switch statement and JSON Schema objects
- New: Individual `server.registerTool(name, { title, description,
inputSchema }, handler)` calls with Zod schemas
### Resource Registration
- Old: `server.resource(name, uri, callback)`
- New: `server.registerResource(name, uri, { title, description,
mimeType }, callback)`
### Prompt Registration
- Old: `server.prompt(name, zodSchema, callback)`
- New: `server.registerPrompt(name, { title, description, argsSchema },
callback)`
## Benefits of New API
1. **Cleaner code** - Each tool/resource/prompt is self-contained with
its own registration call instead of centralized switch statements
2. **Better metadata** - The new API requires explicit `title` and
`description` fields, improving discoverability for MCP clients
3. **Type safety** - Zod schemas provide runtime validation and
TypeScript type inference for handler parameters
4. **Future compatibility** - Aligns with the SDK's direction as it
prepares for v2.0 release
## Dependencies
- Added `zod` as a runtime dependency (was already a peer dependency of
the MCP SDK)
- Added `esbuild` as a dev dependency for faster builds
- Updated build script to use `npx tsc` for better compatibility
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Migrates the MCP server implementation from the deprecated low-level Server API to the newer McpServer API in @modelcontextprotocol/sdk, updating resource/prompt/tool registration accordingly.
Changes:
- Replaced
Server+setRequestHandler(...)handlers withMcpServer+registerResource/registerPrompt/registerTool. - Added
zodfor tool/prompt schema definitions. - Updated build/watch scripts and refreshed lockfile to reflect updated dependency graph.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/index.ts |
Reworks server setup to use McpServer and per-resource/prompt/tool registration. |
package.json |
Adds zod, adds esbuild, and adjusts TypeScript build/watch scripts. |
package-lock.json |
Regenerated to include new dependencies and updated resolved versions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ); | ||
| const server = new McpServer({ | ||
| name: 'optics-mcp', | ||
| version: '0.1.0', |
There was a problem hiding this comment.
The server metadata version is hard-coded to "0.1.0", but package.json declares version "0.2.6". This will cause clients to see an incorrect server version; consider sourcing the version from package.json at build time or updating this value to match the published package version.
| version: '0.1.0', | |
| version: '0.2.6', |
| async (uri) => { | ||
| const category = uri.pathname.replace('/tokens/', '').replace('/', ''); | ||
|
|
||
| if (category === 'all') { | ||
| return { |
There was a problem hiding this comment.
This handler special-cases category === 'all', but optics://tokens/all is also registered as a separate tokens-all resource above. That creates two resource registrations that can match the same URI; consider removing the dedicated tokens-all resource or removing the all branch here to keep resource routing unambiguous.
| "build": "npx tsc", | ||
| "prepare": "npm run build", | ||
| "watch": "tsc --watch", | ||
| "watch": "npx tsc --watch", | ||
| "start": "node dist/index.js", |
There was a problem hiding this comment.
Using npx tsc inside npm scripts is usually unnecessary (npm already prefers the local node_modules/.bin/tsc) and can introduce extra overhead or unexpected behavior if a local install is missing. Consider switching back to tsc / tsc --watch for more deterministic builds.
| "devDependencies": { | ||
| "@types/node": "^20.10.0", | ||
| "typescript": "^5.3.0" | ||
| "esbuild": "^0.27.2", | ||
| "typescript": "^5.9.3" |
There was a problem hiding this comment.
esbuild is added to devDependencies, but there are no scripts or code references to it in this repo. Either add a build/dev script that uses esbuild (to justify the dependency) or remove it to avoid extra install size and native binary downloads.
| "dependencies": { | ||
| "@modelcontextprotocol/sdk": "^1.24.0" | ||
| "@modelcontextprotocol/sdk": "^1.24.0", | ||
| "zod": "^3.25.76" | ||
| }, |
There was a problem hiding this comment.
The lockfile now resolves @modelcontextprotocol/sdk to a version that depends on @hono/node-server (Node >=18.14.1 per package-lock). With engines.node currently set to ">=18.0.0", the package may claim compatibility with Node versions that your dependency tree no longer supports. Consider bumping the engines.node floor (or pinning the SDK) to match the effective minimum.
Jeremy-Walton
left a comment
There was a problem hiding this comment.
Not sure this is a valuable review since I don't really know what the code does.
With moving the various MD files into docs, does that affect the registered resources? Seems like they are pointing to those files and not sure if that will be broken
Why?
The
@modelcontextprotocol/sdkhas deprecated the low-levelServerclass and related APIs in favor of the high-levelMcpServerclass. This migration addresses deprecation warnings and aligns the codebase with the SDK's recommended patterns as it prepares for v2.0.Deprecated APIs being replaced:
Serverclass from@modelcontextprotocol/sdk/server/index.jssetRequestHandler()pattern with centralized schema importsserver.tool(),server.resource(),server.prompt()signaturesWhat Changed
ServerwithMcpServerfrom@modelcontextprotocol/sdk/server/mcp.jsCallToolRequestSchema,ListResourcesRequestSchema, etc.setRequestHandler(CallToolRequestSchema, ...)with switch statement to individualserver.registerTool()callsserver.resource(name, uri, callback)toserver.registerResource(name, uri, {title, description, mimeType}, callback)server.prompt(name, schema, callback)toserver.registerPrompt(name, {title, description, argsSchema}, callback)new Server({name, version}, {capabilities})tonew McpServer({name, version})Pre-merge checklist
package.json- Not updated (no breaking changes to consumers)Screenshots
N/A - No UI changes. This is a backend/SDK migration that maintains the same MCP server functionality.
Made with Cursor