Model Context Protocol (MCP) for Web Developers 2026: Build an AI Tooling Server with TypeScript from Scratch
A complete Indonesian-language guide to understanding and implementing Model Context Protocol (MCP) in modern web projects. Starting from concept, architecture, to implementation of a runnable TypeScript server with validation, error handling, and production best practices.
Model Context Protocol (MCP) for Web Developers 2026: Build an AI Tooling Server with TypeScript from Scratch
7+ minute read • Level: Intermediate • Focus: Web Development + AI Integration
1) Introduction — What is MCP and Why is it Important?
If a few years ago REST APIs were a “common language” between web applications, in 2026 we will see a similar pattern for AI integration: Model Context Protocol (MCP).
Simply put, MCP is an open standard so that AI applications (for example Claude, ChatGPT, or other AI coding assistants) can talk to your tools, data sources, and workflows in a consistent way.
Imagine it this way:
- Before MCP: each tool made its own "plug", request/response format was different.
- With MCP: we have “USB-C for AI”. Once you understand the MCP pattern, you can plug into many clients.
Why is this important for web developers?
- Reuse the backend skills you already have: you still design tools such as API contracts, schema validation, auth, logging.
- Faster integration: AI clients do not need custom adapters per tool.
- More maintainable: clear architecture between AI client, MCP server, and internal services.
- Fast growing ecosystem: GitHub trending +
Real-world context: Many SaaS startups are now adding “AI operators” for support tasks, data lookup, or internal automation. MCP allows web teams to connect AI to existing systems without a complete rewrite.
2) Prerequisites — What Must Be Prepared?
Before you start, make sure you are comfortable with:
- Node.js LTS (Node 20+ recommended)
- Basic-intermediate TypeScript
- Async/await and error handling concepts
- JSON schema validation (we use
zod) - Basic HTTP API (to understand flow tool invocation)
Tools used in this tutorial:
@modelcontextprotocol/serverzodtsxto run TypeScript without manual compilation
Install environment:```bash node -v npm -v
If you don't have it yet, install Node LTS first. After that we are ready to code. --- ## 3) Core Concepts — MCP Foundation with a Simple Analogy To make it easier to imagine, think of MCP like a smart restaurant: - **Client AI** = Server that accepts user requests. - **MCP Server** = Kitchen that knows the menu list (tools), rules and how to cook. - **Tool** = Specific menu (eg "create todo", "search customer data"). - **Schema (Zod)** = Order quality standard (input format must be valid). General flow: 1. User asks AI for something. 2. The AI decides it needs to call the tool. 3. AI sends a request to the MCP server. 4. MCP server input validation + logic execution. 5. Results are returned to AI to be answered by the user. Key concepts that you must grasp: - **Tool-first design**: small, clear, testable capability design. - **Strong validation**: never trust raw input from LLM. - **Deterministic output**: tool results must be consistent so that the AI is not "confused". - **Observability**: log requests, errors, latency. --- ## 4) Architecture / Diagram — Architectural Overview Here is a simple architecture for the “AI Todo Assistant” use case:```text +---------------------+ MCP Protocol +---------------------------+ | AI Client | <------------------------> | MCP Server (TypeScript) | | (Claude/ChatGPT/IDE)| | - register tools | +---------------------+ | - validate input (zod) | | - auth/rate limit/log | +-------------+-------------+ | | service call v +---------------------------+ | Todo Service / DB Layer | | - create/list/update task | +---------------------------+
In production, you can add:
- Redis for rate limits
- PostgreSQL for persistence
- OpenTelemetry for tracing
- API gateway + auth proxy
5) Step-by-Step Implementation (Complete Runnable Code)
In this section we create a minimal but production-minded MCP server:
- Tool
create_todo - Tool
list_todos - Validation using
zod - Neat error handling
- Simple logging
5.1 Initialize the project```bash
mkdir mcp-todo-server cd mcp-todo-server npm init -y npm install @modelcontextprotocol/server zod npm install -D typescript tsx @types/node npx tsc --init
Update `package.json` scripts:```json { "scripts": { "dev": "tsx src/server.ts", "start": "node dist/server.js" } }
5.2 Create the src/server.ts file
The code below is complete and can be run directly in local testing mode.```ts import { Server } from "@modelcontextprotocol/server"; import { StdioServerTransport } from "@modelcontextprotocol/server/stdio"; import { z } from "zod";
/**
- Domain model sederhana untuk demo.
- Untuk production: pindah ke database + repository layer. */ type Todo = { id: string; title: string; done: boolean; createdAt: string; };
const todos: Todo[] = [];
/**
- Utility logging sederhana. */ function logInfo(message: string, meta?: unknown) { console.log(JSON.stringify({ level: "info", message, meta, ts: new Date().toISOString() })); }
function logError(message: string, error: unknown) { console.error( JSON.stringify({ level: "error", message, error: error instanceof Error ? { name: error.name, message: error.message, stack: error.stack } : error, ts: new Date().toISOString(), }) ); }
/**
- Input schema untuk tool create_todo. */ const CreateTodoInput = z.object({ title: z.string().min(3, "Title minimal 3 karakter").max(120, "Title maksimal 120 karakter"), });
/**
- Input schema untuk list_todos. */ const ListTodosInput = z.object({ includeDone: z.boolean().default(true), });
async function main() { // Inisialisasi MCP server const server = new Server( { name: "todo-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } );
// Register tool: create_todo server.tool( "create_todo", { title: "Create Todo", description: "Membuat todo baru untuk task management.", inputSchema: { type: "object", properties: { title: { type: "string", minLength: 3, maxLength: 120 }, }, required: ["title"], }, }, async (rawInput: unknown) => { try { const input = CreateTodoInput.parse(rawInput);
const todo: Todo = { id: crypto.randomUUID(), title: input.title.trim(), done: false, createdAt: new Date().toISOString(), }; todos.push(todo); logInfo("Todo created", { todoId: todo.id, title: todo.title }); return { content: [ { type: "text", text: `✅ Todo berhasil dibuat
ID: ${todo.id} Title: ${todo.title}`, }, ], }; } catch (error) { logError("Failed to create todo", error);
// Jangan bocorkan detail internal ke user const message = error instanceof z.ZodError ? `Input tidak valid: ${error.issues.map((i) => i.message).join(", ")}` : "Gagal membuat todo karena kesalahan internal."; return { isError: true, content: [{ type: "text", text: `❌ ${message}` }], }; } }
);
// Register tool: list_todos server.tool( "list_todos", { title: "List Todos", description: "Menampilkan daftar todo saat ini.", inputSchema: { type: "object", properties: { includeDone: { type: "boolean", default: true }, }, }, }, async (rawInput: unknown) => { try { const input = ListTodosInput.parse(rawInput ?? {});
const result = input.includeDone ? todos : todos.filter((t) => !t.done); if (result.length === 0) { return { content: [{ type: "text", text: "Belum ada todo." }], }; } const lines = result.map((t, index) => `${index + 1}. [${t.done ? "x" : " "}] ${t.title} (id=${t.id})`); return { content: [{ type: "text", text: `📋 Daftar Todo:
${lines.join(" ")}` }], }; } catch (error) { logError("Failed to list todos", error); return { isError: true, content: [{ type: "text", text: "❌ Gagal mengambil daftar todo." }], }; } } );
// Transport stdio cocok untuk local integration/testing const transport = new StdioServerTransport(); await server.connect(transport);
logInfo("MCP server started", { transport: "stdio" }); }
main().catch((error) => { logError("Fatal error on startup", error); process.exit(1); });
### 5.3 Start the server```bash npm run dev
The server is now ready to receive tool calls from MCP-compatible clients.
5.4 Why is this implementation “runnable + healthy”?
- There is input validation with
zod - Clear error handling (validation error vs internal error)
- JSON structured logging
- Does not leak stack traces to end-users
- Domain model is separate from transport logic
6) Best Practices — Tips from Industry Practices
The following are things that are highly recommended when MCP goes into production:
-
Version tools clearly
- Example:
create_todo_v1, then deprecate gradually.
- Example:
-
Schema strict, not loose
- Always set min/max length, enum, pattern.
- It reduces input hallucinations from the model.
-
Idempotency for critical operations
- For example create order/payment: use
idempotencyKey.
- For example create order/payment: use
-
Timeout and retry policy
- Don't leave the tool call hanging for too long.
-
End-to-end observability
- Add tracing (OpenTelemetry) + ID correlation.
-
Principle of least privilege
- The MCP server can only access the resources it needs.
-
Separate demo server vs production server
- MCP's own reference repo emphasizes that many of the example servers are educational, not drop-in production.
7) Common Mistakes — Common Mistakes to Avoid
Mistake #1: “LLM must send correct input”
This is the most common trap. Without a strict schema, strange bugs appear in runtime.
Solution: validate all input, give a directed error message.
Mistake #2: Tool is too fat
One tool does 7 things at once making debugging difficult.
Solution: break it down into small, composable tools.
Mistake #3: There is no auth boundary
Directly expose sensitive tools to AI without permission layers.
Solution: add auth, scope and audit log.
Mistake #4: Output is not deterministic
Sometimes returns object A, sometimes B, random field.
Solution: set a consistent response shape.
Mistake #5: Direct production without load testing
Once used by large teams, latency explodes.
Solution: benchmark from the start, install rate limit + queue if necessary.
8) Advanced Tips — For those who want to level up
If the basic MCP server is running, you can proceed to the following level:
-
Transport strategy
- Local dev: fast and simple stdio.
- Team/server deployment: Streamable HTTP + auth.
-
MCP + existing backend architecture
- Think of MCP as an adapter layer on top of your service domain.
- Don't put the main business logic in the handler tool.
-
Governance tools
- Create an internal catalog: tool name, owner, SLA, sensitivity data.
-
Prompt-aware API design
- Give clear tool descriptions, examples of input/output, behavioral limitations.
-
Secure-by-default pattern
- Input sanitization, outbound allowlist, secret manager, audit trail.
-
Progressive rollout
- Starting from read-only tools (safe), then to write actions.
-
Testing pyramid for MCP
- Unit test: schema validation + pure functions
- Integration test: tool handler + service layer
- Contract test: response shape remains stable between versions
9) Summary & Next Steps
We have discussed from zero to runnable implementation:
- Understand MCP as an AI-tooling integration standard
- Develop a clean architecture
- Built a TypeScript server MCP with proper error handling
- Know best practices + anti-patterns that often occur
Next steps that I suggest for you:
- Add persistence (PostgreSQL + Prisma/Drizzle) 2.Add auth token & role check per tool
- Migration to HTTP transport for multi-client usage
- Add observability (logs + tracing + metrics)
- Create a CI contract test so that schema changes are safe
If you have reached this stage, you not only "can try MCP", but you already have the foundation to build a serious AI-enabled web system.
10) References
Official documentation and repositories used as references:
-
Model Context Protocol — Introduction
https://modelcontextprotocol.io/docs/getting-started/intro -
MCP Architecture
https://modelcontextprotocol.io/docs/learn/architecture -
MCP Specifications
https://spec.modelcontextprotocol.io -
MCP TypeScript SDK (official)
https://github.com/modelcontextprotocol/typescript-sdk -
MCP Servers (reference implementations)
https://github.com/modelcontextprotocol/servers -
MCP Registry
https://registry.modelcontextprotocol.io -
VS Code + MCP docs (ecosystem support)
https://code.visualstudio.com/docs/copilot/chat/mcp-servers -
OpenAI docs — MCP integration references
https://developers.openai.com/api/docs/mcp/
Hopefully this tutorial makes you more confident in building neat, safe and scalable AI integration. 🚀