Install
$ agentstack add skill-yigitkonur-skills-by-yigitkonur-build-mcp-server-sdk-v1 ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README — it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Build MCP Server (SDK v1.x)
Build and maintain MCP servers using @modelcontextprotocol/sdk v1.x — the single-package, Zod-based TypeScript SDK (protocol version 2025-11-25). Covers McpServer, registerTool, registerResource, registerPrompt, transports, OAuth 2.1, sessions, and deployment.
When to use this skill
- Building a new MCP server on
@modelcontextprotocol/sdkv1.x (single package) - Adding tools, resources, or prompts to an existing v1 server
- Migrating a v1 server from deprecated APIs (
tool(),SSEServerTransport, raw JSON Schema) to current ones (registerTool,StreamableHTTPServerTransport, Zod) - Wiring authentication on a v1 server — bearer token, OAuth 2.1 via
mcpAuthRouter, or custom middleware - Hardening transports, sessions, or capabilities on a v1 server (Origin validation, session resumability, sampling/elicitation)
- Diagnosing v1-specific runtime errors —
RequestHandlerExtraaccess, capability declarations, JSON Schema 2020-12 conversion
Do NOT use this skill when
- Project imports from
@modelcontextprotocol/server/@modelcontextprotocol/client/@modelcontextprotocol/node(split packages) → usebuild-mcp-server-sdk-v2 - Handlers receive
(args, ctx)withctx.mcpReq.log()/ctx.http?.authInfo(v2ServerContext) → usebuild-mcp-server-sdk-v2 - Goal is porting an existing v1 server to v2 (not new build, not v1 maintenance) → use
convert-mcp-sdk-v1-to-v2 - Project depends on the
mcp-usewrapper library, not the raw SDK → usebuild-mcp-use-server - Goal is an agentic-quality / hardening / context-budget audit beyond SDK correctness → use
audit-agentic-mcp
Detect v1 vs v2 (do this first)
Before writing any code, confirm v1 by checking three signals. Any one v2 signal means stop and route to a different skill.
| Signal | v1 (this skill) | v2 (build-mcp-server-sdk-v2) | |---|---|---| | package.json dependency | @modelcontextprotocol/sdk (single, ^1.x) | @modelcontextprotocol/server, /client, /node, /express, /hono (split, 2.0.0-alpha.x) | | Import path | @modelcontextprotocol/sdk/server/mcp.js, /server/stdio.js, /server/streamableHttp.js | @modelcontextprotocol/server, @modelcontextprotocol/node | | Handler signature | (args, extra) => … with extra.sendNotification, extra.authInfo, extra.signal flat | (args, ctx) => … with ctx.mcpReq.log(), ctx.mcpReq.signal, ctx.http?.authInfo | | HTTP transport class | StreamableHTTPServerTransport (or legacy SSEServerTransport) | NodeStreamableHTTPServerTransport | | Module system | CJS or ESM | ESM-only, "type": "module" required | | Node engine | Node 18+ | Node 20+ | | Zod | Zod v3, ZodRawShape accepted ({ name: z.string() }) | Zod v4, full z.object({...}) only |
Legacy low-level v1 code may also import request schemas like ListToolsRequestSchema, CallToolRequestSchema from @modelcontextprotocol/sdk/types.js and call server.setRequestHandler(...) directly. That is still v1 — but it is the deprecated low-level path; the skill recommends migrating it to McpServer.registerTool (see references/patterns/anti-patterns.md).
If package.json exists, run bash scripts/check-mcp-sdk-v1-version.sh [project-dir] (see scripts/check-mcp-sdk-v1-version.sh.md) — it asserts single-package v1 and refuses to run if v2 split packages are present.
Core rules
- Always use
McpServerfrom@modelcontextprotocol/sdk/server/mcp.js— the low-levelServerclass is deprecated for direct use - Always use
registerTool/registerResource/registerPrompt— positionaltool()/resource()/prompt()overloads are deprecated - Always use
zodfor input/output schemas — the SDK auto-converts to JSON Schema 2020-12 - Always use
StreamableHTTPServerTransportfor HTTP —SSEServerTransportis deprecated - Always set
annotationson tools (readOnlyHint,destructiveHint,idempotentHint,openWorldHint) — LLMs rely on them for safe execution - Tool names per SEP-986: 1–64 chars from
A–Z a–z 0–9 _ - . /; formatservice_action_resource(e.g.github_search_repos) - Input validation failures SHOULD return
{ isError: true }(tool execution error, LLM-recoverable) — not thrownMcpError(protocol error) - Access
server.server(the underlying low-levelServer) only for sampling, elicitation, resource subscriptions, or custom protocol extensions
Workflow
1 — Detect what exists
Run tree -L 3 and inspect package.json and tsconfig.json. Look for:
@modelcontextprotocol/sdkin dependencies → existing v1 server (go to Step 2A)@modelcontextprotocol/server(split) → wrong skill, redirect tobuild-mcp-server-sdk-v2mcp-usein dependencies → wrong skill, redirect tobuild-mcp-use-server.mcp.jsonor top-levelmcpkey inpackage.json→ MCP client config, not server codesrc/with tool handler files → existing implementation to extend- Empty/greenfield → go to Step 2B
For existing projects, run bash scripts/check-mcp-sdk-v1-version.sh [project-dir] to confirm v1 single-package and zod are present.
2A — Audit an existing v1 server
When an MCP server already exists, do not rebuild. Read the implementation and assess each axis:
- API style: deprecated
tool()/resource()/setRequestHandlerlow-level → migrate toregisterTool/registerResource - Schemas: raw JSON Schema objects → convert to Zod (preserves type inference and JSON Schema generation)
- Transport:
SSEServerTransport→ migrate toStreamableHTTPServerTransport - Annotations: missing on any tool → add
readOnlyHint/destructiveHint/idempotentHint/openWorldHint - Origin validation: HTTP transport without DNS-rebinding protection → add
createMcpExpressApp()orhostHeaderValidationmiddleware - Capabilities: verify
tools,resources,prompts,loggingdeclared correctly during initialization
Then proceed to the user's requested change (add tools, fix bugs, add auth, etc.).
2B — Scope a new v1 server
Ask or infer:
- What does the server wrap? API, database, file system, CLI tool
- Transport? stdio (local CLI), Streamable HTTP stateful (sessions, resumability), Streamable HTTP stateless (simple req/resp)
- Auth? None (local stdio), static bearer, OAuth 2.1, custom middleware
- Surfaces? Tools (most common), resources (data access), prompts (reusable templates)
- Client features? Sampling (LLM completions), elicitation (user input), roots (filesystem access)
For empty greenfield, scaffold with bash scripts/scaffold-v1-server.sh [stdio|http-stateful|http-stateless] (see scripts/scaffold-v1-server.sh.md).
3 — Branch by scenario
| Scenario | First read | |---|---| | New stdio server | references/guides/quick-start.md | | New HTTP server (stateful or stateless) | references/guides/transports.md | | Add tools to existing server | references/guides/tools-and-schemas.md | | Add resources or prompts | references/guides/resources-and-prompts.md | | Add authentication | references/guides/authentication.md | | Build a v1 client | references/guides/client-api.md | | Add sampling, elicitation, or session resumability | references/guides/sessions-and-lifecycle.md | | Long-running tools / durable tasks | references/guides/experimental-tasks.md | | Understand the MCP protocol contract | references/guides/protocol-spec.md | | Deploy to production | references/patterns/deployment.md | | Wire logging, error handling, rate limits, monitoring | references/patterns/production-patterns.md | | Avoid common v1 mistakes | references/patterns/anti-patterns.md | | Copy-paste working server example | references/examples/server-recipes.md |
4 — Preflight
- [ ] Node.js 18+ (required for
globalThis.crypto) - [ ]
npm install @modelcontextprotocol/sdk zod— both required - [ ] TypeScript 5+ with
"moduleResolution": "node16"or"nodenext" - [ ] HTTP transport: also
npm install express(Express 5 recommended) - [ ] Existing project passed
scripts/check-mcp-sdk-v1-version.sh
5 — Build sequence
- Create
McpServerinstance withname,version, optionaldescriptionandicons - Define Zod schemas for each tool's input (and
outputSchemaif returningstructuredContent) - Register tools with
server.registerTool(name, config, handler)— config carries schema, annotations, description - Register resources with
server.registerResource()if exposing data - Register prompts with
server.registerPrompt()if exposing templates - Create transport and connect:
await server.connect(transport) - Handle graceful shutdown:
process.on('SIGINT', async () => { await server.close(); process.exit(0); })
See references/examples/server-recipes.md for complete working examples by transport.
6 — Validate
- Local checks first:
npm run build, focused unit tests - Live smoke test with the bundled
test-by-mcpc-cliskill,npx @anthropic-ai/mcp-inspector, or raw JSON-RPC. Minimum sequence: initialize/connect →tools/list→ one successful tool call → one invalid-argument call returningisError: true - Verify Zod catches bad input — pass invalid args, confirm
isError: true - Verify annotations are accurate per tool
- Verify capabilities are declared (initialize response)
- For deeper hardening or agentic-quality audits, route to
audit-agentic-mcp— do not duplicate that here
Quick start — minimal stdio server
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer(
{ name: "my-server", version: "1.0.0" },
{ instructions: "A helpful server" }
);
server.registerTool("greet", {
description: "Greet a user by name",
inputSchema: { name: z.string().describe("The user's name") },
annotations: { readOnlyHint: true, destructiveHint: false },
}, async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}!` }],
}));
await server.connect(new StdioServerTransport());
Core API summary
McpServer
new McpServer(
{ name: string, version: string, description?: string, icons?: Icon[] },
{ capabilities?: ServerCapabilities, instructions?: string }
)
server.connect(transport: Transport): Promise
server.close(): Promise
server.registerTool(name, config, handler): RegisteredTool
server.registerResource(name, uri | template, config, handler): RegisteredResource
server.registerPrompt(name, config, handler): RegisteredPrompt
server.sendToolListChanged(): void
server.sendResourceListChanged(): void
server.sendPromptListChanged(): void
server.sendLoggingMessage(params): Promise
registerTool config
{
title?: string, // Human-readable display name
description?: string, // LLM reads this to decide when to call
inputSchema?: ZodRawShape | ZodSchema,
outputSchema?: ZodRawShape | ZodSchema, // Enables structuredContent validation
annotations?: {
readOnlyHint?: boolean,
destructiveHint?: boolean,
idempotentHint?: boolean,
openWorldHint?: boolean,
},
icons?: Icon[], // 2025-11-25
}
CallToolResult
{
content: Array,
structuredContent?: Record,
isError?: boolean,
}
RequestHandlerExtra (v1-specific — flat shape)
Every handler receives extra as the last argument:
{
signal: AbortSignal, // Cooperative cancellation
authInfo?: AuthInfo, // From OAuth middleware
sessionId?: string,
requestId: RequestId,
requestInfo?: RequestInfo, // Original HTTP request metadata
_meta?: RequestMeta,
sendNotification: (notification) => Promise,
sendRequest: (request, schema, options?) => Promise,
}
This flat shape is the single biggest v1-vs-v2 tell. v2 nests these fields under ctx.mcpReq / ctx.http. To port an existing v1 server to v2, use convert-mcp-sdk-v1-to-v2.
Error handling
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
// Hard protocol errors (tool not found, bad params at the protocol layer):
throw new McpError(ErrorCode.InvalidParams, "Missing required field: query");
// Soft tool errors (recoverable; LLM can retry or self-correct):
return { content: [{ type: "text", text: "Error: rate limit exceeded" }], isError: true };
Per spec: input validation errors SHOULD use isError: true, not thrown McpError — soft errors enable model self-correction.
Decision rules
- Prefer
ZodRawShape({ name: z.string() }) for simple inputs; use fullz.object()only for transforms, refinements, discriminated unions - Prefer
isError: truesoft errors over thrownMcpErrorfor recoverable failures - Prefer stdio for local-only servers (zero infra, single client)
- Prefer Streamable HTTP for remote or multi-client servers
- Prefer stateful HTTP (with
sessionIdGenerator) when the server needs progress notifications, resumability, or multi-turn context - Prefer stateless HTTP (
sessionIdGenerator: undefined) for simple request-response tools - Use
outputSchemawhen the tool returns validated structured data alongside text - Tool names:
service_action_resource, 1–64 chars (SEP-986)
Guardrails
- Never use deprecated
tool(),resource(),prompt()positional methods - Never use deprecated
SSEServerTransportin new servers - Never use the
Serverclass directly — go throughMcpServer - Never expose internal error details to clients — return user-friendly messages
- Never skip
zodschemas for tool inputs — unvalidated input is a security risk - Never hardcode secrets — use environment variables
- Never omit graceful shutdown for HTTP servers
- Never run HTTP servers without DNS-rebinding protection —
Originheader MUST be validated (usecreateMcpExpressApp()orhostHeaderValidation); respond 403 for invalid origins - Never set
inputSchema: null— for parameterless tools, omitinputSchemaentirely
Output contract
When work completes, report:
- Target path; new build vs existing-server maintenance
- SDK package and version range from
package.json - Transport(s): stdio, stateful Streamable HTTP, stateless Streamable HTTP
- Tool / resource / prompt counts and names
- Auth mode: none, static bearer, OAuth 2.1, custom middleware
- Validation actually run and verification rung reached
- Publish/deploy path: npm
bin/npxcommand for stdio; HTTP endpoint path for remote; Docker/serverless note when applicable server-info:
{
"name": "example-server",
"sdk": "@modelcontextprotocol/sdk@^1.x",
"transports": ["stdio"],
"tools": 3,
"resources": 0,
"prompts": 0,
"auth": "none",
"validatedWith": ["build", "test-by-mcpc-cli"]
}
Reference routing
Read only what the current branch needs. The full set:
Bundled scripts
| Script | When to run | |---|---| | scripts/check-mcp-sdk-v1-version.sh | Existing-project preflight; asserts single-package v1 SDK and zod present, refuses if v2 split packages found. See scripts/check-mcp-sdk-v1-version.sh.md. | | scripts/scaffold-v1-server.sh | Empty greenfield target after picking stdio, http-stateful, or http-stateless. See scripts/scaffold-v1-server.sh.md. |
Start-here guides
| Reference | When to read | |---|---| | references/guides/quick-start.md | Scaffolding a new server from scratch | | references/guides/tools-and-schemas.md | Registering tools, defining Zod schemas, handling tool results | | references/guides/transports.md | Choosing and configuring stdio, Streamable HTTP, or SSE (legacy) |
Server capabilities
| Reference | When to read | |---|---| | references/guides/resources-and-prompts.md | Adding resources (static or template URI) or prompts | | `references/guides/aut
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: yigitkonur
- Source: yigitkonur/skills-by-yigitkonur
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.