dailytutorfor.you
& AI Science Data

Complete 2026 Tutorial: Building a Secure, Production-Ready TypeScript MCP Server for AI Agents

A practical guide to building an MCP Server with TypeScript from zero to production: core concepts, architecture, runnable implementation, security best practices, common mistakes, and scaling strategies for modern AI workflows.

10 min read

Complete 2026 Tutorial: Building a Secure, Production-Ready TypeScript MCP Server for AI Agents

Reading time: ±15 minutes
Level: Intermediate (serious beginners can still follow along)
Stack: Node.js, TypeScript, Model Context Protocol (MCP)

1) Introduction — What and Why

If you’ve been actively following developer trends over the last few months, you’ve definitely noticed one pattern: almost every team is experimenting with AI agents. From GitHub Trending, agent-themed repositories and tool integration projects are rising fast. In the MCP ecosystem itself, adoption is growing wider because many clients (IDEs, chatbots, internal apps) need a standard way to “connect” to external data and tools.

The classic problem is: before standards existed, each AI integration tended to be ad-hoc. Today you build a custom endpoint to read a database, tomorrow you build another adapter for the file system, next week you add an external API with a different format. In the end:

  • integrations become messy,
  • hard to maintain,
  • and most dangerous: security policies become inconsistent.

That’s where Model Context Protocol (MCP) becomes relevant. Think of it as “USB-C for AI apps”: one standard protocol to expose tools, resources, and prompts to a model/agent.

In this tutorial, we’ll focus on building a TypeScript-based MCP Server that is:

  1. runnable locally,
  2. has proper error handling,
  3. safe from weird input,
  4. easy to extend into real-world use cases.

We’ll build an example “Task Helper” server with two main tools:

  • create_task: create a new task,
  • list_tasks: view the task list with filters.

Even though it’s simple, the architecture pattern is the same one used in production systems.


2) Prerequisites — Before You Start

Prepare these first:

  • Node.js 20+
  • npm / pnpm (examples use npm)
  • Basic TypeScript (interfaces, async/await)
  • Understand basic HTTP & JSON
  • Your favorite IDE (VS Code recommended)

Optional global install:

npm i -g tsx

Why tsx? Because you can run TypeScript files directly without setting up a heavy build pipeline at the initial stage.


3) Core Concepts — Fundamentals with an Analogy

To make it easier to visualize, let’s use a restaurant analogy 🍜

  • MCP Client = the waiter receiving orders from the user/model.
  • MCP Server = the kitchen with specific capabilities.
  • Tool = action menu (example: “create task”, “fetch data”).
  • Resource = reference materials/data (documents, files, etc.).
  • Transport = communication channel (stdio or streamable HTTP).

Important concepts you must hold onto

  1. Tool contracts must be clear
    Input and output schemas must be consistent. This prevents the model from guessing formats.

  2. Don’t make the server too permissive
    Don’t expose tools that can do anything without guardrails.

  3. Input validation is mandatory
    Don’t trust model input 100%. Models can hallucinate parameters.

  4. Errors must be structured
    Good error messages speed up debugging and make agents more stable.

  5. Security by design
    Start with the principle of least privilege: tools should only have the minimum access they need.


4) Architecture / Diagram

We’ll use a simple architecture like this:

+-------------------+ MCP Transport +-------------------------+ | MCP Client/Agent | <-----------------------> | MCP Server (TypeScript) | | (IDE / Chat App) | | | +-------------------+ | +-------------------+ | | | Tool Registry | | | | - create_task | | | | - list_tasks | | | +-------------------+ | | | | | +-------------------+ | | | Service Layer | | | | validation + biz | | | +-------------------+ | | | | | +-------------------+ | | | In-memory store | | | | (demo; can swap) | | | +-------------------+ | +-------------------------+

Request flow:

  1. Agent calls the create_task tool.
  2. MCP server validates input.
  3. Service layer processes business logic.
  4. Result is returned in a consistent format.

5) Step-by-Step Implementation (Runnable)

5.1 Project initialization

mkdir mcp-task-server cd mcp-task-server npm init -y npm i @modelcontextprotocol/server zod npm i -D typescript tsx @types/node npx tsc --init

Update package.json script:

{ "scripts": { "dev": "tsx src/index.ts" } }

5.2 Create file src/index.ts

The code below is complete and runnable for a local demo.

import { z } from "zod"; type TaskStatus = "todo" | "in_progress" | "done"; interface Task { id: string; title: string; description?: string; status: TaskStatus; createdAt: string; } // In-memory store untuk demo. Di production bisa diganti PostgreSQL/Redis. const tasks: Task[] = []; // Utility: generate id sederhana function generateId(): string { return `task_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; } // Schema validasi input const createTaskSchema = z.object({ title: z.string().min(3, "title minimal 3 karakter").max(120), description: z.string().max(1000).optional(), status: z.enum(["todo", "in_progress", "done"]).default("todo") }); const listTasksSchema = z.object({ status: z.enum(["todo", "in_progress", "done"]).optional(), limit: z.number().int().min(1).max(100).default(20) }); // Bentuk response yang konsisten function ok(data: unknown) { return { success: true, data, timestamp: new Date().toISOString() }; } function fail(message: string, detail?: unknown) { return { success: false, error: { message, detail }, timestamp: new Date().toISOString() }; } // Simulasi handler tool MCP (portable untuk berbagai transport) async function createTaskTool(input: unknown) { try { const parsed = createTaskSchema.parse(input); const newTask: Task = { id: generateId(), title: parsed.title, description: parsed.description, status: parsed.status, createdAt: new Date().toISOString() }; tasks.push(newTask); return ok({ message: "Task berhasil dibuat", task: newTask }); } catch (error) { if (error instanceof z.ZodError) { return fail("Validasi input gagal", error.flatten()); } return fail("Terjadi error saat membuat task", String(error)); } } async function listTasksTool(input: unknown) { try { const parsed = listTasksSchema.parse(input); let result = [...tasks]; if (parsed.status) { result = result.filter((t) => t.status === parsed.status); } result = result.slice(0, parsed.limit); return ok({ count: result.length, items: result }); } catch (error) { if (error instanceof z.ZodError) { return fail("Validasi input gagal", error.flatten()); } return fail("Terjadi error saat mengambil task", String(error)); } } // Demo runner agar file bisa dijalankan langsung async function main() { console.log("MCP Task Server Demo starting... "); // 1) create task valid const create1 = await createTaskTool({ title: "Belajar MCP TypeScript", description: "Implement tool create_task & list_tasks", status: "todo" }); console.log("[create_task #1]", JSON.stringify(create1, null, 2)); // 2) create task invalid (contoh error handling) const create2 = await createTaskTool({ title: "Hi" }); console.log(" [create_task #2 - invalid]", JSON.stringify(create2, null, 2)); // 3) list tasks const list = await listTasksTool({ status: "todo", limit: 10 }); console.log(" [list_tasks]", JSON.stringify(list, null, 2)); console.log(" Demo selesai. Integrasikan handler ini ke MCP transport (stdio/http) sesuai client kamu."); } main().catch((err) => { console.error("Fatal error:", err); process.exit(1); });

