Create a MCP server from scratch

create-a-mcp-server-from-scratch

Complete Tutorial: Creating an MCP Task Manager Server from Scratch

📖 Table of Contents

  1. Introduction to Model Context Protocol (MCP)
  2. Prerequisites and Installation
  3. Project Structure
  4. Basic Configuration
  5. MCP Server Implementation
  6. Endpoints: Tools
  7. Endpoints: Resources
  8. Endpoints: Prompts
  9. Error Handling
  10. Main Server
  11. Code Organization and Patterns
  12. Testing and Validation
  13. Docker Deployment
  14. Additional Resources

1. Introduction to Model Context Protocol (MCP)

What is MCP?

The Model Context Protocol (MCP) is a standardized communication protocol that allows Large Language Models (LLMs) to interact with external systems in a secure and structured manner. It uses JSON-RPC 2.0 for communication.

Main Components

  • Tools: Actions that the LLM can execute (create, modify, delete)
  • Resources: Data that the LLM can read (files, databases)
  • Prompts: Predefined templates to guide interactions

MCP Session Lifecycle

sequenceDiagram
    Client->>Server: initialize
    Server->>Client: capabilities
    Client->>Server: tools/list
    Client->>Server: resources/list
    Client->>Server: prompts/list
    Client->>Server: tools/call | resources/read | prompts/get

2. Prerequisites and Installation

Required Technologies

  • Node.js 18+ with TypeScript
  • @modelcontextprotocol/sdk for MCP implementation
  • Zod for schema validation
  • Database (SQLite for this tutorial)

Installation

# Create the project
mkdir mcp-task-manager
cd mcp-task-manager

# Initialize Node.js
npm init -y

# Install dependencies
npm install @modelcontextprotocol/sdk zod sqlite3
npm install -D typescript @types/node ts-node nodemon

# TypeScript configuration
npx tsc --init

3. Project Structure

mcp-task-manager/
├── src/
│   ├── server.ts           # MCP server entry point
│   ├── database/
│   │   ├── db.ts          # Database configuration
│   │   └── schema.sql     # Table schema
│   ├── tools/
│   │   ├── index.ts       # Tools export
│   │   ├── create-task.ts
│   │   ├── update-task.ts
│   │   ├── complete-task.ts
│   │   └── delete-task.ts
│   ├── resources/
│   │   ├── index.ts       # Resources export
│   │   ├── task-list.ts
│   │   ├── task-stats.ts
│   │   └── task-detail.ts
│   ├── prompts/
│   │   ├── index.ts       # Prompts export
│   │   ├── weekly-review.ts
│   │   └── sprint-planning.ts
│   ├── types/
│   │   └── task.ts        # TypeScript types
│   └── utils/
│       └── errors.ts      # Error handling
├── Dockerfile             # Multi-stage Docker configuration
├── docker-compose.yml     # Docker orchestration
├── .dockerignore          # Files to ignore for Docker
├── .env.example           # Environment variables example
├── package.json
├── tsconfig.json
└── README.md

4. Basic Configuration

package.json

{
  "name": "mcp-task-manager",
  "version": "1.0.0",
  "description": "MCP server for task management",
  "main": "dist/server.js",
  "scripts": {
    "build": "tsc && mkdir -p dist/database && cp src/database/schema.sql dist/database/",
    "start": "node dist/server.js",
    "dev": "nodemon src/server.ts",
    "test": "jest",
    "docker:build": "docker build -t task-manager-mcp-server .",
    "docker:run": "docker run -d --name task-manager-mcp-server -p 3000:3000 task-manager-mcp-server",
    "docker:stop": "docker stop task-manager-mcp-server && docker rm task-manager-mcp-server",
    "docker:logs": "docker logs task-manager-mcp-server"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.21.0",
    "sqlite3": "^5.1.7",
    "zod": "^3.25.76"
  },
  "devDependencies": {
    "@types/node": "^24.10.0",
    "nodemon": "^3.1.10",
    "ts-node": "^10.9.2",
    "typescript": "^5.9.3"
  }
}

tsconfig.json

{
  // Visit https://aka.ms/tsconfig to read more about this file
  "compilerOptions": {
    // File Layout
    "outDir": "./dist",
    "rootDir": "./src",

    // Environment Settings
    // See also https://aka.ms/tsconfig/module
    "module": "commonjs",
    "target": "ES2022",
    "esModuleInterop": true,
    // For nodejs:
    "lib": ["ES2022"],
    "types": ["node"],
    // and npm install -D @types/node

    // Other Outputs
    "sourceMap": true,
    "declaration": true,
    "declarationMap": true,

    // Stricter Typechecking Options
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,

    // Style Options
    // "noImplicitReturns": true,
    // "noImplicitOverride": true,
    // "noUnusedLocals": true,
    // "noUnusedParameters": true,
    // "noFallthroughCasesInSwitch": true,
    // "noPropertyAccessFromIndexSignature": true,

    // Recommended Options
    "strict": true,
    "skipLibCheck": true,
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

5. MCP Server Implementation

Basic Types (src/types/task.ts)

The system uses Zod for validation and TypeScript type generation:

import { z } from 'zod';

// Main schema with complete validation
export const TaskSchema = z.object({
  id: z.string().optional(),                           // Auto-generated
  title: z.string().min(1).max(200),                  // Required title (1-200 chars)
  description: z.string().optional(),                  // Optional description
  priority: z.enum(['low', 'medium', 'high', 'urgent']).default('medium'),
  status: z.enum(['pending', 'in_progress', 'completed', 'cancelled']).default('pending'),
  due_date: z.string().datetime().optional(),         // ISO 8601 format
  tags: z.array(z.string()).default([]),              // Array of strings
  assignee: z.string().optional(),                    // Assigned user
  created_at: z.string().datetime().optional(),       // Creation timestamp
  updated_at: z.string().datetime().optional(),       // Modification timestamp
  completed_at: z.string().datetime().optional()      // Completion timestamp
});

// TypeScript type automatically inferred
export type Task = z.infer<typeof TaskSchema>;

// Derived schemas for CRUD operations
export const CreateTaskSchema = TaskSchema.omit({ 
  id: true, 
  created_at: true, 
  updated_at: true, 
  completed_at: true 
});

export const UpdateTaskSchema = TaskSchema.partial().extend({
  id: z.string()                                      // Required ID for updates
});

export const CompleteTaskSchema = z.object({
  task_id: z.string(),
  completion_notes: z.string().optional(),
  time_spent: z.number().min(0).optional()           // Time in minutes
});

Advantages of this approach:

  1. Type Safety: Automatic runtime validation
  2. Living documentation: Schemas serve as reference
  3. Reusability: One schema, multiple uses (API, DB, validation)
  4. Explicit errors: Clear error messages for clients

Validation example:

// ✅ Valid
const task = CreateTaskSchema.parse({
  title: "New task",
  priority: "high",
  tags: ["urgent", "feature"]
});

// ❌ Validation error
const invalid = CreateTaskSchema.parse({
  title: "",  // Error: minimum 1 character
  priority: "invalid"  // Error: unauthorized value
});

Database Configuration (src/database/db.ts)

import sqlite3 from 'sqlite3';
import path from 'path';
import fs from 'fs';
import { Task } from '../types/task';

export class TaskDatabase {
  private db: sqlite3.Database;

  constructor(dbPath: string = './tasks.db') {
    this.db = new sqlite3.Database(dbPath);
    this.initializeDatabase();
  }

  private async initializeDatabase() {
    const schemaPath = path.join(__dirname, 'schema.sql');
    const schema = fs.readFileSync(schemaPath, 'utf8');

    return new Promise<void>((resolve, reject) => {
      this.db.exec(schema, (err) => {
        if (err) reject(err);
        else resolve();
      });
    });
  }

  async createTask(task: Omit<Task, 'id' | 'created_at' | 'updated_at'>): Promise<string> {
    const sql = `
      INSERT INTO tasks (title, description, priority, status, due_date, tags, assignee)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    `;

    return new Promise((resolve, reject) => {
      this.db.run(sql, [
        task.title,
        task.description || null,
        task.priority,
        task.status || 'pending',
        task.due_date || null,
        JSON.stringify(task.tags || []),
        task.assignee || null
      ], function(err) {
        if (err) reject(err);
        else resolve(`task-${this.lastID}`);
      });
    });
  }

  async getTask(id: string): Promise<Task | null> {
    const sql = 'SELECT * FROM tasks WHERE id = ?';

    return new Promise((resolve, reject) => {
      this.db.get(sql, [id.replace('task-', '')], (err, row: any) => {
        if (err) reject(err);
        else if (!row) resolve(null);
        else {
          resolve({
            id: `task-${row.id}`,
            title: row.title,
            description: row.description,
            priority: row.priority,
            status: row.status,
            due_date: row.due_date,
            tags: JSON.parse(row.tags || '[]'),
            assignee: row.assignee,
            created_at: row.created_at,
            updated_at: row.updated_at,
            completed_at: row.completed_at
          });
        }
      });
    });
  }

  async getAllTasks(): Promise<Task[]> {
    const sql = 'SELECT * FROM tasks ORDER BY created_at DESC';

    return new Promise((resolve, reject) => {
      this.db.all(sql, [], (err, rows: any[]) => {
        if (err) reject(err);
        else {
          const tasks = rows.map(row => ({
            id: `task-${row.id}`,
            title: row.title,
            description: row.description,
            priority: row.priority,
            status: row.status,
            due_date: row.due_date,
            tags: JSON.parse(row.tags || '[]'),
            assignee: row.assignee,
            created_at: row.created_at,
            updated_at: row.updated_at,
            completed_at: row.completed_at
          }));
          resolve(tasks);
        }
      });
    });
  }

  async updateTask(id: string, updates: Partial<Task>): Promise<boolean> {
    const fields = [];
    const values: string[] = [];

    Object.entries(updates).forEach(([key, value]) => {
      if (key !== 'id' && value !== undefined) {
        fields.push(`${key} = ?`);
        values.push(key === 'tags' ? JSON.stringify(value) : String(value));
      }
    });

    if (fields.length === 0) return false;

    fields.push('updated_at = CURRENT_TIMESTAMP');
    const sql = `UPDATE tasks SET ${fields.join(', ')} WHERE id = ?`;
    values.push(id.replace('task-', ''));

    return new Promise((resolve, reject) => {
      this.db.run(sql, values, function(err) {
        if (err) reject(err);
        else resolve(this.changes > 0);
      });
    });
  }

  async completeTask(id: string, notes?: string, timeSpent?: number): Promise<boolean> {
    const sql = `
      UPDATE tasks 
      SET status = 'completed', 
          completed_at = CURRENT_TIMESTAMP,
          completion_notes = ?,
          time_spent = ?,
          updated_at = CURRENT_TIMESTAMP
      WHERE id = ?
    `;

    return new Promise((resolve, reject) => {
      this.db.run(sql, [notes || null, timeSpent || null, id.replace('task-', '')], function(err) {
        if (err) reject(err);
        else resolve(this.changes > 0);
      });
    });
  }

  async deleteTask(id: string): Promise<boolean> {
    const sql = 'DELETE FROM tasks WHERE id = ?';

    return new Promise((resolve, reject) => {
      this.db.run(sql, [id.replace('task-', '')], function(err) {
        if (err) reject(err);
        else resolve(this.changes > 0);
      });
    });
  }

  async getStats(): Promise<any> {
    const sql = `
      SELECT 
        COUNT(*) as total_tasks,
        SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
        SUM(CASE WHEN status = 'in_progress' THEN 1 ELSE 0 END) as in_progress,
        SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
        SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled,
        SUM(CASE WHEN priority = 'urgent' THEN 1 ELSE 0 END) as urgent,
        SUM(CASE WHEN priority = 'high' THEN 1 ELSE 0 END) as high,
        SUM(CASE WHEN priority = 'medium' THEN 1 ELSE 0 END) as medium,
        SUM(CASE WHEN priority = 'low' THEN 1 ELSE 0 END) as low,
        AVG(time_spent) as avg_completion_time
      FROM tasks
    `;

    return new Promise((resolve, reject) => {
      this.db.get(sql, [], (err, row: any) => {
        if (err) reject(err);
        else resolve(row);
      });
    });
  }
}

Database Schema (src/database/schema.sql)

CREATE TABLE IF NOT EXISTS tasks (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT NOT NULL,
  description TEXT,
  priority TEXT DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'urgent')),
  status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'completed', 'cancelled')),
  due_date TEXT,
  tags TEXT DEFAULT '[]',
  assignee TEXT,
  completion_notes TEXT,
  time_spent INTEGER,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  completed_at DATETIME
);

CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee);
CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date);

6. Endpoints: Tools

Task Creation (src/tools/create-task.ts)

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';
import { CreateTaskSchema } from '../types/task.js';
import { McpError, ErrorCode } from '../utils/errors.js';

export const createTaskTool: Tool = {
  name: 'create_task',
  description: 'Create a new task in the system',
  inputSchema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        description: 'Task title',
        minLength: 1,
        maxLength: 200
      },
      description: {
        type: 'string',
        description: 'Detailed task description'
      },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'urgent'],
        default: 'medium',
        description: 'Priority level'
      },
      due_date: {
        type: 'string',
        format: 'date-time',
        description: 'Due date (ISO 8601)'
      },
      tags: {
        type: 'array',
        items: { type: 'string' },
        description: 'List of associated tags'
      },
      assignee: {
        type: 'string',
        description: 'Person assigned to the task'
      }
    },
    required: ['title'],
    additionalProperties: false
  }
};

export async function handleCreateTask(args: any, db: TaskDatabase) {
  try {
    // Argument validation
    const validatedArgs = CreateTaskSchema.parse(args);

    // Task creation
    const taskId = await db.createTask(validatedArgs);

    return {
      content: [{
        type: 'text',
        text: 'Task created successfully'
      }],
      task_id: taskId,
      created_at: new Date().toISOString(),
      status: 'pending'
    };
  } catch (error) {
    if (error instanceof Error) {
      throw new McpError(ErrorCode.INVALID_PARAMS, `Validation error: ${error.message}`);
    }
    throw new McpError(ErrorCode.INTERNAL_ERROR, 'Internal error during creation');
  }
}

Task Update (src/tools/update-task.ts)

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';
import { UpdateTaskSchema } from '../types/task.js';
import { McpError, ErrorCode } from '../utils/errors.js';

export const updateTaskTool: Tool = {
  name: 'update_task',
  description: 'Update an existing task',
  inputSchema: {
    type: 'object',
    properties: {
      task_id: {
        type: 'string',
        description: 'Task identifier to modify'
      },
      title: {
        type: 'string',
        minLength: 1,
        maxLength: 200
      },
      description: { type: 'string' },
      priority: {
        type: 'string',
        enum: ['low', 'medium', 'high', 'urgent']
      },
      status: {
        type: 'string',
        enum: ['pending', 'in_progress', 'completed', 'cancelled']
      },
      due_date: {
        type: 'string',
        format: 'date-time'
      },
      tags: {
        type: 'array',
        items: { type: 'string' }
      },
      assignee: { type: 'string' }
    },
    required: ['task_id'],
    additionalProperties: false
  }
};

export async function handleUpdateTask(args: any, db: TaskDatabase) {
  try {
    const { task_id, ...updates } = args;

    // Check if task exists
    const existingTask = await db.getTask(task_id);
    if (!existingTask) {
      throw new McpError(ErrorCode.TASK_NOT_FOUND, `Task ${task_id} not found`);
    }

    // Check if task is already completed
    if (existingTask.status === 'completed') {
      throw new McpError(ErrorCode.TASK_ALREADY_COMPLETED, 'Cannot modify a completed task');
    }

    // Update
    const success = await db.updateTask(task_id, updates);

    if (success) {
      const updatedTask = await db.getTask(task_id);
      return {
        content: [{
          type: 'text',
          text: 'Task updated successfully'
        }],
        task: updatedTask
      };
    } else {
      throw new McpError(ErrorCode.INTERNAL_ERROR, 'Update failed');
    }
  } catch (error) {
    if (error instanceof McpError) throw error;
    throw new McpError(ErrorCode.INTERNAL_ERROR, 'Internal error during update');
  }
}

Task Completion (src/tools/complete-task.ts)

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';
import { CompleteTaskSchema } from '../types/task.js';
import { McpError, ErrorCode } from '../utils/errors.js';

export const completeTaskTool: Tool = {
  name: 'complete_task',
  description: 'Mark a task as completed',
  inputSchema: {
    type: 'object',
    properties: {
      task_id: {
        type: 'string',
        description: 'Task identifier to complete'
      },
      completion_notes: {
        type: 'string',
        description: 'Notes about task completion'
      },
      time_spent: {
        type: 'number',
        minimum: 0,
        description: 'Time spent in minutes'
      }
    },
    required: ['task_id'],
    additionalProperties: false
  }
};

export async function handleCompleteTask(args: any, db: TaskDatabase) {
  try {
    const validatedArgs = CompleteTaskSchema.parse(args);

    // Check if task exists
    const existingTask = await db.getTask(validatedArgs.task_id);
    if (!existingTask) {
      throw new McpError(ErrorCode.TASK_NOT_FOUND, `Task ${validatedArgs.task_id} not found`);
    }

    // Check if task is already completed
    if (existingTask.status === 'completed') {
      throw new McpError(ErrorCode.TASK_ALREADY_COMPLETED, 'Task already completed');
    }

    // Complete the task
    const success = await db.completeTask(
      validatedArgs.task_id,
      validatedArgs.completion_notes,
      validatedArgs.time_spent
    );

    if (success) {
      return {
        content: [{
          type: 'text',
          text: 'Task completed successfully'
        }],
        completed_at: new Date().toISOString(),
        total_time: validatedArgs.time_spent
      };
    } else {
      throw new McpError(ErrorCode.INTERNAL_ERROR, 'Completion failed');
    }
  } catch (error) {
    if (error instanceof McpError) throw error;
    throw new McpError(ErrorCode.INTERNAL_ERROR, 'Internal error during completion');
  }
}

Task Deletion (src/tools/delete-task.ts)

import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';
import { McpError, ErrorCode } from '../utils/errors.js';

export const deleteTaskTool: Tool = {
  name: 'delete_task',
  description: 'Permanently delete a task',
  inputSchema: {
    type: 'object',
    properties: {
      task_id: {
        type: 'string',
        description: 'Task identifier to delete'
      },
      confirm: {
        type: 'boolean',
        description: 'Deletion confirmation (security)',
        default: false
      }
    },
    required: ['task_id', 'confirm'],
    additionalProperties: false
  }
};

export async function handleDeleteTask(args: any, db: TaskDatabase) {
  try {
    const { task_id, confirm } = args;

    // Check confirmation
    if (!confirm) {
      throw new McpError(ErrorCode.INVALID_PARAMS, 'Confirmation required to delete a task');
    }

    // Check if task exists
    const existingTask = await db.getTask(task_id);
    if (!existingTask) {
      throw new McpError(ErrorCode.TASK_NOT_FOUND, `Task ${task_id} not found`);
    }

    // Save information before deletion
    const taskInfo = {
      id: existingTask.id,
      title: existingTask.title,
      status: existingTask.status
    };

    // Delete the task
    const success = await db.deleteTask(task_id);

    if (success) {
      return {
        content: [{
          type: 'text',
          text: `Task "${taskInfo.title}" deleted successfully`
        }],
        deleted_task: taskInfo,
        deleted_at: new Date().toISOString()
      };
    } else {
      throw new McpError(ErrorCode.INTERNAL_ERROR, 'Deletion failed');
    }
  } catch (error) {
    if (error instanceof McpError) throw error;
    throw new McpError(ErrorCode.INTERNAL_ERROR, 'Internal error during deletion');
  }
}

Tools Export (src/tools/index.ts)

import { createTaskTool, handleCreateTask } from './create-task.js';
import { updateTaskTool, handleUpdateTask } from './update-task.js';
import { completeTaskTool, handleCompleteTask } from './complete-task.js';
import { deleteTaskTool, handleDeleteTask } from './delete-task.js';

export const tools = [
  createTaskTool,
  updateTaskTool,
  completeTaskTool,
  deleteTaskTool
];

export const toolHandlers = {
  create_task: handleCreateTask,
  update_task: handleUpdateTask,
  complete_task: handleCompleteTask,
  delete_task: handleDeleteTask
};

7. Endpoints: Resources

Task List (src/resources/task-list.ts)

import { Resource } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';

export const taskListResource: Resource = {
  uri: 'task://list',
  name: 'Task List',
  description: 'All active tasks in the system',
  mimeType: 'application/json'
};

export async function handleTaskListResource(db: TaskDatabase) {
  try {
    const tasks = await db.getAllTasks();

    return {
      contents: [{
        uri: 'task://list',
        mimeType: 'application/json',
        text: JSON.stringify({
          tasks: tasks.map(task => ({
            id: task.id,
            title: task.title,
            status: task.status,
            priority: task.priority,
            assignee: task.assignee,
            due_date: task.due_date,
            created_at: task.created_at
          })),
          total: tasks.length
        }, null, 2)
      }]
    };
  } catch (error) {
    throw new Error('Error retrieving tasks');
  }
}

Statistics (src/resources/task-stats.ts)

import { Resource } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';

export const taskStatsResource: Resource = {
  uri: 'task://stats',
  name: 'Task Statistics',
  description: 'Global statistics of the task system',
  mimeType: 'application/json'
};

export async function handleTaskStatsResource(db: TaskDatabase) {
  try {
    const stats = await db.getStats();
    const tasks = await db.getAllTasks();

    // Calculate overdue tasks
    const now = new Date();
    const overdueTasks = tasks.filter(task => 
      task.due_date && 
      new Date(task.due_date) < now && 
      task.status !== 'completed'
    ).length;

    // Calculate completion rate
    const completionRate = stats.total_tasks > 0 
      ? (stats.completed / stats.total_tasks) * 100 
      : 0;

    const statsData = {
      total_tasks: stats.total_tasks,
      by_status: {
        pending: stats.pending,
        in_progress: stats.in_progress,
        completed: stats.completed,
        cancelled: stats.cancelled
      },
      by_priority: {
        urgent: stats.urgent,
        high: stats.high,
        medium: stats.medium,
        low: stats.low
      },
      overdue_tasks: overdueTasks,
      completion_rate: Math.round(completionRate * 10) / 10,
      average_completion_time: stats.avg_completion_time || 0
    };

    return {
      contents: [{
        uri: 'task://stats',
        mimeType: 'application/json',
        text: JSON.stringify(statsData, null, 2)
      }]
    };
  } catch (error) {
    throw new Error('Error retrieving statistics');
  }
}

Task Detail (src/resources/task-detail.ts)

import { Resource } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';
import { McpError, ErrorCode } from '../utils/errors.js';

export const taskDetailResource: Resource = {
  uri: 'task://detail/{id}',
  name: 'Task Detail',
  description: 'Detailed information of a specific task',
  mimeType: 'application/json'
};

export async function handleTaskDetailResource(uri: string, db: TaskDatabase) {
  try {
    // Extract ID from URI
    const match = uri.match(/task://detail/(.+)/);
    if (!match) {
      throw new McpError(ErrorCode.INVALID_PARAMS, 'Invalid URI for task detail');
    }

    const taskId = match[1];
    if (!taskId) {
      throw new McpError(ErrorCode.INVALID_PARAMS, 'Missing task ID in URI');
    }

    const task = await db.getTask(taskId);

    if (!task) {
      throw new McpError(ErrorCode.TASK_NOT_FOUND, `Task ${taskId} not found`);
    }

    return {
      contents: [{
        uri: uri,
        mimeType: 'application/json',
        text: JSON.stringify(task, null, 2)
      }]
    };
  } catch (error) {
    if (error instanceof McpError) throw error;
    throw new Error('Error retrieving task detail');
  }
}

Resources Export (src/resources/index.ts)

import { taskListResource, handleTaskListResource } from './task-list.js';
import { taskStatsResource, handleTaskStatsResource } from './task-stats.js';
import { taskDetailResource, handleTaskDetailResource } from './task-detail.js';

export const resources = [
  taskListResource,
  taskStatsResource,
  taskDetailResource
];

export const resourceHandlers = {
  taskList: handleTaskListResource,
  taskStats: handleTaskStatsResource,
  taskDetail: handleTaskDetailResource
};

8. Endpoints: Prompts

Weekly Review (src/prompts/weekly-review.ts)

import { Prompt } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';

export const weeklyReviewPrompt: Prompt = {
  name: 'weekly_review',
  description: 'Generate a weekly review report',
  arguments: [
    {
      name: 'week_number',
      description: 'Week number to analyze',
      required: false
    },
    {
      name: 'team',
      description: 'Filter by specific team',
      required: false
    }
  ]
};

export async function handleWeeklyReviewPrompt(args: any, db: TaskDatabase) {
  try {
    const weekNumber = args.week_number || getCurrentWeekNumber();
    const team = args.team || 'all teams';

    // Retrieve data
    const tasks = await db.getAllTasks();
    const stats = await db.getStats();

    // Filter by team if specified
    const filteredTasks = args.team 
      ? tasks.filter(task => task.assignee?.includes(args.team))
      : tasks;

    const description = `Weekly review ${team} - Week ${weekNumber}`;

    return {
      description,
      messages: [
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Generate a detailed review for week ${weekNumber} of ${team} with:

📊 **Available Data:**
- Total tasks: ${filteredTasks.length}
- Completed tasks: ${filteredTasks.filter(t => t.status === 'completed').length}
- In progress tasks: ${filteredTasks.filter(t => t.status === 'in_progress').length}
- Pending tasks: ${filteredTasks.filter(t => t.status === 'pending').length}

📋 **Requested Analysis:**
1. **Tasks completed this week**
2. **Tasks in progress with their progression**
3. **Identified blockers and solutions**
4. **Performance metrics**
5. **Recommendations for next week**

Use a structured format with emojis and clear sections.`
          }
        },
        {
          role: 'assistant',
          content: {
            type: 'text',
            text: `I will analyze the data for week ${weekNumber} for ${team} and generate a comprehensive report...`
          }
        }
      ]
    };
  } catch (error) {
    throw new Error('Error generating weekly review prompt');
  }
}

function getCurrentWeekNumber(): number {
  const now = new Date();
  const start = new Date(now.getFullYear(), 0, 1);
  const days = Math.floor((now.getTime() - start.getTime()) / (24 * 60 * 60 * 1000));
  return Math.ceil((days + start.getDay() + 1) / 7);
}

Sprint Planning (src/prompts/sprint-planning.ts)

import { Prompt } from '@modelcontextprotocol/sdk/types.js';
import { TaskDatabase } from '../database/db.js';

export const sprintPlanningPrompt: Prompt = {
  name: 'sprint_planning',
  description: 'Assist with sprint planning with velocity analysis',
  arguments: [
    {
      name: 'sprint_duration',
      description: 'Sprint duration in days (default: 14)',
      required: false
    },
    {
      name: 'team_capacity',
      description: 'Team capacity in person-days',
      required: false
    }
  ]
};

export async function handleSprintPlanningPrompt(args: any, db: TaskDatabase) {
  try {
    const sprintDuration = args.sprint_duration || 14;
    const teamCapacity = args.team_capacity;

    const tasks = await db.getAllTasks();
    const stats = await db.getStats();

    // Analyze priority tasks
    const highPriorityTasks = tasks.filter(t => 
      ['urgent', 'high'].includes(t.priority) && 
      t.status === 'pending'
    );

    // Calculate average velocity
    const completedTasks = tasks.filter(t => t.status === 'completed');
    const avgCompletionTime = stats.avg_completion_time || 0;

    return {
      description: `Sprint planning - ${sprintDuration} days`,
      messages: [
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Help me plan the next sprint with this data:

🏃‍♂️ **Sprint Parameters:**
- Duration: ${sprintDuration} days
- Team capacity: ${teamCapacity || 'not specified'}

📊 **Task Analysis:**
- Priority tasks (urgent/high): ${highPriorityTasks.length}
- Pending tasks: ${tasks.filter(t => t.status === 'pending').length}
- Average completion time: ${avgCompletionTime} minutes
- Current completion rate: ${((stats.completed / stats.total_tasks) * 100).toFixed(1)}%

🎯 **Request:**
1. **Suggest optimal task distribution**
2. **Identify critical dependencies**
3. **Propose realistic timeline**
4. **Anticipate potential risks**
5. **Recommend tracking metrics**

Generate a detailed plan with clear milestones.`
          }
        },
        {
          role: 'assistant',
          content: {
            type: 'text',
            text: `Let's analyze the data to create an optimal sprint plan...`
          }
        }
      ]
    };
  } catch (error) {
    throw new Error('Error generating sprint planning prompt');
  }
}

Prompts Export (src/prompts/index.ts)

import { weeklyReviewPrompt, handleWeeklyReviewPrompt } from './weekly-review.js';
import { sprintPlanningPrompt, handleSprintPlanningPrompt } from './sprint-planning.js';

export const prompts = [
  weeklyReviewPrompt,
  sprintPlanningPrompt
];

export const promptHandlers = {
  weekly_review: handleWeeklyReviewPrompt,
  sprint_planning: handleSprintPlanningPrompt
};

9. Error Handling

Error Codes (src/utils/errors.ts)

export enum ErrorCode {
  // Standard JSON-RPC errors
  PARSE_ERROR = -32700,
  INVALID_REQUEST = -32600,
  METHOD_NOT_FOUND = -32601,
  INVALID_PARAMS = -32602,
  INTERNAL_ERROR = -32603,

  // Task Manager specific errors
  TASK_NOT_FOUND = -32000,
  PERMISSION_DENIED = -32001,
  TASK_ALREADY_COMPLETED = -32002,
  INVALID_DATE_FORMAT = -32003,
  QUOTA_EXCEEDED = -32004
}

export class McpError extends Error {
  constructor(
    public code: ErrorCode,
    message: string,
    public data?: any
  ) {
    super(message);
    this.name = 'McpError';
  }

  toJsonRpc() {
    return {
      code: this.code,
      message: this.message,
      ...(this.data && { data: this.data })
    };
  }
}

export function getErrorMessage(code: ErrorCode): string {
  switch (code) {
    case ErrorCode.TASK_NOT_FOUND:
      return 'Task not found';
    case ErrorCode.PERMISSION_DENIED:
      return 'Permission denied';
    case ErrorCode.TASK_ALREADY_COMPLETED:
      return 'Task already completed';
    case ErrorCode.INVALID_DATE_FORMAT:
      return 'Invalid date format';
    case ErrorCode.QUOTA_EXCEEDED:
      return 'Quota exceeded';
    default:
      return 'Internal error';
  }
}

10. Main Server

Hybrid HTTP/STDIO Architecture

The TaskManagerServer supports two communication modes:

private createHttpServer() {
  return http.createServer(async (req, res) => {
    // CORS configuration for web integration
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

    // Handle OPTIONS requests (CORS preflight)
    if (req.method === 'OPTIONS') {
      res.writeHead(200);
      res.end();
      return;
    }

    // Health check endpoint
    if (req.method === 'GET' && req.url === '/health') {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ 
        status: 'healthy', 
        timestamp: new Date().toISOString() 
      }));
      return;
    }

    // Main MCP endpoint
    if (req.method === 'POST' && req.url === '/mcp') {
      // JSON-RPC request processing
      // ...
    }
  });
}

HTTP Mode Advantages:

  • Simplified integration with Claude Desktop
  • Easy debugging with curl, Postman, etc.
  • Health check for monitoring
  • CORS enabled for web applications
  • Scalability for multiple clients

2. STDIO Mode (Legacy)

if (process.env.MCP_TRANSPORT === 'stdio') {
  const transport = new StdioServerTransport();
  await this.server.connect(transport);
  console.error('Task Manager MCP Server started (stdio)');
}

MCP Request Handling

The server implements a JSON-RPC translation layer:

private async handleMcpRequest(request: any) {
  const { method, params } = request;

  switch (method) {
    case 'initialize':
      return {
        jsonrpc: '2.0',
        id: request.id,
        result: {
          protocolVersion: '1.0',
          serverInfo: { name: 'task-manager-server', version: '1.0.0' },
          capabilities: { tools: {}, resources: {}, prompts: {} }
        }
      };

    case 'tools/list':
      return { jsonrpc: '2.0', id: request.id, result: { tools } };

    case 'tools/call':
      // Validation and tool execution
      const { name, arguments: args } = params;
      if (!toolHandlers[name as keyof typeof toolHandlers]) {
        throw new McpError(ErrorCode.METHOD_NOT_FOUND, `Tool ${name} not found`);
      }
      const result = await toolHandlers[name as keyof typeof toolHandlers](args, this.database);
      return { jsonrpc: '2.0', id: request.id, result };

    // Other methods...
  }
}