Run:

npm run dev

If the output shows a success response + validation error, your foundation is correct.

5.3 Integrate with real MCP Transport

In production implementations, the createTaskTool and listTasksTool handlers are registered to the MCP server based on the SDK/transport you choose (stdio or Streamable HTTP). The principles remain the same:

  • register tool names,
  • define input schemas,
  • return structured outputs,
  • audit logging for every invocation.

6) Best Practices — Field-Tested Tips

  1. Schema-first development
    Define input/output schemas first, then logic. This drastically reduces integration bugs.

  2. Idempotency for critical operations
    Tools like create_payment or submit_order must have idempotency keys.

  3. Audit logs per tool call
    Store: who called, when, important parameters, result, latency.

  4. Timeout + retry policy
    Don’t let tool calls hang. Apply explicit timeouts.

  5. Least privilege access
    If a tool only needs read access, don’t grant write permissions.

  6. Rate limiting
    Protect the server from excessive invocation loops from agents.

  7. Contract versioning
    If you change schemas, use versioning (v1, v2) so older clients don’t break.


7) Common Mistakes — What Often Makes Systems Fragile

Mistake #1: Assuming model output is always valid

Models can send typoed or missing fields. Solution: strict validation + safe defaults.

Mistake #2: Error messages are too generic

Internal Error without details makes debugging slow. Solution: structured error codes + details (without leaking secrets).

Mistake #3: “Super admin” tools

A single tool that can access everything is a security anti-pattern. Solution: split tools into granular capabilities.

Mistake #4: No input size limits

Large payloads can cause crashes/memory spikes. Solution: set max length in schema.

Mistake #5: Going to production without observability

Without metrics/logs, you are blind during incidents. Solution: add tracing from the start.


8) Advanced Tips — If You Want to Level Up

A) Add real persistence

Replace in-memory storage with PostgreSQL:

  • table tasks(id, title, description, status, created_at)
  • indexes on status and created_at

B) Add an auth layer

For remote MCP servers, use OAuth and token scoping. Make sure sensitive tools require specific scopes.

C) Guard against prompt injection side-effects

Although injection happens at the model content level, the impact can reach tool calls. Mitigation:

  • whitelist domains/tools,
  • require confirmation for destructive actions,
  • sanitize and limit parameters.

D) Multi-tenant design

If used by many teams/tenants:

  • every request must include tenantId,
  • queries must always be tenant-scoped,
  • audit logs must be separated per tenant.

E) Test with contract testing

Create tests that ensure tool schemas don’t change silently. This is critical for agent stability.


9) Summary & Next Steps

We’ve covered end-to-end how to build a clean TypeScript MCP server:

  • understand MCP concepts (tool, resource, transport),
  • design a minimal but scalable architecture,
  • implement a runnable setup with validation + error handling,
  • apply best practices and avoid common pitfalls,
  • plan concrete steps toward production.

Next steps I recommend for this week:

  1. Integrate the sample tools into a real MCP transport (stdio/http).
  2. Add a database (PostgreSQL).
  3. Add auth + audit logs.
  4. Create 3 contract tests for tool input/output.
  5. Deploy to staging and test with 1 real agent client.

If these 5 steps are done, you’ll already have a strong foundation for building more serious AI workflows (for example customer support assistants, internal ops assistants, or engineering copilots).


10) References

Here are the main references for deeper study:

  1. Model Context Protocol — Introduction
    https://modelcontextprotocol.io/introduction

  2. MCP TypeScript SDK (official)
    https://github.com/modelcontextprotocol/typescript-sdk

  3. MCP Reference Servers (official examples)
    https://github.com/modelcontextprotocol/servers

  4. OpenAI Docs — Building MCP servers for apps/API integrations
    https://developers.openai.com/api/docs/mcp/

  5. VS Code Docs — Add and manage MCP servers
    https://code.visualstudio.com/docs/copilot/customization/mcp-servers


Trend Research Notes (April 2026)

  • GitHub Trending shows a surge of repositories around AI agent tooling and on-device/LLM integration.
  • The MCP ecosystem is maturing with SDKs, reference servers, and cross-tool developer adoption.
  • Secure integration topics (auth, sandboxing, least privilege) are discussed more frequently as agent adoption moves toward production.

If you want a follow-up series, the next most relevant topic is: "MCP Client orchestration + observability for multi-tool agents".