Centralized Error Handling

try {
  const request = JSON.parse(body);
  const response = await this.handleMcpRequest(request);

  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(response));
} catch (error) {
  res.writeHead(500, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify({ 
    error: 'Internal server error', 
    message: error instanceof Error ? error.message : 'Unknown error' 
  }));
}

Main Entry Point (src/server.ts)

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { 
  ListToolsRequestSchema,
  CallToolRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
  ListPromptsRequestSchema,
  GetPromptRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
import * as http from 'http';
import { TaskDatabase } from './database/db.js';
import { tools, toolHandlers } from './tools/index.js';
import { resources, resourceHandlers } from './resources/index.js';
import { prompts, promptHandlers } from './prompts/index.js';
import { McpError, ErrorCode } from './utils/errors.js';

class TaskManagerServer {
  private server: Server;
  private database: TaskDatabase;
  private httpServer?: http.Server;

  constructor() {
    this.database = new TaskDatabase();
    this.server = new Server(
      {
        name: 'task-manager-server',
        version: '1.0.0'
      },
      {
        capabilities: {
          tools: {},
          resources: {},
          prompts: {}
        }
      }
    );

    this.setupHandlers();
  }

  private setupHandlers() {
    // Handler for tools with MCP schemas
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      if (!toolHandlers[name as keyof typeof toolHandlers]) {
        throw new McpError(ErrorCode.METHOD_NOT_FOUND, `Tool ${name} not found`);
      }

      try {
        return await toolHandlers[name as keyof typeof toolHandlers](args, this.database);
      } catch (error) {
        if (error instanceof McpError) {
          throw error;
        }
        throw new McpError(ErrorCode.INTERNAL_ERROR, 'Internal server error');
      }
    });

    // Handler for resources
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
      resources
    }));

    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const { uri } = request.params;

      // Route to appropriate handler based on URI
      if (uri === 'task://list') {
        return await resourceHandlers.taskList(this.database);
      } else if (uri === 'task://stats') {
        return await resourceHandlers.taskStats(this.database);
      } else if (uri.startsWith('task://detail/')) {
        return await resourceHandlers.taskDetail(uri, this.database);
      } else {
        throw new McpError(ErrorCode.METHOD_NOT_FOUND, `Resource ${uri} not found`);
      }
    });

    // Handler for prompts
    this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
      prompts
    }));

    this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      if (!promptHandlers[name as keyof typeof promptHandlers]) {
        throw new McpError(ErrorCode.METHOD_NOT_FOUND, `Prompt ${name} not found`);
      }

      return await promptHandlers[name as keyof typeof promptHandlers](args || {}, this.database);
    });
  }

  async start() {
    const port = process.env.PORT || 3000;

    if (process.env.MCP_TRANSPORT === 'stdio') {
      const transport = new StdioServerTransport();
      await this.server.connect(transport);
      console.error('Task Manager MCP Server started (stdio)');
    } else {
      this.httpServer = this.createHttpServer();
      this.httpServer.listen(port, () => {
        console.error(`Task Manager MCP Server started on port ${port} (HTTP)`);
      });
    }
  }

  async stop() {
    if (this.httpServer) {
      this.httpServer.close();
    }
  }
}

// Start the server
const server = new TaskManagerServer();
server.start().catch(console.error);

Advanced Error Handling Pattern

// src/utils/error-handling.ts
export class TaskManagerError extends Error {
  constructor(
    public code: string,
    message: string,
    public details?: any
  ) {
    super(message);
    this.name = 'TaskManagerError';
  }
}

export function createErrorResponse(error: unknown, id: number | string) {
  if (error instanceof TaskManagerError) {
    return {
      jsonrpc: '2.0',
      id,
      error: {
        code: -32000,
        message: error.message,
        data: { code: error.code, details: error.details }
      }
    };
  }

  return {
    jsonrpc: '2.0',
    id,
    error: {
      code: -32603,
      message: 'Internal error',
      data: { originalError: error instanceof Error ? error.message : String(error) }
    }
  };
}

Resource Caching Pattern

// src/utils/cache.ts
export class ResourceCache {
  private cache = new Map<string, { data: any; timestamp: number; ttl: number }>();

  set(key: string, data: any, ttlMs: number = 60000) {
    this.cache.set(key, {
      data,
      timestamp: Date.now(),
      ttl: ttlMs
    });
  }

  get(key: string): any | null {
    const entry = this.cache.get(key);
    if (!entry) return null;

    if (Date.now() - entry.timestamp > entry.ttl) {
      this.cache.delete(key);
      return null;
    }

    return entry.data;
  }

  clear() {
    this.cache.clear();
  }
}

// Usage in resources
export async function handleTaskListResource(database: TaskDatabase): Promise<Resource> {
  const cacheKey = 'task-list';
  const cached = resourceCache.get(cacheKey);

  if (cached) {
    return {
      contents: [{
        type: 'text',
        text: cached
      }]
    };
  }

  const tasks = await database.getAllTasks();
  const result = JSON.stringify({ tasks, total: tasks.length, cached: false }, null, 2);

  resourceCache.set(cacheKey, result, 30000); // 30 seconds TTL

  return {
    contents: [{
      type: 'text',
      text: result
    }]
  };
}

Applied patterns:

  • Promisification of SQLite callbacks
  • Data transformation (ID prefixing)
  • Explicit null handling
  • Type safety with TypeScript

11. Code Organization and Patterns

Structuring Pattern

The project follows a clear modular architecture:

src/
├── server.ts          # Entry point and orchestration
├── database/          # Persistence layer
│   ├── db.ts         # Database logic
│   └── schema.sql    # Table structure
├── tools/            # MCP Actions (Create, Update, Delete)
├── resources/        # MCP Data (Read-only)
├── prompts/          # LLM interaction templates
├── types/            # TypeScript schemas and types
└── utils/            # Cross-cutting utilities

Export Pattern

Each module exposes a consistent pattern:

// tools/index.ts
export const tools = [createTaskTool, updateTaskTool, ...];
export const toolHandlers = {
  create_task: handleCreateTask,
  // ...
};

Benefits:

  • Centralized imports from main server
  • Automatic discovery of new tools
  • Type safety with object keys
  • Simplified maintenance

Validation Pattern

Layered validation with Zod:

export async function handleCreateTask(args: any, db: TaskDatabase) {
  try {
    // 1. Schema validation
    const validatedArgs = CreateTaskSchema.parse(args);

    // 2. Business logic validation
    if (validatedArgs.due_date && new Date(validatedArgs.due_date) < new Date()) {
      throw new McpError(ErrorCode.INVALID_PARAMS, 'Due date cannot be in the past');
    }

    // 3. Database operation
    const task = await db.createTask(validatedArgs);

    return {
      task_id: `task-${task.id}`,
      status: task.status,
      created_at: task.created_at
    };
  } catch (error) {
    if (error instanceof z.ZodError) {
      throw new McpError(ErrorCode.INVALID_PARAMS, `Validation failed: ${error.message}`);
    }
    throw error;
  }
}

Database Pattern

Encapsulation with promisification:

async getTask(id: string): Promise<Task | null> {
  const sql = 'SELECT * FROM tasks WHERE id = ?';
  return new Promise((resolve, reject) => {
    this.db.get(sql, [id.replace('task-', '')], (err, row) => {
      if (err) reject(err);
      else if (!row) resolve(null);
      else resolve({
        ...row,
        id: `task-${row.id}`,
        tags: row.tags ? JSON.parse(row.tags) : []
      });
    });
  });
}

Applied patterns:

  • Promisification of SQLite callbacks
  • Data transformation (ID prefixing)
  • Explicit null handling
  • Type safety with TypeScript

12. Testing and Validation

Jest Configuration (jest.config.js)

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['/src', '/tests'],
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
  transform: {
    '^.+\.ts$': 'ts-jest',
  },
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
  ],
  moduleFileExtensions: ['ts', 'js', 'json'],
};

Integration Tests (tests/integration.test.ts)

import { TaskDatabase } from '../src/database/db';
import { handleCreateTask } from '../src/tools/create-task';
import { handleTaskListResource } from '../src/resources/task-list';

describe('Task Manager Integration Tests', () => {
  let db: TaskDatabase;

  beforeEach(() => {
    // Use in-memory database for tests
    db = new TaskDatabase(':memory:');
  });

  test('Creating and retrieving a task', async () => {
    // Create a task
    const result = await handleCreateTask({
      title: 'Test Task',
      description: 'Task for testing',
      priority: 'high',
      tags: ['test', 'automation']
    }, db);

    expect(result.task_id).toBeDefined();
    expect(result.status).toBe('pending');

    // Retrieve task list
    const listResult = await handleTaskListResource(db);
    const tasks = JSON.parse(listResult.contents[0].text);

    expect(tasks.tasks).toHaveLength(1);
    expect(tasks.tasks[0].title).toBe('Test Task');
    expect(tasks.tasks[0].priority).toBe('high');
  });

  test('Error handling - non-existent task', async () => {
    const task = await db.getTask('task-999');
    expect(task).toBeNull();
  });
});

Manual Test Script (scripts/test-mcp.js)

#!/usr/bin/env node

const { spawn } = require('child_process');

function testMcpServer() {
  console.log('🧪 Testing MCP Task Manager server...n');

  const server = spawn('npm', ['run', 'dev']);

  // Initialization test
  const initMessage = {
    jsonrpc: '2.0',
    id: 1,
    method: 'initialize',
    params: {
      protocolVersion: '1.0',
      clientInfo: { name: 'test-client', version: '1.0.0' }
    }
  };

  server.stdin.write(JSON.stringify(initMessage) + 'n');

  server.stdout.on('data', (data) => {
    try {
      const response = JSON.parse(data.toString());
      console.log('✅ Response received:', response);
    } catch (error) {
      console.log('📝 Output:', data.toString());
    }
  });

  server.stderr.on('data', (data) => {
    console.error('❌ Error:', data.toString());
  });

  // Stop after 5 seconds
  setTimeout(() => {
    server.kill();
    console.log('n🏁 Test completed');
  }, 5000);
}

testMcpServer();

13. Docker Deployment

Multi-stage Dockerfile

Create an optimized Dockerfile with multi-stage build:

# Task Manager MCP Server Dockerfile
# Multi-stage build for optimized production image

# Stage 1: Build stage
FROM node:20-alpine AS builder

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies (including dev dependencies for building)
RUN npm ci

# Copy source code
COPY src/ ./src/
COPY tsconfig.json ./

# Build the application
RUN npm run build

# Stage 2: Production stage
FROM node:20-alpine AS production

# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init

# Create app user for security
RUN addgroup -g 1001 -S mcpuser && 
    adduser -S mcpuser -u 1001

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install only production dependencies
RUN npm ci --only=production && 
    npm cache clean --force

# Copy built application from builder stage
COPY --from=builder /app/dist ./dist

# Create data directory for SQLite database
RUN mkdir -p /app/data && 
    chown -R mcpuser:mcpuser /app

USER mcpuser

# Expose the port for MCP server (HTTP mode by default)
EXPOSE 3000

# Set environment variables for HTTP mode
ENV MCP_TRANSPORT=http
ENV PORT=3000

# Health check for HTTP MCP server
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 
    CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

# Use dumb-init to handle signals properly
ENTRYPOINT ["dumb-init", "--"]

# Start the MCP server
CMD ["node", "dist/server.js"]

.dockerignore File

The .dockerignore file optimizes Docker build context by excluding unnecessary files:

node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
dist
coverage
*.md
.nyc_output
tasks.db

Why these exclusions?

  • node_modules/: Will be installed in the container
  • dist/: Will be generated during build
  • tasks.db: Local development database
  • .env* files: Prevents accidental inclusion of secrets
  • *.md: Documentation not needed in production

Docker Scripts in package.json

{
  "scripts": {
    "build": "tsc && mkdir -p dist/database && cp src/database/schema.sql dist/database/",
    "start": "node dist/server.js",
    "dev": "nodemon src/server.ts",
    "test": "jest",
    "docker:build": "docker build -t task-manager-mcp-server .",
    "docker:run": "docker run -d --name task-manager-mcp-server -p 3000:3000 task-manager-mcp-server",
    "docker:stop": "docker stop task-manager-mcp-server && docker rm task-manager-mcp-server",
    "docker:logs": "docker logs task-manager-mcp-server"
  }
}

HTTP API Endpoints

The server exposes the following endpoints:

Health Check

GET /health

Returns the server health status.

MCP Communication

POST /mcp
Content-Type: application/json

Example call to list tools:

curl -X POST http://localhost:3000/mcp 
  -H "Content-Type: application/json" 
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'

Example call to create a task:

curl -X POST http://localhost:3000/mcp 
  -H "Content-Type: application/json" 
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "create_task",
      "arguments": {
        "title": "New task",
        "description": "Task description",
        "priority": "high"
      }
    }
  }'

Deployment Commands

# Build Docker image
npm run docker:build

# Launch container
npm run docker:run

# View logs
npm run docker:logs

# Stop container
npm run docker:stop

# Build and run in one command
npm run docker:build && npm run docker:run

Production Configuration

Environment Variables

The .env.example file defines configuration variables:

NODE_ENV=production
MCP_TRANSPORT=http
PORT=3000
DB_PATH=/app/data/tasks.db
LOG_LEVEL=info

Variable descriptions:

  • NODE_ENV: Runtime environment (development/production)
  • MCP_TRANSPORT: Transport mode (http/stdio)
  • PORT: HTTP server listening port
  • DB_PATH: Path to SQLite database
  • LOG_LEVEL: Logging level (debug/info/warn/error)

Usage:

# Copy and customize
cp .env.example .env.production

# Use with Docker
docker run --env-file .env.production task-manager-mcp-server

Transport Mode

The server supports two modes:

  • HTTP (default): Communication via REST endpoints
  • STDIO: Communication via stdin/stdout (for direct integration)

Volume for Data Persistence

# Launch with persistent volume
docker run -d 
  --name task-manager-mcp-server 
  -p 3000:3000 
  -v task-manager-data:/app/data 
  task-manager-mcp-server

Docker Compose (optional)

The docker-compose.yml file simplifies deployment and container management:

version: '3.8'

services:
  task-manager-mcp:
    build: .
    container_name: task-manager-mcp-server
    ports:
      - "3000:3000"
    volumes:
      - task-manager-data:/app/data
    environment:
      - NODE_ENV=production
      - MCP_TRANSPORT=http
      - PORT=3000
      - DB_PATH=/app/data/tasks.db
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  task-manager-data:
    driver: local

Docker Compose advantages:

  • Data persistence: Named volume for SQLite database
  • Automatic restart: restart: unless-stopped
  • HTTP health check: Automatic monitoring via /health
  • Centralized configuration: Environment variables defined
  • Simplified management: docker-compose up -d to start everything

Claude Desktop Integration

Modify Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "task-mcp": {
      "type": "http",
      "url": "http://localhost:3000"
    }
  }
}

Docker Configuration

To use the server via Docker:

{
  "mcpServers": {
    "task-mcp": {
      "type": "http",
      "url": "http://localhost:3000"
    }
  }
}

Then launch the container:

npm run docker:build
npm run docker:run

STDIO Configuration (alternative)

If you prefer to use stdio communication:

{
  "mcpServers": {
    "task-manager": {
      "command": "node",
      "args": ["https://dev.to/path/to/mcp-task-manager/dist/server.js"],
      "env": {
        "NODE_ENV": "production",
        "MCP_TRANSPORT": "stdio"
      }
    }
  }
}

Monitoring and Maintenance

Monitoring with Health Check

The /health endpoint provides status information:

# Simple check
curl http://localhost:3000/health

# Response
{
  "status": "healthy",
  "timestamp": "2025-11-05T16:25:58.230Z"
}

Container Logs

# View logs in real-time
docker logs -f task-manager-mcp-server
npm run docker:logs

# View last lines
docker logs --tail 100 task-manager-mcp-server

Docker Metrics

# Container statistics
docker stats task-manager-mcp-server

# Complete inspection
docker inspect task-manager-mcp-server

Updates

# Method 1: NPM Scripts
npm run docker:stop
npm run docker:build
npm run docker:run

# Method 2: Docker Compose
docker-compose down
docker-compose build
docker-compose up -d

Data Backup

# Create volume backup
docker run --rm -v task-manager-data:/data -v $(pwd):/backup alpine tar czf /backup/tasks-backup.tar.gz -C /data .

# Restore backup
docker run --rm -v task-manager-data:/data -v $(pwd):/backup alpine tar xzf /backup/tasks-backup.tar.gz -C /data

Debug and Troubleshooting

# Access container
docker exec -it task-manager-mcp-server sh

# Check processes
docker exec task-manager-mcp-server ps aux

# Test connectivity
docker exec task-manager-mcp-server wget -qO- http://localhost:3000/health

14. Additional Resources

MCP Documentation

Development Tools

Possible Extensions

  1. Authentication: JWT, OAuth 2.0
  2. Webhooks: Real-time notifications
  3. Integrations: Slack, Teams, GitHub Issues
  4. Analytics: Advanced metrics, dashboards
  5. REST API: Complementary HTTP interface

Practical Usage Examples

API Testing with curl

# 1. Start server
npm run docker:run

# 2. Check health
curl http://localhost:3000/health

# 3. List available tools
curl -X POST http://localhost:3000/mcp 
  -H "Content-Type: application/json" 
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'

# 4. Create a task
curl -X POST http://localhost:3000/mcp 
  -H "Content-Type: application/json" 
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "create_task",
      "arguments": {
        "title": "Implement authentication",
        "description": "Add JWT auth to MCP server",
        "priority": "high",
        "tags": ["security", "backend"],
        "assignee": "john.doe@example.com"
      }
    }
  }'

# 5. List tasks
curl -X POST http://localhost:3000/mcp 
  -H "Content-Type: application/json" 
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "resources/read",
    "params": {
      "uri": "task://list"
    }
  }'

# 6. View statistics
curl -X POST http://localhost:3000/mcp 
  -H "Content-Type: application/json" 
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "resources/read",
    "params": {
      "uri": "task://stats"
    }
  }'

Script Integration

#!/bin/bash
# create-task.sh - Script to create tasks via API

TITLE="$1"
DESCRIPTION="$2"
PRIORITY="${3:-medium}"

if [ -z "$TITLE" ]; then
  echo "Usage: $0  [description] [priority]"</span>
  <span class="nb">exit </span>1
<span class="k">fi

</span>curl <span class="nt">-X</span> POST http://localhost:3000/mcp <span class="se"></span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se"></span>
  <span class="nt">-d</span> <span class="s2">"{
    </span><span class="se">"</span><span class="s2">jsonrpc</span><span class="se">"</span><span class="s2">: </span><span class="se">"</span><span class="s2">2.0</span><span class="se">"</span><span class="s2">,
    </span><span class="se">"</span><span class="s2">id</span><span class="se">"</span><span class="s2">: 1,
    </span><span class="se">"</span><span class="s2">method</span><span class="se">"</span><span class="s2">: </span><span class="se">"</span><span class="s2">tools/call</span><span class="se">"</span><span class="s2">,
    </span><span class="se">"</span><span class="s2">params</span><span class="se">"</span><span class="s2">: {
      </span><span class="se">"</span><span class="s2">name</span><span class="se">"</span><span class="s2">: </span><span class="se">"</span><span class="s2">create_task</span><span class="se">"</span><span class="s2">,
      </span><span class="se">"</span><span class="s2">arguments</span><span class="se">"</span><span class="s2">: {
        </span><span class="se">"</span><span class="s2">title</span><span class="se">"</span><span class="s2">: </span><span class="se">"</span><span class="nv">$TITLE</span><span class="se">"</span><span class="s2">,
        </span><span class="se">"</span><span class="s2">description</span><span class="se">"</span><span class="s2">: </span><span class="se">"</span><span class="nv">$DESCRIPTION</span><span class="se">"</span><span class="s2">,
        </span><span class="se">"</span><span class="s2">priority</span><span class="se">"</span><span class="s2">: </span><span class="se">"</span><span class="nv">$PRIORITY</span><span class="se">"</span><span class="s2">
      }
    }
  }"</span> | jq <span class="s1">'.'</span>
</code></pre>
</div>
<h3 id="usage-examples">
<p>  Usage Examples<br />
</h3>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code><span class="c"># Local development</span>
npm run dev

<span class="c"># Build and test Docker image</span>
npm run docker:build
npm run docker:run
npm run docker:logs

<span class="c"># With docker-compose</span>
docker-compose up <span class="nt">-d</span>
docker-compose logs <span class="nt">-f</span>

<span class="c"># Production with persistent volume</span>
docker run <span class="nt">-d</span> <span class="se"></span>
  <span class="nt">--name</span> task-manager-mcp-server <span class="se"></span>
  <span class="nt">-v</span> <span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span>/data:/app/data <span class="se"></span>
  task-manager-mcp-server
</code></pre>
</div>
<h2 id="%f0%9f%8e%af-conclusion">
<p>  🎯 Conclusion<br />
</h2>
<p>This tutorial has guided you through creating a complete professional MCP Task Manager server. You now have:</p>
<h3 id="%e2%9c%85-complete-mcp-architecture">
<p>  ✅ <strong>Complete MCP Architecture</strong><br />
</h3>
<ul>
<li>
<strong>4 Tools</strong>: Create, update, complete, delete tasks</li>
<li>
<strong>3 Resources</strong>: List, statistics, task details
</li>
<li>
<strong>2 Prompts</strong>: Weekly review, sprint planning</li>
<li>
<strong>Zod Validation</strong>: Type safety and robust validation</li>
</ul>
<h3 id="%e2%9c%85-production-ready-infrastructure">
<p>  ✅ <strong>Production-Ready Infrastructure</strong><br />
</h3>
<ul>
<li>
<strong>Hybrid HTTP/STDIO server</strong> with REST endpoints</li>
<li>
<strong>SQLite database</strong> with optimized schema</li>
<li>
<strong>Multi-stage Docker</strong> with non-root user</li>
<li>
<strong>Health checks</strong> and integrated monitoring</li>
<li>
<strong>CORS and error handling</strong> professional-grade</li>
</ul>
<h3 id="%e2%9c%85-automated-deployment">
<p>  ✅ <strong>Automated Deployment</strong><br />
</h3>
<ul>
<li>
<strong>NPM scripts</strong> for Docker (build, run, stop, logs)</li>
<li>
<strong>Docker Compose</strong> with persistent volumes</li>
<li><strong>Configurable environment variables</strong></li>
<li>
<strong>HTTP health checks</strong> for monitoring</li>
</ul>
<h3 id="%e2%9c%85-claude-desktop-configuration">
<p>  ✅ <strong>Claude Desktop Configuration</strong><br />
</h3>
<div class="highlight js-code-highlight">
<pre class="highlight json"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"mcpServers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"task-mcp"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http://localhost:3000"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre>
</div>
<h3 id="%f0%9f%9a%80-implemented-features">
<p>  🚀 <strong>Implemented Features</strong><br />
</h3>
<h4 id="tools-actions">
<p>  Tools (Actions)<br />
</h4>
<ul>
<li>
<code>create_task</code>: Creation with complete validation</li>
<li>
<code>update_task</code>: Modification with completed task protection</li>
<li>
<code>complete_task</code>: Finalization with notes and time tracking</li>
<li>
<code>delete_task</code>: Secure deletion with confirmation</li>
</ul>
<h4 id="resources-data">
<p>  Resources (Data)<br />
</h4>
<ul>
<li>
<code>task://list</code>: Overview with metadata</li>
<li>
<code>task://stats</code>: Dashboards and metrics</li>
<li>
<code>task://detail/{id}</code>: Detailed information per task</li>
</ul>
<h4 id="prompts-llm-templates">
<p>  Prompts (LLM Templates)<br />
</h4>
<ul>
<li>
<code>weekly_review</code>: Weekly performance analysis</li>
<li>
<code>sprint_planning</code>: Task planning and distribution</li>
</ul>
<h3 id="%f0%9f%94%a7-extensibility">
<p>  🔧 <strong>Extensibility</strong><br />
</h3>
<p>The modular architecture facilitates adding new features:
</p>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code><span class="c1">// New tool in src/tools/</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">archiveTaskTool</span><span class="p">:</span> <span class="nx">Tool</span> <span class="o">=</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">};</span>
<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nf">handleArchiveTask</span><span class="p">(</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">db</span><span class="p">:</span> <span class="nx">TaskDatabase</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>

<span class="c1">// New resource in src/resources/</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">taskCalendarResource</span><span class="p">:</span> <span class="nx">Resource</span> <span class="o">=</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">};</span>
<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nf">handleTaskCalendarResource</span><span class="p">(</span><span class="nx">db</span><span class="p">:</span> <span class="nx">TaskDatabase</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>

<span class="c1">// New prompt in src/prompts/</span>
<span class="k">export</span> <span class="kd">const</span> <span class="nx">standupPrompt</span><span class="p">:</span> <span class="nx">Prompt</span> <span class="o">=</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">};</span>
<span class="k">export</span> <span class="k">async</span> <span class="kd">function</span> <span class="nf">handleStandupPrompt</span><span class="p">(</span><span class="nx">args</span><span class="p">:</span> <span class="kr">any</span><span class="p">,</span> <span class="nx">db</span><span class="p">:</span> <span class="nx">TaskDatabase</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
</code></pre>
</div>
<h3 id="%f0%9f%9b%a0-suggested-next-steps">
<p>  🛠 <strong>Suggested Next Steps</strong><br />
</h3>
<ol>
<li>
<strong>Authentication and Authorization</strong>
</li>
</ol>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code>   <span class="c1">// JWT middleware to secure API</span>
   <span class="k">import</span> <span class="nx">jwt</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">jsonwebtoken</span><span class="dl">'</span><span class="p">;</span>

   <span class="kd">const</span> <span class="nx">authMiddleware</span> <span class="o">=</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">next</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
     <span class="kd">const</span> <span class="nx">token</span> <span class="o">=</span> <span class="nx">req</span><span class="p">.</span><span class="nx">headers</span><span class="p">.</span><span class="nx">authorization</span><span class="p">?.</span><span class="nf">split</span><span class="p">(</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">)[</span><span class="mi">1</span><span class="p">];</span>
     <span class="k">if </span><span class="p">(</span><span class="o">!</span><span class="nx">token</span> <span class="o">||</span> <span class="o">!</span><span class="nx">jwt</span><span class="p">.</span><span class="nf">verify</span><span class="p">(</span><span class="nx">token</span><span class="p">,</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">JWT_SECRET</span><span class="p">))</span> <span class="p">{</span>
       <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nf">status</span><span class="p">(</span><span class="mi">401</span><span class="p">).</span><span class="nf">json</span><span class="p">({</span> <span class="na">error</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Unauthorized</span><span class="dl">'</span> <span class="p">});</span>
     <span class="p">}</span>
     <span class="nf">next</span><span class="p">();</span>
   <span class="p">};</span>
</code></pre>
</div>
<ol>
<li>
<strong>External Integrations</strong>
</li>
</ol>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code>   <span class="c1">// Webhooks for notifications</span>
   <span class="k">export</span> <span class="kd">const</span> <span class="nx">webhookTool</span><span class="p">:</span> <span class="nx">Tool</span> <span class="o">=</span> <span class="p">{</span>
     <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">setup_webhook</span><span class="dl">'</span><span class="p">,</span>
     <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Configure external notifications</span><span class="dl">'</span><span class="p">,</span>
     <span class="c1">// ...</span>
   <span class="p">};</span>

   <span class="c1">// Calendar synchronization</span>
   <span class="k">export</span> <span class="kd">const</span> <span class="nx">calendarSyncTool</span><span class="p">:</span> <span class="nx">Tool</span> <span class="o">=</span> <span class="p">{</span>
     <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">sync_calendar</span><span class="dl">'</span><span class="p">,</span>
     <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Synchronize with Google Calendar/Outlook</span><span class="dl">'</span><span class="p">,</span>
     <span class="c1">// ...</span>
   <span class="p">};</span>
</code></pre>
</div>
<ol>
<li>
<strong>Web Interface (Optional)</strong>
</li>
</ol>
<div class="highlight js-code-highlight">
<pre class="highlight shell"><code>   <span class="c"># React/Vue frontend for administration</span>
   npm create vite@latest task-manager-ui <span class="nt">--</span> <span class="nt">--template</span> react-ts
   <span class="nb">cd </span>task-manager-ui
   npm <span class="nb">install </span>axios @tanstack/react-query
</code></pre>
</div>
<ol>
<li>
<strong>Advanced Database</strong>
</li>
</ol>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code>   <span class="c1">// Migration to PostgreSQL for production</span>
   <span class="k">import</span> <span class="p">{</span> <span class="nx">Pool</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">pg</span><span class="dl">'</span><span class="p">;</span>

   <span class="k">export</span> <span class="kd">class</span> <span class="nc">PostgresTaskDatabase</span> <span class="kd">extends</span> <span class="nc">TaskDatabase</span> <span class="p">{</span>
     <span class="k">private</span> <span class="nx">pool</span><span class="p">:</span> <span class="nx">Pool</span><span class="p">;</span>
     <span class="c1">// ...</span>
   <span class="p">}</span>
</code></pre>
</div>
<ol>
<li>
<strong>Analytics and Monitoring</strong>
</li>
</ol>
<div class="highlight js-code-highlight">
<pre class="highlight typescript"><code>   <span class="c1">// Prometheus metrics</span>
   <span class="k">import</span> <span class="nx">prometheus</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">prom-client</span><span class="dl">'</span><span class="p">;</span>

   <span class="kd">const</span> <span class="nx">taskCreationCounter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">prometheus</span><span class="p">.</span><span class="nc">Counter</span><span class="p">({</span>
     <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">tasks_created_total</span><span class="dl">'</span><span class="p">,</span>
     <span class="na">help</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Total number of tasks created</span><span class="dl">'</span>
   <span class="p">});</span>
</code></pre>
</div>
<p>The MCP Task Manager server is now ready for production use and can serve as a solid foundation for more complex task management and productivity applications!</p>
<h3 id="%f0%9f%93%9a-reference-documentation">
<p>  📚 <strong>Reference Documentation</strong><br />
</h3>
<ul>
<li><a href="https://modelcontextprotocol.io/docs" rel="noopener noreferrer">MCP Specification</a></li>
<li><a href="https://docs.anthropic.com/claude/docs" rel="noopener noreferrer">Claude Desktop Configuration</a></li>
<li><a href="https://docs.docker.com/develop/dev-best-practices/" rel="noopener noreferrer">Docker Best Practices</a></li>
<li><a href="https://www.typescriptlang.org/docs/" rel="noopener noreferrer">TypeScript Handbook</a></li>
<li><a href="https://zod.dev/" rel="noopener noreferrer">Zod Documentation</a></li>
</ul>
			</div>

			<div class="cs-entry__tags"><ul><li><a href="https://prodsens.live/tag/ai/" rel="tag">ai</a></li><li><a href="https://prodsens.live/tag/mcp/" rel="tag">mcp</a></li><li><a href="https://prodsens.live/tag/prodsens-live/" rel="tag">prodsens live</a></li><li><a href="https://prodsens.live/tag/software/" rel="tag">Software</a></li><li><a href="https://prodsens.live/tag/typescript/" rel="tag">typescript</a></li></ul></div>			<div class="cs-entry__after-share-buttons">
						<div class="pk-share-buttons-wrap pk-share-buttons-layout-simple pk-share-buttons-scheme-simple-light pk-share-buttons-has-counts pk-share-buttons-has-total-counts pk-share-buttons-after-post pk-share-buttons-mode-php pk-share-buttons-mode-rest" data-post-id="40640" data-share-url="https://prodsens.live/2025/11/06/create-a-mcp-server-from-scratch/" >

							<div class="pk-share-buttons-total pk-share-buttons-total-no-count">
												<div class="pk-share-buttons-title pk-font-primary">Total</div>
							<div class="pk-share-buttons-count pk-font-heading">0</div>
							<div class="pk-share-buttons-label pk-font-secondary">Shares</div>
										</div>
				
			<div class="pk-share-buttons-items">

										<div class="pk-share-buttons-item pk-share-buttons-facebook pk-share-buttons-no-count" data-id="facebook">

							<a href="https://www.facebook.com/sharer.php?u=https://prodsens.live/2025/11/06/create-a-mcp-server-from-scratch/" class="pk-share-buttons-link" target="_blank">

																	<i class="pk-share-buttons-icon pk-icon pk-icon-facebook"></i>
								
								
																	<span class="pk-share-buttons-label pk-font-primary">Share</span>
								
																	<span class="pk-share-buttons-count pk-font-secondary">0</span>
															</a>

							
							
													</div>
											<div class="pk-share-buttons-item pk-share-buttons-twitter pk-share-buttons-no-count" data-id="twitter">

							<a href="https://twitter.com/share?&text=Create%20a%20MCP%20server%20from%20scratch&via=prodsens_pro&url=https://prodsens.live/2025/11/06/create-a-mcp-server-from-scratch/" class="pk-share-buttons-link" target="_blank">

																	<i class="pk-share-buttons-icon pk-icon pk-icon-twitter"></i>
								
								
																	<span class="pk-share-buttons-label pk-font-primary">Tweet</span>
								
																	<span class="pk-share-buttons-count pk-font-secondary">0</span>
															</a>

							
							
													</div>
											<div class="pk-share-buttons-item pk-share-buttons-pinterest pk-share-buttons-no-count" data-id="pinterest">

							<a href="https://pinterest.com/pin/create/bookmarklet/?url=https://prodsens.live/2025/11/06/create-a-mcp-server-from-scratch/&media=https://prodsens.live/wp-content/uploads/2025/11/40640-create-a-mcp-server-from-scratch.jpg" class="pk-share-buttons-link" target="_blank">

																	<i class="pk-share-buttons-icon pk-icon pk-icon-pinterest"></i>
								
								
																	<span class="pk-share-buttons-label pk-font-primary">Pin it</span>
								
																	<span class="pk-share-buttons-count pk-font-secondary">0</span>
															</a>

							
							
													</div>
								</div>
		</div>
				</div>
			


<div class="cs-entry__comments cs-entry__comments-collapse" id="comments-hidden">

	
	
		<div id="respond" class="comment-respond">
		<h5 class="cs-section-heading cnvs-block-section-heading is-style-cnvs-block-section-heading-default halignleft  "><span class="cnvs-section-title"><span>Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="/2025/11/06/create-a-mcp-server-from-scratch/#respond" style="display:none;">Cancel reply</a></small></span></span></h5><form action="https://prodsens.live/wp-comments-post.php" method="post" id="commentform" class="comment-form"><p class="comment-notes"><span id="email-notes">Your email address will not be published.</span> <span class="required-field-message">Required fields are marked <span class="required">*</span></span></p><p class="comment-form-comment"><label for="comment">Comment <span class="required">*</span></label> <textarea autocomplete="new-password"  id="i402533545"  name="i402533545"   cols="45" rows="8" maxlength="65525" required></textarea><textarea id="comment" aria-label="hp-comment" aria-hidden="true" name="comment" autocomplete="new-password" style="padding:0 !important;clip:rect(1px, 1px, 1px, 1px) !important;position:absolute !important;white-space:nowrap !important;height:1px !important;width:1px !important;overflow:hidden !important;" tabindex="-1"></textarea><script data-noptimize>document.getElementById("comment").setAttribute( "id", "a3617b2918a86b4915ba3a3a83664ffa" );document.getElementById("i402533545").setAttribute( "id", "comment" );</script></p><p class="comment-form-author"><label for="author">Name <span class="required">*</span></label> <input id="author" name="author" type="text" value="" size="30" maxlength="245" autocomplete="name" required /></p>
<p class="comment-form-email"><label for="email">Email <span class="required">*</span></label> <input id="email" name="email" type="email" value="" size="30" maxlength="100" aria-describedby="email-notes" autocomplete="email" required /></p>
<p class="comment-form-url"><label for="url">Website</label> <input id="url" name="url" type="url" value="" size="30" maxlength="200" autocomplete="url" /></p>
<p class="comment-form-cookies-consent"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes" /> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label></p>
<p class="form-submit"><input name="submit" type="submit" id="submit" class="submit" value="Post Comment" /> <input type='hidden' name='comment_post_ID' value='40640' id='comment_post_ID' />
<input type='hidden' name='comment_parent' id='comment_parent' value='0' />
</p></form>	</div><!-- #respond -->
	
</div>

	<div class="cs-entry__comments-show" id="comments">
		<button>View Comments (0)</button>
	</div>

	<div class="cs-entry__prev-next">
					<div class="cs-entry__prev-next-item cs-entry__prev">

				<a class="cs-entry__prev-next-link" href="https://prodsens.live/2025/11/06/erc-20-erc-721erc-1155/"></a>

				<div class="cs-entry__prev-next-label">
					<h5 class="cs-section-heading cnvs-block-section-heading is-style-cnvs-block-section-heading-default halignleft  "><span class="cnvs-section-title"><span><span class="cs-section-subheadings">Previous Post</span></span></span></h5>				</div>

				<div class="cs-entry">
					<div class="cs-entry__outer">
						
						<div class="cs-entry__inner cs-entry__content">
							<div class="cs-entry__post-meta" ><div class="cs-meta-category"><ul class="post-categories">
	<li><a href="https://prodsens.live/category/software/" rel="category tag">Software</a></li></ul></div></div>
							<h2 class="cs-entry__title"><a href="https://prodsens.live/2025/11/06/erc-20-erc-721erc-1155/">ERC-20, ERC-721,ERC-1155</a></h2>
							<div class="cs-entry__post-meta" ><div class="cs-meta-date">November 6, 2025</div></div>						</div>
					</div>
				</div>
			</div>
						<div class="cs-entry__prev-next-item cs-entry__next">

				<a class="cs-entry__prev-next-link" href="https://prodsens.live/2025/11/06/when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response/"></a>

				<div class="cs-entry__prev-next-label">
					<h5 class="cs-section-heading cnvs-block-section-heading is-style-cnvs-block-section-heading-default halignleft  "><span class="cnvs-section-title"><span><span class="cs-section-subheadings">Next Post</span></span></span></h5>				</div>

				<div class="cs-entry">
					<div class="cs-entry__outer">
													<div class="cs-entry__inner cs-entry__thumbnail cs-overlay-ratio cs-ratio-square">
								<div class="cs-overlay-background cs-overlay-transparent">
									<img width="110" height="110" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAG4AAABuAQMAAAD8lbS4AAAAA1BMVEUAAP+KeNJXAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAABVJREFUOMtjYBgFo2AUjIJRMAroAQAGcgABdoTxvAAAAABJRU5ErkJggg==" class="attachment-csco-small size-csco-small pk-lazyload wp-post-image" alt="when-ai-becomes-your-sre:-how-incident.io-is-automating-incident-response" decoding="async" data-pk-sizes="auto" data-ls-sizes="(max-width: 110px) 100vw, 110px" data-pk-src="https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-110x110.png" data-pk-srcset="https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-110x110.png 110w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-300x300.png 300w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-1024x1024.png 1024w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-150x150.png 150w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-768x768.png 768w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-1536x1536.png 1536w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-80x80.png 80w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-380x380.png 380w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-550x550.png 550w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-800x800.png 800w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-1160x1160.png 1160w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response-146x146.png 146w, https://prodsens.live/wp-content/uploads/2025/11/40642-when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response.png 2000w" />								</div>
							</div>
						
						<div class="cs-entry__inner cs-entry__content">
							<div class="cs-entry__post-meta" ><div class="cs-meta-category"><ul class="post-categories">
	<li><a href="https://prodsens.live/category/product-management/" rel="category tag">Product Management</a></li></ul></div></div>
							<h2 class="cs-entry__title"><a href="https://prodsens.live/2025/11/06/when-ai-becomes-your-sre-how-incident-io-is-automating-incident-response/">When AI Becomes Your SRE: How Incident.io Is Automating Incident Response</a></h2>
							<div class="cs-entry__post-meta" ><div class="cs-meta-date">November 6, 2025</div></div>						</div>
					</div>
				</div>
			</div>
				</div>
			</div>

		
	</div>

	</div>

		
	
	
</div>


	<aside id="secondary" class="cs-widget-area cs-sidebar__area">
		<div class="cs-sidebar__inner">
						<div class="widget block-2 widget_block widget_search"><form role="search" method="get" action="https://prodsens.live/" class="wp-block-search__button-outside wp-block-search__text-button wp-block-search"    ><label class="wp-block-search__label" for="wp-block-search__input-1" >Search</label><div class="wp-block-search__inside-wrapper " ><input class="wp-block-search__input" id="wp-block-search__input-1" placeholder="Type your text" value="" type="search" name="s" required /><button aria-label="search" class="wp-block-search__button wp-element-button" type="submit" >search</button></div></form></div><div class="widget block-3 widget_block">
<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow">
<h2 class="wp-block-heading">Recent Posts</h2>


<ul class="wp-block-latest-posts__list wp-block-latest-posts"><li><a class="wp-block-latest-posts__post-title" href="https://prodsens.live/2025/11/11/understanding-controllers-in-express-js/">Understanding Controllers in Express.js</a></li>
<li><a class="wp-block-latest-posts__post-title" href="https://prodsens.live/2025/11/11/99217-growing-through-service-my-journey-as-an-asq-member-leader/">Growing Through Service: My Journey as an ASQ Member Leader</a></li>
<li><a class="wp-block-latest-posts__post-title" href="https://prodsens.live/2025/11/11/get-hasura-ready-in-1-hour-a-copy-pasteable-prod-framework-i-wish-i-had-on-hasura-day-1/">Get Hasura Ready in 1 hour — A copy-pasteable PROD Framework I wish I had on Hasura Day 1.</a></li>
<li><a class="wp-block-latest-posts__post-title" href="https://prodsens.live/2025/11/11/burying-carbon-how-ai-is-supercharging-subsurface-storage/">Burying Carbon: How AI is Supercharging Subsurface Storage</a></li>
<li><a class="wp-block-latest-posts__post-title" href="https://prodsens.live/2025/11/11/top-us-saas-and-ai-conferences-to-sponsor/">Top US SaaS and AI conferences to sponsor in 2026</a></li>
</ul></div></div>
</div><div class="widget block-4 widget_block">
<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow">
<h2 class="wp-block-heading">Recent Comments</h2>


<ol class="wp-block-latest-comments"><li class="wp-block-latest-comments__comment"><article><footer class="wp-block-latest-comments__comment-meta"><a class="wp-block-latest-comments__comment-author" href="https://www.villagetalkies.com">Village Talkies</a> on <a class="wp-block-latest-comments__comment-link" href="https://prodsens.live/2023/12/13/2024-creator-economy-predictions/#comment-12243">2024 Creator Economy Predictions</a></footer></article></li><li class="wp-block-latest-comments__comment"><article><footer class="wp-block-latest-comments__comment-meta"><a class="wp-block-latest-comments__comment-author" href="https://www.villagetalkies.com">Village Talkies</a> on <a class="wp-block-latest-comments__comment-link" href="https://prodsens.live/2023/05/31/the-ultimate-guide-to-hiring-a-pr-agency-in-2023/#comment-11411">The Ultimate Guide to Hiring a PR Agency in 2023</a></footer></article></li><li class="wp-block-latest-comments__comment"><article><footer class="wp-block-latest-comments__comment-meta"><a class="wp-block-latest-comments__comment-author" href="https://sebbie.pl/tag/javascript/">Rocco Liviero</a> on <a class="wp-block-latest-comments__comment-link" href="https://prodsens.live/2022/10/21/defender-for-devops-on-azuredevops/#comment-9035">Defender for DevOps on AzureDevOps</a></footer></article></li><li class="wp-block-latest-comments__comment"><article><footer class="wp-block-latest-comments__comment-meta"><a class="wp-block-latest-comments__comment-author" href="http://PK">اخبار روز اتومبیل</a> on <a class="wp-block-latest-comments__comment-link" href="https://prodsens.live/2023/01/11/b2b-marketing-trends/#comment-8998">B2B Marketing Trends That Will Grow Your Business in 2023</a></footer></article></li><li class="wp-block-latest-comments__comment"><article><footer class="wp-block-latest-comments__comment-meta"><a class="wp-block-latest-comments__comment-author" href="https://herotom.com">Adaline Morrow</a> on <a class="wp-block-latest-comments__comment-link" href="https://prodsens.live/2023/04/21/10-best-ai-content-creation-tools-in-2023/#comment-8979">10 Best AI Content Creation Tools in 2023</a></footer></article></li></ol></div></div>
</div><div class="widget block-12 widget_block">
<h2 class="wp-block-heading">Tags</h2>
</div><div class="widget block-8 widget_block widget_tag_cloud"><p class="wp-block-tag-cloud"><a href="https://prodsens.live/tag/ai/" class="tag-cloud-link tag-link-419 tag-link-position-1" style="font-size: 13.569060773481pt;" aria-label="ai (973 items)">ai</a>
<a href="https://prodsens.live/tag/articles/" class="tag-cloud-link tag-link-1061 tag-link-position-2" style="font-size: 10.707182320442pt;" aria-label="Articles (420 items)">Articles</a>
<a href="https://prodsens.live/tag/artificial-intelligence/" class="tag-cloud-link tag-link-775 tag-link-position-3" style="font-size: 9.0828729281768pt;" aria-label="Artificial Intelligence (254 items)">Artificial Intelligence</a>
<a href="https://prodsens.live/tag/aws/" class="tag-cloud-link tag-link-229 tag-link-position-4" style="font-size: 9.9337016574586pt;" aria-label="aws (327 items)">aws</a>
<a href="https://prodsens.live/tag/beginners/" class="tag-cloud-link tag-link-184 tag-link-position-5" style="font-size: 15.270718232044pt;" aria-label="beginners (1,634 items)">beginners</a>
<a href="https://prodsens.live/tag/career/" class="tag-cloud-link tag-link-190 tag-link-position-6" style="font-size: 9.7016574585635pt;" aria-label="career (307 items)">career</a>
<a href="https://prodsens.live/tag/cloud/" class="tag-cloud-link tag-link-308 tag-link-position-7" style="font-size: 8.3867403314917pt;" aria-label="cloud (210 items)">cloud</a>
<a href="https://prodsens.live/tag/company-news/" class="tag-cloud-link tag-link-573 tag-link-position-8" style="font-size: 9.1602209944751pt;" aria-label="Company News (263 items)">Company News</a>
<a href="https://prodsens.live/tag/content-marketing/" class="tag-cloud-link tag-link-267 tag-link-position-9" style="font-size: 10.475138121547pt;" aria-label="Content Marketing (389 items)">Content Marketing</a>
<a href="https://prodsens.live/tag/css/" class="tag-cloud-link tag-link-171 tag-link-position-10" style="font-size: 9.2375690607735pt;" aria-label="css (266 items)">css</a>
<a href="https://prodsens.live/tag/database/" class="tag-cloud-link tag-link-415 tag-link-position-11" style="font-size: 8.0773480662983pt;" aria-label="database (188 items)">database</a>
<a href="https://prodsens.live/tag/devops/" class="tag-cloud-link tag-link-195 tag-link-position-12" style="font-size: 10.939226519337pt;" aria-label="devops (448 items)">devops</a>
<a href="https://prodsens.live/tag/discuss/" class="tag-cloud-link tag-link-163 tag-link-position-13" style="font-size: 10.78453038674pt;" aria-label="discuss (429 items)">discuss</a>
<a href="https://prodsens.live/tag/expertise/" class="tag-cloud-link tag-link-1350 tag-link-position-14" style="font-size: 8.1546961325967pt;" aria-label="Expertise (193 items)">Expertise</a>
<a href="https://prodsens.live/tag/frontend/" class="tag-cloud-link tag-link-338 tag-link-position-15" style="font-size: 8.1546961325967pt;" aria-label="frontend (196 items)">frontend</a>
<a href="https://prodsens.live/tag/growth/" class="tag-cloud-link tag-link-118 tag-link-position-16" style="font-size: 8.0773480662983pt;" aria-label="growth (191 items)">growth</a>
<a href="https://prodsens.live/tag/guest-post/" class="tag-cloud-link tag-link-2939 tag-link-position-17" style="font-size: 8.6961325966851pt;" aria-label="Guest Post (229 items)">Guest Post</a>
<a href="https://prodsens.live/tag/java/" class="tag-cloud-link tag-link-283 tag-link-position-18" style="font-size: 8.232044198895pt;" aria-label="java (197 items)">java</a>
<a href="https://prodsens.live/tag/javascript/" class="tag-cloud-link tag-link-160 tag-link-position-19" style="font-size: 15.270718232044pt;" aria-label="javascript (1,619 items)">javascript</a>
<a href="https://prodsens.live/tag/learning/" class="tag-cloud-link tag-link-221 tag-link-position-20" style="font-size: 9.0055248618785pt;" aria-label="learning (251 items)">learning</a>
<a href="https://prodsens.live/tag/machinelearning/" class="tag-cloud-link tag-link-386 tag-link-position-21" style="font-size: 8pt;" aria-label="machinelearning (187 items)">machinelearning</a>
<a href="https://prodsens.live/tag/marketing/" class="tag-cloud-link tag-link-81 tag-link-position-22" style="font-size: 15.889502762431pt;" aria-label="Marketing (1,950 items)">Marketing</a>
<a href="https://prodsens.live/tag/marketing-strategy/" class="tag-cloud-link tag-link-214 tag-link-position-23" style="font-size: 8.0773480662983pt;" aria-label="Marketing Strategy (190 items)">Marketing Strategy</a>
<a href="https://prodsens.live/tag/node/" class="tag-cloud-link tag-link-238 tag-link-position-24" style="font-size: 8.7734806629834pt;" aria-label="node (232 items)">node</a>
<a href="https://prodsens.live/tag/opensource/" class="tag-cloud-link tag-link-161 tag-link-position-25" style="font-size: 11.403314917127pt;" aria-label="opensource (513 items)">opensource</a>
<a href="https://prodsens.live/tag/planing/" class="tag-cloud-link tag-link-2955 tag-link-position-26" style="font-size: 8.8508287292818pt;" aria-label="planing (241 items)">planing</a>
<a href="https://prodsens.live/tag/planning/" class="tag-cloud-link tag-link-130 tag-link-position-27" style="font-size: 8.4640883977901pt;" aria-label="Planning (211 items)">Planning</a>
<a href="https://prodsens.live/tag/podcast/" class="tag-cloud-link tag-link-104 tag-link-position-28" style="font-size: 8.5414364640884pt;" aria-label="podcast (218 items)">podcast</a>
<a href="https://prodsens.live/tag/podcasts/" class="tag-cloud-link tag-link-1063 tag-link-position-29" style="font-size: 9.9337016574586pt;" aria-label="Podcasts (327 items)">Podcasts</a>
<a href="https://prodsens.live/tag/prodsens-live/" class="tag-cloud-link tag-link-2926 tag-link-position-30" style="font-size: 22pt;" aria-label="prodsens live (12,010 items)">prodsens live</a>
<a href="https://prodsens.live/tag/productivity/" class="tag-cloud-link tag-link-157 tag-link-position-31" style="font-size: 11.944751381215pt;" aria-label="Productivity (607 items)">Productivity</a>
<a href="https://prodsens.live/tag/product-management/" class="tag-cloud-link tag-link-79 tag-link-position-32" style="font-size: 15.116022099448pt;" aria-label="Product Management (1,551 items)">Product Management</a>
<a href="https://prodsens.live/tag/product-managment/" class="tag-cloud-link tag-link-2930 tag-link-position-33" style="font-size: 12.254143646409pt;" aria-label="product managment (655 items)">product managment</a>
<a href="https://prodsens.live/tag/programming/" class="tag-cloud-link tag-link-162 tag-link-position-34" style="font-size: 15.812154696133pt;" aria-label="programming (1,897 items)">programming</a>
<a href="https://prodsens.live/tag/project-management/" class="tag-cloud-link tag-link-132 tag-link-position-35" style="font-size: 10.32044198895pt;" aria-label="Project Management (368 items)">Project Management</a>
<a href="https://prodsens.live/tag/projectmanager-blog/" class="tag-cloud-link tag-link-131 tag-link-position-36" style="font-size: 10.165745856354pt;" aria-label="ProjectManager Blog (354 items)">ProjectManager Blog</a>
<a href="https://prodsens.live/tag/python/" class="tag-cloud-link tag-link-259 tag-link-position-37" style="font-size: 11.016574585635pt;" aria-label="python (458 items)">python</a>
<a href="https://prodsens.live/tag/quality-management/" class="tag-cloud-link tag-link-1110 tag-link-position-38" style="font-size: 15.657458563536pt;" aria-label="Quality Management (1,801 items)">Quality Management</a>
<a href="https://prodsens.live/tag/react/" class="tag-cloud-link tag-link-158 tag-link-position-39" style="font-size: 11.790055248619pt;" aria-label="react (580 items)">react</a>
<a href="https://prodsens.live/tag/security/" class="tag-cloud-link tag-link-271 tag-link-position-40" style="font-size: 8.1546961325967pt;" aria-label="security (192 items)">security</a>
<a href="https://prodsens.live/tag/software/" class="tag-cloud-link tag-link-151 tag-link-position-41" style="font-size: 19.67955801105pt;" aria-label="Software (6,076 items)">Software</a>
<a href="https://prodsens.live/tag/tutorial/" class="tag-cloud-link tag-link-168 tag-link-position-42" style="font-size: 13.723756906077pt;" aria-label="tutorial (1,015 items)">tutorial</a>
<a href="https://prodsens.live/tag/typescript/" class="tag-cloud-link tag-link-239 tag-link-position-43" style="font-size: 9.2375690607735pt;" aria-label="typescript (271 items)">typescript</a>
<a href="https://prodsens.live/tag/uncategorized/" class="tag-cloud-link tag-link-1029 tag-link-position-44" style="font-size: 9.0055248618785pt;" aria-label="Uncategorized (251 items)">Uncategorized</a>
<a href="https://prodsens.live/tag/webdev/" class="tag-cloud-link tag-link-172 tag-link-position-45" style="font-size: 16.740331491713pt;" aria-label="webdev (2,491 items)">webdev</a></p></div><div class="widget block-14 widget_block widget_calendar"><div class="wp-block-calendar"><table id="wp-calendar" class="wp-calendar-table">
	<caption>November 2025</caption>
	<thead>
	<tr>
		<th scope="col" aria-label="Monday">M</th>
		<th scope="col" aria-label="Tuesday">T</th>
		<th scope="col" aria-label="Wednesday">W</th>
		<th scope="col" aria-label="Thursday">T</th>
		<th scope="col" aria-label="Friday">F</th>
		<th scope="col" aria-label="Saturday">S</th>
		<th scope="col" aria-label="Sunday">S</th>
	</tr>
	</thead>
	<tbody>
	<tr>
		<td colspan="5" class="pad"> </td><td><a href="https://prodsens.live/2025/11/01/" aria-label="Posts published on November 1, 2025">1</a></td><td><a href="https://prodsens.live/2025/11/02/" aria-label="Posts published on November 2, 2025">2</a></td>
	</tr>
	<tr>
		<td><a href="https://prodsens.live/2025/11/03/" aria-label="Posts published on November 3, 2025">3</a></td><td><a href="https://prodsens.live/2025/11/04/" aria-label="Posts published on November 4, 2025">4</a></td><td><a href="https://prodsens.live/2025/11/05/" aria-label="Posts published on November 5, 2025">5</a></td><td><a href="https://prodsens.live/2025/11/06/" aria-label="Posts published on November 6, 2025">6</a></td><td><a href="https://prodsens.live/2025/11/07/" aria-label="Posts published on November 7, 2025">7</a></td><td><a href="https://prodsens.live/2025/11/08/" aria-label="Posts published on November 8, 2025">8</a></td><td><a href="https://prodsens.live/2025/11/09/" aria-label="Posts published on November 9, 2025">9</a></td>
	</tr>
	<tr>
		<td><a href="https://prodsens.live/2025/11/10/" aria-label="Posts published on November 10, 2025">10</a></td><td id="today"><a href="https://prodsens.live/2025/11/11/" aria-label="Posts published on November 11, 2025">11</a></td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td>
	</tr>
	<tr>
		<td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td>
	</tr>
	<tr>
		<td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td>
	</tr>
	</tbody>
	</table><nav aria-label="Previous and next months" class="wp-calendar-nav">
		<span class="wp-calendar-nav-prev"><a href="https://prodsens.live/2025/10/">« Oct</a></span>
		<span class="pad"> </span>
		<span class="wp-calendar-nav-next"> </span>
	</nav></div></div>					</div>
	</aside>
	
							
						</div>

								<div class="cs-entry__post-related">
			<h5 class="cs-section-heading cnvs-block-section-heading is-style-cnvs-block-section-heading-default halignleft  "><span class="cnvs-section-title"><span><span class="cs-section-subheadings">Related Posts</span></span></span></h5>
			<div class="cs-entry__post-wrap">
				

	<article class="cs-entry-default post-31543 post type-post status-publish format-standard has-post-thumbnail category-software tag-prodsens-live tag-python tag-pytorch tag-randomresizedcrop tag-software tag-v2 cs-entry cs-video-wrap">
		<div class="cs-entry__outer">
							<div class="cs-entry__inner cs-entry__thumbnail cs-entry__overlay cs-overlay-ratio cs-ratio-original" data-scheme="inverse">

											<div class="cs-overlay-background">
							<img width="380" height="250" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXwAAAD6AQMAAACYt274AAAAA1BMVEUAAP+KeNJXAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAACNJREFUaN7twTEBAAAAwqD1T20MH6AAAAAAAAAAAAAAAICfAS/aAAH7Vn1zAAAAAElFTkSuQmCC" class="attachment-csco-thumbnail size-csco-thumbnail pk-lazyload wp-post-image" alt="randomresizedcrop-in-pytorch-(1)" decoding="async" loading="lazy" data-pk-sizes="auto" data-ls-sizes="auto, (max-width: 380px) 100vw, 380px" data-pk-src="https://prodsens.live/wp-content/uploads/2025/02/31543-randomresizedcrop-in-pytorch-1-380x250.png" data-pk-srcset="https://prodsens.live/wp-content/uploads/2025/02/31543-randomresizedcrop-in-pytorch-1-380x250.png 380w, https://prodsens.live/wp-content/uploads/2025/02/31543-randomresizedcrop-in-pytorch-1-230x150.png 230w, https://prodsens.live/wp-content/uploads/2025/02/31543-randomresizedcrop-in-pytorch-1-260x170.png 260w" />						</div>
					
					
					
											<div class="cs-overlay-content">
							<div class="cs-entry__post-meta" ><div class="cs-meta-comments"><span class="cs-meta-icon"><i class="cs-icon cs-icon-message-square"></i></span><a href="https://prodsens.live/2025/02/09/randomresizedcrop-in-pytorch-1/#respond" class="comments-link" >0</a></div><div class="cs-meta-reading-time"><span class="cs-meta-icon"><i class="cs-icon cs-icon-clock"></i></span>1 min</div></div>						</div>
					
					<a href="https://prodsens.live/2025/02/09/randomresizedcrop-in-pytorch-1/" class="cs-overlay-link"></a>
				</div>
			
			<div class="cs-entry__inner cs-entry__content">

				<div class="cs-entry__post-meta" ><div class="cs-meta-category"><ul class="post-categories">
	<li><a href="https://prodsens.live/category/software/" rel="category tag">Software</a></li></ul></div></div>
				<h2 class="cs-entry__title"><a href="https://prodsens.live/2025/02/09/randomresizedcrop-in-pytorch-1/">RandomResizedCrop in PyTorch (1)</a></h2>
									<div class="cs-entry__excerpt">
						Buy Me a Coffee☕ *Memos: My post explains OxfordIIITPet(). RandomResizedCrop() can crop a random part of an image,…					</div>
				
							<div class="cs-entry__details ">
									<div class="cs-entry__details-data">
																<a class="cs-author-avatar" href="https://prodsens.live/author/andy-beohar/"><img alt='' src='https://secure.gravatar.com/avatar/73d36ee0df8c698888a2c3d5faec7fb6b1a0e6846ca5b2f56b86ba23b2fab791?s=40&d=mm&r=g' srcset='https://secure.gravatar.com/avatar/73d36ee0df8c698888a2c3d5faec7fb6b1a0e6846ca5b2f56b86ba23b2fab791?s=80&d=mm&r=g 2x' class='avatar avatar-40 photo' height='40' width='40' loading='lazy' decoding='async'/></a>
															<div class="cs-entry__details-meta">
							<div class="cs-entry__author-meta"><a href="https://prodsens.live/author/andy-beohar/">Andy Beohar</a></div><div class="cs-entry__post-meta" ><div class="cs-meta-date">February 9, 2025</div></div>						</div>
					</div>
				
									<div class="cs-entry__read-more">
						<a href="https://prodsens.live/2025/02/09/randomresizedcrop-in-pytorch-1/">
							Read More						</a>
					</div>
				
							</div>
						</div>
		</div>
	</article>



	<article class="cs-entry-default post-39224 post type-post status-publish format-standard has-post-thumbnail category-software tag-beginners tag-learning tag-prodsens-live tag-productivity tag-programming tag-software cs-entry cs-video-wrap">
		<div class="cs-entry__outer">
							<div class="cs-entry__inner cs-entry__thumbnail cs-entry__overlay cs-overlay-ratio cs-ratio-original" data-scheme="inverse">

											<div class="cs-overlay-background">
							<img width="380" height="250" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXwAAAD6AQMAAACYt274AAAAA1BMVEUAAP+KeNJXAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAACNJREFUaN7twTEBAAAAwqD1T20MH6AAAAAAAAAAAAAAAICfAS/aAAH7Vn1zAAAAAElFTkSuQmCC" class="attachment-csco-thumbnail size-csco-thumbnail pk-lazyload wp-post-image" alt="learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-in-the-ai-age" decoding="async" loading="lazy" data-pk-sizes="auto" data-ls-sizes="auto, (max-width: 380px) 100vw, 380px" data-pk-src="https://prodsens.live/wp-content/uploads/2025/09/39224-learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-68d24a4b25b99-380x250.png" data-pk-srcset="https://prodsens.live/wp-content/uploads/2025/09/39224-learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-68d24a4b25b99-380x250.png 380w, https://prodsens.live/wp-content/uploads/2025/09/39224-learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-68d24a4b25b99-230x150.png 230w, https://prodsens.live/wp-content/uploads/2025/09/39224-learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-68d24a4b25b99-260x170.png 260w" />						</div>
					
					
					
											<div class="cs-overlay-content">
							<div class="cs-entry__post-meta" ><div class="cs-meta-comments"><span class="cs-meta-icon"><i class="cs-icon cs-icon-message-square"></i></span><a href="https://prodsens.live/2025/09/23/learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-in-the-ai-age/#respond" class="comments-link" >0</a></div><div class="cs-meta-reading-time"><span class="cs-meta-icon"><i class="cs-icon cs-icon-clock"></i></span>3 min</div></div>						</div>
					
					<a href="https://prodsens.live/2025/09/23/learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-in-the-ai-age/" class="cs-overlay-link"></a>
				</div>
			
			<div class="cs-entry__inner cs-entry__content">

				<div class="cs-entry__post-meta" ><div class="cs-meta-category"><ul class="post-categories">
	<li><a href="https://prodsens.live/category/software/" rel="category tag">Software</a></li></ul></div></div>
				<h2 class="cs-entry__title"><a href="https://prodsens.live/2025/09/23/learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-in-the-ai-age/">Learning Principles to Help You Learn Faster and Smarter if You Want to Become a Dev in the AI Age</a></h2>
									<div class="cs-entry__excerpt">
						We’re living in the AI age. Information is growing at exponential speed, and the ability to learn faster,…					</div>
				
							<div class="cs-entry__details ">
									<div class="cs-entry__details-data">
																<a class="cs-author-avatar" href="https://prodsens.live/author/ann-anica-christian/"><img alt='' src='https://secure.gravatar.com/avatar/aafa06f7fdd3c005378d4c0869a4a153332142f8002d08b0fba9d99a6dd46597?s=40&d=mm&r=g' srcset='https://secure.gravatar.com/avatar/aafa06f7fdd3c005378d4c0869a4a153332142f8002d08b0fba9d99a6dd46597?s=80&d=mm&r=g 2x' class='avatar avatar-40 photo' height='40' width='40' loading='lazy' decoding='async'/></a>
															<div class="cs-entry__details-meta">
							<div class="cs-entry__author-meta"><a href="https://prodsens.live/author/ann-anica-christian/">Ann-Anica Christian</a></div><div class="cs-entry__post-meta" ><div class="cs-meta-date">September 23, 2025</div></div>						</div>
					</div>
				
									<div class="cs-entry__read-more">
						<a href="https://prodsens.live/2025/09/23/learning-principles-to-help-you-learn-faster-and-smarter-if-you-want-to-become-a-dev-in-the-ai-age/">
							Read More						</a>
					</div>
				
							</div>
						</div>
		</div>
	</article>



	<article class="cs-entry-default post-33112 post type-post status-publish format-standard has-post-thumbnail category-software tag-prodsens-live tag-software tag-softwaredevelopment tag-softwareengineering tag-webdev cs-entry cs-video-wrap">
		<div class="cs-entry__outer">
							<div class="cs-entry__inner cs-entry__thumbnail cs-entry__overlay cs-overlay-ratio cs-ratio-original" data-scheme="inverse">

											<div class="cs-overlay-background">
							<img width="380" height="250" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAXwAAAD6AQMAAACYt274AAAAA1BMVEUAAP+KeNJXAAAAAXRSTlMAQObYZgAAAAlwSFlzAAAOxAAADsQBlSsOGwAAACNJREFUaN7twTEBAAAAwqD1T20MH6AAAAAAAAAAAAAAAICfAS/aAAH7Vn1zAAAAAElFTkSuQmCC" class="attachment-csco-thumbnail size-csco-thumbnail pk-lazyload wp-post-image" alt="think-before-you-code:-the-art-of-understanding-problems" decoding="async" loading="lazy" data-pk-sizes="auto" data-ls-sizes="auto, (max-width: 380px) 100vw, 380px" data-pk-src="https://prodsens.live/wp-content/uploads/2025/03/33112-think-before-you-code-the-art-of-understanding-problems-380x250.png" data-pk-srcset="https://prodsens.live/wp-content/uploads/2025/03/33112-think-before-you-code-the-art-of-understanding-problems-380x250.png 380w, https://prodsens.live/wp-content/uploads/2025/03/33112-think-before-you-code-the-art-of-understanding-problems-230x150.png 230w, https://prodsens.live/wp-content/uploads/2025/03/33112-think-before-you-code-the-art-of-understanding-problems-260x170.png 260w" />						</div>
					
					
					
											<div class="cs-overlay-content">
							<div class="cs-entry__post-meta" ><div class="cs-meta-comments"><span class="cs-meta-icon"><i class="cs-icon cs-icon-message-square"></i></span><a href="https://prodsens.live/2025/03/27/think-before-you-code-the-art-of-understanding-problems/#respond" class="comments-link" >0</a></div><div class="cs-meta-reading-time"><span class="cs-meta-icon"><i class="cs-icon cs-icon-clock"></i></span>3 min</div></div>						</div>
					
					<a href="https://prodsens.live/2025/03/27/think-before-you-code-the-art-of-understanding-problems/" class="cs-overlay-link"></a>
				</div>
			
			<div class="cs-entry__inner cs-entry__content">

				<div class="cs-entry__post-meta" ><div class="cs-meta-category"><ul class="post-categories">
	<li><a href="https://prodsens.live/category/software/" rel="category tag">Software</a></li></ul></div></div>
				<h2 class="cs-entry__title"><a href="https://prodsens.live/2025/03/27/think-before-you-code-the-art-of-understanding-problems/">Think Before You Code: The Art of Understanding Problems</a></h2>
									<div class="cs-entry__excerpt">
						🚧 Warning: hard truths ahead. Today we’re addressing the uncomfortable reality that thinking before coding is becoming a…					</div>
				
							<div class="cs-entry__details ">
									<div class="cs-entry__details-data">
																<a class="cs-author-avatar" href="https://prodsens.live/author/leadingagile/"><img alt='' src='https://secure.gravatar.com/avatar/63a2f1c70649381035ff84375c222790b91eb5908bfb34eae1a3de44b3464d8b?s=40&d=mm&r=g' srcset='https://secure.gravatar.com/avatar/63a2f1c70649381035ff84375c222790b91eb5908bfb34eae1a3de44b3464d8b?s=80&d=mm&r=g 2x' class='avatar avatar-40 photo' height='40' width='40' loading='lazy' decoding='async'/></a>
															<div class="cs-entry__details-meta">
							<div class="cs-entry__author-meta"><a href="https://prodsens.live/author/leadingagile/">LeadingAgile</a></div><div class="cs-entry__post-meta" ><div class="cs-meta-date">March 27, 2025</div></div>						</div>
					</div>
				
									<div class="cs-entry__read-more">
						<a href="https://prodsens.live/2025/03/27/think-before-you-code-the-art-of-understanding-problems/">
							Read More						</a>
					</div>
				
							</div>
						</div>
		</div>
	</article>

			</div>
		</div>
	
	
	
					</div>

					
				</div>

				
			</main>

		
		
<footer data-rocket-location-hash="2c6cbd98bb2d03e13a6ca52edce2d693" class="cs-footer cs-footer-two" data-scheme="dark">
	<div class="cs-container">
		<div class="cs-footer__item">
			<div class="cs-footer__col  cs-col-left">
				<div class="cs-footer__inner">
							<div class="cs-logo">
			<a class="cs-footer__logo cs-logo-once" href="https://prodsens.live/">
				prodSens.live			</a>

					</div>
		
								<div class="cs-footer__desc">
				Designed & Developed by <a href="https://prodsens.live/">Xezero.com</a>			</div>
							</div>
			</div>
			<div class="cs-footer__col cs-col-center">
							<div class="footer-nav-menu">
				<ul id="menu-primary" class="cs-footer__nav cs-nav-grid"><li id="menu-item-198" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-privacy-policy menu-item-198"><a rel="privacy-policy" href="https://prodsens.live/privacy-policy-2/">Privacy Policy</a></li>
<li id="menu-item-1494" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1494"><a href="https://prodsens.live/terms-conditions/">Terms & Conditions</a></li>
<li id="menu-item-1493" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-1493"><a href="https://prodsens.live/contact-us/">Contact us</a></li>
</ul>			</div>
						</div>
			<div class="cs-footer__col cs-col-right">
				<div class="cs-footer-social-links">
							<div class="cs-footer-social-links">
				<div class="pk-social-links-wrap  pk-social-links-template-nav pk-social-links-align-default pk-social-links-scheme-bold pk-social-links-titles-disabled pk-social-links-counts-disabled pk-social-links-labels-disabled">
		<div class="pk-social-links-items">
								<div class="pk-social-links-item pk-social-links-facebook  pk-social-links-no-count" data-id="facebook">
						<a href="https://facebook.com/prodsenslive" class="pk-social-links-link" target="_blank" rel="nofollow noopener" aria-label="Facebook">
							<i class="pk-social-links-icon pk-icon pk-icon-facebook"></i>
							
							
							
													</a>
					</div>
										<div class="pk-social-links-item pk-social-links-linkedin  pk-social-links-no-count" data-id="linkedin">
						<a href="https://www.linkedin.com/company/prodsens-live" class="pk-social-links-link" target="_blank" rel="nofollow noopener" aria-label="LinkedIn">
							<i class="pk-social-links-icon pk-icon pk-icon-linkedin"></i>
							
							
							
													</a>
					</div>
										<div class="pk-social-links-item pk-social-links-twitter  pk-social-links-no-count" data-id="twitter">
						<a href="https://twitter.com/prodsens_pro" class="pk-social-links-link" target="_blank" rel="nofollow noopener" aria-label="Twitter">
							<i class="pk-social-links-icon pk-icon pk-icon-twitter"></i>
							
							
							
													</a>
					</div>
							</div>
	</div>
			</div>
						</div>
			</div>
		</div>
	</div>
</footer>

		
	</div>

	
</div>


<script type="speculationrules">
{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"\/*"},{"not":{"href_matches":["\/wp-*.php","\/wp-admin\/*","\/wp-content\/uploads\/*","\/wp-content\/*","\/wp-content\/plugins\/*","\/wp-content\/themes\/networker\/*","\/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]}
</script>

<style type="text/css" media="all" id="canvas-widget-blocks-dynamic-styles">

</style>
<!--googleoff: all--><div id="cookie-law-info-bar" data-nosnippet="true"><span><div class="cli-bar-container cli-style-v2"><div class="cli-bar-message">We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.</div><div class="cli-bar-btn_container"><a role='button' class="medium cli-plugin-button cli-plugin-main-button cli_settings_button" style="margin:0px 5px 0px 0px">Cookie Settings</a><a id="wt-cli-accept-all-btn" role='button' data-cli_action="accept_all" class="wt-cli-element medium cli-plugin-button wt-cli-accept-all-btn cookie_action_close_header cli_action_button">Accept All</a></div></div></span></div><div id="cookie-law-info-again" data-nosnippet="true"><span id="cookie_hdr_showagain">Manage consent</span></div><div class="cli-modal" data-nosnippet="true" id="cliSettingsPopup" tabindex="-1" role="dialog" aria-labelledby="cliSettingsPopup" aria-hidden="true">
  <div class="cli-modal-dialog" role="document">
	<div class="cli-modal-content cli-bar-popup">
		  <button type="button" class="cli-modal-close" id="cliModalClose">
			<svg class="" viewBox="0 0 24 24"><path d="M19 6.41l-1.41-1.41-5.59 5.59-5.59-5.59-1.41 1.41 5.59 5.59-5.59 5.59 1.41 1.41 5.59-5.59 5.59 5.59 1.41-1.41-5.59-5.59z"></path><path d="M0 0h24v24h-24z" fill="none"></path></svg>
			<span class="wt-cli-sr-only">Close</span>
		  </button>
		  <div class="cli-modal-body">
			<div class="cli-container-fluid cli-tab-container">
	<div class="cli-row">
		<div class="cli-col-12 cli-align-items-stretch cli-px-0">
			<div class="cli-privacy-overview">
				<h4>Privacy Overview</h4>				<div class="cli-privacy-content">
					<div class="cli-privacy-content-text">This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.</div>
				</div>
				<a class="cli-privacy-readmore" aria-label="Show more" role="button" data-readmore-text="Show more" data-readless-text="Show less"></a>			</div>
		</div>
		<div class="cli-col-12 cli-align-items-stretch cli-px-0 cli-tab-section-container">
												<div class="cli-tab-section">
						<div class="cli-tab-header">
							<a role="button" tabindex="0" class="cli-nav-link cli-settings-mobile" data-target="necessary" data-toggle="cli-toggle-tab">
								Necessary							</a>
															<div class="wt-cli-necessary-checkbox">
									<input type="checkbox" class="cli-user-preference-checkbox"  id="wt-cli-checkbox-necessary" data-id="checkbox-necessary" checked="checked"  />
									<label class="form-check-label" for="wt-cli-checkbox-necessary">Necessary</label>
								</div>
								<span class="cli-necessary-caption">Always Enabled</span>
													</div>
						<div class="cli-tab-content">
							<div class="cli-tab-pane cli-fade" data-id="necessary">
								<div class="wt-cli-cookie-description">
									Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
<table class="cookielawinfo-row-cat-table cookielawinfo-winter"><thead><tr><th class="cookielawinfo-column-1">Cookie</th><th class="cookielawinfo-column-3">Duration</th><th class="cookielawinfo-column-4">Description</th></tr></thead><tbody><tr class="cookielawinfo-row"><td class="cookielawinfo-column-1">cookielawinfo-checkbox-analytics</td><td class="cookielawinfo-column-3">11 months</td><td class="cookielawinfo-column-4">This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".</td></tr><tr class="cookielawinfo-row"><td class="cookielawinfo-column-1">cookielawinfo-checkbox-functional</td><td class="cookielawinfo-column-3">11 months</td><td class="cookielawinfo-column-4">The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".</td></tr><tr class="cookielawinfo-row"><td class="cookielawinfo-column-1">cookielawinfo-checkbox-necessary</td><td class="cookielawinfo-column-3">11 months</td><td class="cookielawinfo-column-4">This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".</td></tr><tr class="cookielawinfo-row"><td class="cookielawinfo-column-1">cookielawinfo-checkbox-others</td><td class="cookielawinfo-column-3">11 months</td><td class="cookielawinfo-column-4">This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.</td></tr><tr class="cookielawinfo-row"><td class="cookielawinfo-column-1">cookielawinfo-checkbox-performance</td><td class="cookielawinfo-column-3">11 months</td><td class="cookielawinfo-column-4">This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".</td></tr><tr class="cookielawinfo-row"><td class="cookielawinfo-column-1">viewed_cookie_policy</td><td class="cookielawinfo-column-3">11 months</td><td class="cookielawinfo-column-4">The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.</td></tr></tbody></table>								</div>
							</div>
						</div>
					</div>
																	<div class="cli-tab-section">
						<div class="cli-tab-header">
							<a role="button" tabindex="0" class="cli-nav-link cli-settings-mobile" data-target="functional" data-toggle="cli-toggle-tab">
								Functional							</a>
															<div class="cli-switch">
									<input type="checkbox" id="wt-cli-checkbox-functional" class="cli-user-preference-checkbox"  data-id="checkbox-functional" />
									<label for="wt-cli-checkbox-functional" class="cli-slider" data-cli-enable="Enabled" data-cli-disable="Disabled"><span class="wt-cli-sr-only">Functional</span></label>
								</div>
													</div>
						<div class="cli-tab-content">
							<div class="cli-tab-pane cli-fade" data-id="functional">
								<div class="wt-cli-cookie-description">
									Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
								</div>
							</div>
						</div>
					</div>
																	<div class="cli-tab-section">
						<div class="cli-tab-header">
							<a role="button" tabindex="0" class="cli-nav-link cli-settings-mobile" data-target="performance" data-toggle="cli-toggle-tab">
								Performance							</a>
															<div class="cli-switch">
									<input type="checkbox" id="wt-cli-checkbox-performance" class="cli-user-preference-checkbox"  data-id="checkbox-performance" />
									<label for="wt-cli-checkbox-performance" class="cli-slider" data-cli-enable="Enabled" data-cli-disable="Disabled"><span class="wt-cli-sr-only">Performance</span></label>
								</div>
													</div>
						<div class="cli-tab-content">
							<div class="cli-tab-pane cli-fade" data-id="performance">
								<div class="wt-cli-cookie-description">
									Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
								</div>
							</div>
						</div>
					</div>
																	<div class="cli-tab-section">
						<div class="cli-tab-header">
							<a role="button" tabindex="0" class="cli-nav-link cli-settings-mobile" data-target="analytics" data-toggle="cli-toggle-tab">
								Analytics							</a>
															<div class="cli-switch">
									<input type="checkbox" id="wt-cli-checkbox-analytics" class="cli-user-preference-checkbox"  data-id="checkbox-analytics" />
									<label for="wt-cli-checkbox-analytics" class="cli-slider" data-cli-enable="Enabled" data-cli-disable="Disabled"><span class="wt-cli-sr-only">Analytics</span></label>
								</div>
													</div>
						<div class="cli-tab-content">
							<div class="cli-tab-pane cli-fade" data-id="analytics">
								<div class="wt-cli-cookie-description">
									Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
								</div>
							</div>
						</div>
					</div>
																	<div class="cli-tab-section">
						<div class="cli-tab-header">
							<a role="button" tabindex="0" class="cli-nav-link cli-settings-mobile" data-target="advertisement" data-toggle="cli-toggle-tab">
								Advertisement							</a>
															<div class="cli-switch">
									<input type="checkbox" id="wt-cli-checkbox-advertisement" class="cli-user-preference-checkbox"  data-id="checkbox-advertisement" />
									<label for="wt-cli-checkbox-advertisement" class="cli-slider" data-cli-enable="Enabled" data-cli-disable="Disabled"><span class="wt-cli-sr-only">Advertisement</span></label>
								</div>
													</div>
						<div class="cli-tab-content">
							<div class="cli-tab-pane cli-fade" data-id="advertisement">
								<div class="wt-cli-cookie-description">
									Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
								</div>
							</div>
						</div>
					</div>
																	<div class="cli-tab-section">
						<div class="cli-tab-header">
							<a role="button" tabindex="0" class="cli-nav-link cli-settings-mobile" data-target="others" data-toggle="cli-toggle-tab">
								Others							</a>
															<div class="cli-switch">
									<input type="checkbox" id="wt-cli-checkbox-others" class="cli-user-preference-checkbox"  data-id="checkbox-others" />
									<label for="wt-cli-checkbox-others" class="cli-slider" data-cli-enable="Enabled" data-cli-disable="Disabled"><span class="wt-cli-sr-only">Others</span></label>
								</div>
													</div>
						<div class="cli-tab-content">
							<div class="cli-tab-pane cli-fade" data-id="others">
								<div class="wt-cli-cookie-description">
									Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
								</div>
							</div>
						</div>
					</div>
										</div>
	</div>
</div>
		  </div>
		  <div class="cli-modal-footer">
			<div class="wt-cli-element cli-container-fluid cli-tab-container">
				<div class="cli-row">
					<div class="cli-col-12 cli-align-items-stretch cli-px-0">
						<div class="cli-tab-footer wt-cli-privacy-overview-actions">
						
															<a id="wt-cli-privacy-save-btn" role="button" tabindex="0" data-cli-action="accept" class="wt-cli-privacy-btn cli_setting_save_button wt-cli-privacy-accept-btn cli-btn">SAVE & ACCEPT</a>
													</div>
						
					</div>
				</div>
			</div>
		</div>
	</div>
  </div>
</div>
<div data-rocket-location-hash="79966eb1feae7497261e26fa757919a1" class="cli-modal-backdrop cli-fade cli-settings-overlay"></div>
<div data-rocket-location-hash="8202117e2b044c4659c14be121bd0f3c" class="cli-modal-backdrop cli-fade cli-popupbar-overlay"></div>
<!--googleon: all-->			<a href="#top" class="pk-scroll-to-top">
				<i class="pk-icon pk-icon-up"></i>
			</a>
					<div data-rocket-location-hash="e27696f9f27f9766352e7eca75bf6b23" class="pk-mobile-share-overlay">
							</div>
					<div data-rocket-location-hash="0d86fd5afa4658b1f9669ebe1d4f91f1" id="fb-root"></div>
		<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v17.0&appId=&autoLogAppEvents=1" nonce="Ci8te34e"></script>
	        <script>
            var _SEARCHWP_LIVE_AJAX_SEARCH_BLOCKS = true;
            var _SEARCHWP_LIVE_AJAX_SEARCH_ENGINE = 'default';
            var _SEARCHWP_LIVE_AJAX_SEARCH_CONFIG = 'default';
        </script>
        <link rel='stylesheet' id='cookie-law-info-table-css' href='https://prodsens.live/wp-content/plugins/cookie-law-info/legacy/public/css/cookie-law-info-table.css?ver=3.3.6' media='all' />
<script src="https://prodsens.live/wp-content/plugins/canvas/components/basic-elements/block-alert/public-block-alert.js?ver=2.5.1" id="canvas-block-alert-script-js"></script>
<script src="https://prodsens.live/wp-content/plugins/canvas/components/basic-elements/block-collapsibles/public-block-collapsibles.js?ver=2.5.1" id="canvas-block-collapsibles-script-js"></script>
<script src="https://prodsens.live/wp-content/plugins/canvas/components/basic-elements/block-tabs/public-block-tabs.js?ver=2.5.1" id="canvas-block-tabs-script-js"></script>
<script src="https://prodsens.live/wp-content/plugins/canvas/components/justified-gallery/block/jquery.justifiedGallery.min.js?ver=2.5.1" id="justifiedgallery-js"></script>
<script id="canvas-justified-gallery-js-extra">
var canvasJG = {"rtl":""};
</script>
<script src="https://prodsens.live/wp-content/plugins/canvas/components/justified-gallery/block/public-block-justified-gallery.js?ver=2.5.1" id="canvas-justified-gallery-js"></script>
<script src="https://prodsens.live/wp-includes/js/imagesloaded.min.js?ver=5.0.0" id="imagesloaded-js"></script>
<script src="https://prodsens.live/wp-content/plugins/canvas/components/slider-gallery/block/flickity.pkgd.min.js?ver=2.5.1" id="flickity-js"></script>
<script id="canvas-slider-gallery-js-extra">
var canvas_sg_flickity = {"page_info_sep":" of "};
</script>
<script src="https://prodsens.live/wp-content/plugins/canvas/components/slider-gallery/block/public-block-slider-gallery.js?ver=2.5.1" id="canvas-slider-gallery-js"></script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/basic-elements/public/js/public-powerkit-basic-elements.js?ver=4.0.0" id="powerkit-basic-elements-js"></script>
<script id="powerkit-justified-gallery-js-extra">
var powerkitJG = {"rtl":""};
</script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/justified-gallery/public/js/public-powerkit-justified-gallery.js?ver=3.0.2" id="powerkit-justified-gallery-js"></script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/lazyload/public/js/lazysizes.config.js?ver=6.8.3" id="lazysizes.config-js"></script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/lazyload/public/js/lazysizes.min.js?ver=6.8.3" id="lazysizes-js"></script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/lightbox/public/js/glightbox.min.js?ver=3.0.2" id="glightbox-js"></script>
<script id="powerkit-lightbox-js-extra">
var powerkit_lightbox_localize = {"text_previous":"Previous","text_next":"Next","text_close":"Close","text_loading":"Loading","text_counter":"of","single_image_selectors":".entry-content img","gallery_selectors":".wp-block-gallery,.gallery","exclude_selectors":"","zoom_icon":"1"};
</script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/lightbox/public/js/public-powerkit-lightbox.js?ver=3.0.2" id="powerkit-lightbox-js"></script>
<script id="powerkit-opt-in-forms-js-extra">
var opt_in = {"ajax_url":"https:\/\/prodsens.live\/wp-admin\/admin-ajax.php","warning_privacy":"Please confirm that you agree with our policies.","is_admin":"","server_error":"Server error occurred. Please try again later."};
</script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/opt-in-forms/public/js/public-powerkit-opt-in-forms.js?ver=3.0.2" id="powerkit-opt-in-forms-js"></script>
<script async="async" defer="defer" src="//assets.pinterest.com/js/pinit.js?ver=6.8.3" id="powerkit-pinterest-js"></script>
<script id="powerkit-pin-it-js-extra">
var powerkit_pinit_localize = {"image_selectors":".entry-content img","exclude_selectors":".cnvs-block-row,.cnvs-block-section,.cnvs-block-posts .entry-thumbnail,.cnvs-post-thumbnail,.pk-block-author,.pk-featured-categories img,.pk-inline-posts-container img,.pk-instagram-image,.pk-subscribe-image,.wp-block-cover,.pk-block-posts,.cs-posts-area__main,.cs-entry","only_hover":"1"};
</script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/pinterest/public/js/public-powerkit-pin-it.js?ver=3.0.2" id="powerkit-pin-it-js"></script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/scroll-to-top/public/js/public-powerkit-scroll-to-top.js?ver=3.0.2" id="powerkit-scroll-to-top-js"></script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/share-buttons/public/js/public-powerkit-share-buttons.js?ver=3.0.2" id="powerkit-share-buttons-js"></script>
<script id="powerkit-slider-gallery-js-extra">
var powerkit_sg_flickity = {"page_info_sep":" of "};
</script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/slider-gallery/public/js/public-powerkit-slider-gallery.js?ver=3.0.2" id="powerkit-slider-gallery-js"></script>
<script id="powerkit-table-of-contents-js-extra">
var powerkit_toc_config = {"label_show":"Show","label_hide":"Hide"};
</script>
<script src="https://prodsens.live/wp-content/plugins/powerkit/modules/table-of-contents/public/js/public-powerkit-table-of-contents.js?ver=3.0.2" id="powerkit-table-of-contents-js"></script>
<script id="csco-scripts-js-extra">
var csLocalize = {"siteSchemeMode":"system","siteSchemeToogle":"1"};
var csco_mega_menu = {"rest_url":"https:\/\/prodsens.live\/wp-json\/csco\/v1\/menu-posts"};
</script>
<script src="https://prodsens.live/wp-content/themes/networker/assets/js/scripts.js?ver=1.0.7" id="csco-scripts-js"></script>
<script src="https://prodsens.live/wp-includes/js/comment-reply.min.js?ver=6.8.3" id="comment-reply-js" async data-wp-strategy="async"></script>
<script id="swp-live-search-client-js-extra">
var searchwp_live_search_params = [];
searchwp_live_search_params = {"ajaxurl":"https:\/\/prodsens.live\/wp-admin\/admin-ajax.php","origin_id":40640,"config":{"default":{"engine":"default","input":{"delay":300,"min_chars":3},"results":{"position":"bottom","width":"auto","offset":{"x":0,"y":5}},"spinner":{"lines":12,"length":8,"width":3,"radius":8,"scale":1,"corners":1,"color":"#424242","fadeColor":"transparent","speed":1,"rotate":0,"animation":"searchwp-spinner-line-fade-quick","direction":1,"zIndex":2000000000,"className":"spinner","top":"50%","left":"50%","shadow":"0 0 1px transparent","position":"absolute"}}},"msg_no_config_found":"No valid SearchWP Live Search configuration found!","aria_instructions":"When autocomplete results are available use up and down arrows to review and enter to go to the desired page. Touch device users, explore by touch or with swipe gestures."};;
</script>
<script src="https://prodsens.live/wp-content/plugins/searchwp-live-ajax-search/assets/javascript/dist/script.min.js?ver=1.8.6" id="swp-live-search-client-js"></script>
	<script type="text/javascript">
		"use strict";

		(function($) {

			$( window ).on( 'load', function() {

				// Get all links.
				var powerkitSLinksIds = [];

				var powerkitSLinksRestBox = $( '.pk-social-links-mode-rest' );

				// Generate links Ids.
				$( powerkitSLinksRestBox ).each( function( index, wrap ) {

					if ( ! $( wrap ).hasClass( 'pk-social-links-counts-disabled' ) ) {

						$( wrap ).find( '.pk-social-links-item' ).each( function() {
							if ( $( this ).attr( 'data-id' ).length > 0 ) {
								powerkitSLinksIds.push( $( this ).attr( 'data-id' ) );
							}
						});
					}
				});

				// Generate links data.
				var powerkitSLinksData = {};

				if( powerkitSLinksIds.length > 0 ) {
					powerkitSLinksData = { 'ids' : powerkitSLinksIds.join() };
				}

				// Check data.
				if ( ! Object.entries( powerkitSLinksData ).length ) {
					return;
				}

				// Get results by REST API.
				$.ajax({
					type: 'GET',
					url: 'https://prodsens.live/wp-json/social-counts/v1/get-counts',
					data: powerkitSLinksData,
					beforeSend: function(){

						// Add Loading Class.
						powerkitSLinksRestBox.addClass( 'pk-social-links-loading' );
					},
					success: function( response ) {

						if ( ! $.isEmptyObject( response ) && ! response.hasOwnProperty( 'code' ) ) {

							// SLinks loop.
							$.each( response, function( index, data ) {

								// Find Bsa Item.
								var powerkitSLinksItem = powerkitSLinksRestBox.find( '.pk-social-links-item[data-id="' + index + '"]');

								// Set Class.
								if ( data.hasOwnProperty( 'class' ) ) {
									powerkitSLinksItem.addClass( data.class );
								}

								// Set Count.
								if ( data.hasOwnProperty( 'result' ) && data.result !== null && data.result.hasOwnProperty( 'count' ) ) {

									if ( data.result.count ) {
										// Class Item.
										powerkitSLinksItem.removeClass( 'pk-social-links-no-count' ).addClass( 'pk-social-links-item-count' );

										// Count item.
										powerkitSLinksItem.find( '.pk-social-links-count' ).not( '.pk-tippy' ).html( data.result.count );
									}
								} else {
									powerkitSLinksItem.addClass( 'pk-social-links-no-count' );
								}

							});
						}

						// Remove Loading Class.
						powerkitSLinksRestBox.removeClass( 'pk-social-links-loading' );
					},
					error: function() {

						// Remove Loading Class.
						powerkitSLinksRestBox.removeClass( 'pk-social-links-loading' );
					}
				});
			});

		})(jQuery);
	</script>
		<script type="text/javascript">
		"use strict";

		(function($) {

			$( window ).on( 'load', function() {

				// Each All Share boxes.
				$( '.pk-share-buttons-mode-rest' ).each( function() {

					var powerkitButtonsIds = [],
						powerkitButtonsBox = $( this );

					// Check Counts.
					if ( ! powerkitButtonsBox.hasClass( 'pk-share-buttons-has-counts' ) && ! powerkitButtonsBox.hasClass( 'pk-share-buttons-has-total-counts' ) ) {
						return;
					}

					powerkitButtonsBox.find( '.pk-share-buttons-item' ).each( function() {
						if ( $( this ).attr( 'data-id' ).length > 0 ) {
							powerkitButtonsIds.push( $( this ).attr( 'data-id' ) );
						}
					});

					// Generate accounts data.
					var powerkitButtonsData = {};

					if( powerkitButtonsIds.length > 0 ) {
						powerkitButtonsData = {
							'ids'     : powerkitButtonsIds.join(),
							'post_id' : powerkitButtonsBox.attr( 'data-post-id' ),
							'url'     : powerkitButtonsBox.attr( 'data-share-url' ),
						};
					}

					// Get results by REST API.
					$.ajax({
						type: 'GET',
						url: 'https://prodsens.live/wp-json/social-share/v1/get-shares',
						data: powerkitButtonsData,
						beforeSend: function(){

							// Add Loading Class.
							powerkitButtonsBox.addClass( 'pk-share-buttons-loading' );
						},
						success: function( response ) {

							if ( ! $.isEmptyObject( response ) && ! response.hasOwnProperty( 'code' ) ) {

								// Accounts loop.
								$.each( response, function( index, data ) {

									if ( index !== 'total_count' ) {

										// Find Bsa Item.
										var powerkitButtonsItem = powerkitButtonsBox.find( '.pk-share-buttons-item[data-id="' + index + '"]');

										// Set Count.
										if ( data.hasOwnProperty( 'count' ) && data.count  ) {

											powerkitButtonsItem.removeClass( 'pk-share-buttons-no-count' ).addClass( 'pk-share-buttons-item-count' );
											powerkitButtonsItem.find( '.pk-share-buttons-count' ).html( data.count );

										} else {
											powerkitButtonsItem.addClass( 'pk-share-buttons-no-count' );
										}
									}
								});

								if ( powerkitButtonsBox.hasClass( 'pk-share-buttons-has-total-counts' ) && response.hasOwnProperty( 'total_count' ) ) {
									var powerkitButtonsTotalBox = powerkitButtonsBox.find( '.pk-share-buttons-total' );

									if ( response.total_count ) {
										powerkitButtonsTotalBox.find( '.pk-share-buttons-count' ).html( response.total_count );
										powerkitButtonsTotalBox.show().removeClass( 'pk-share-buttons-total-no-count' );
									}
								}
							}

							// Remove Loading Class.
							powerkitButtonsBox.removeClass( 'pk-share-buttons-loading' );
						},
						error: function() {

							// Remove Loading Class.
							powerkitButtonsBox.removeClass( 'pk-share-buttons-loading' );
						}
					});
				});
			});

		})(jQuery);
	</script>
	
<script>var rocket_beacon_data = {"ajax_url":"https:\/\/prodsens.live\/wp-admin\/admin-ajax.php","nonce":"b822aed32c","url":"https:\/\/prodsens.live\/2025\/11\/06\/create-a-mcp-server-from-scratch","is_mobile":false,"width_threshold":1600,"height_threshold":700,"delay":500,"debug":null,"status":{"atf":true,"lrc":true,"preconnect_external_domain":true},"elements":"img, video, picture, p, main, div, li, svg, section, header, span","lrc_threshold":1800,"preconnect_external_domain_elements":["link","script","iframe"],"preconnect_external_domain_exclusions":["static.cloudflareinsights.com","rel=\"profile\"","rel=\"preconnect\"","rel=\"dns-prefetch\"","rel=\"icon\""]}</script><script data-name="wpr-wpr-beacon" src='https://prodsens.live/wp-content/plugins/clsop/assets/js/wpr-beacon.min.js' async></script></body>
</html>
<!--
Performance optimized by Redis Object Cache. Learn more: https://wprediscache.com

Retrieved 13778 objects (4 MB) from Redis using PhpRedis (v5.3.7).
-->

<!-- Performance optimized by AccelerateWP. -->