برنامه نویسی

نحوه ساخت سرورهای MCP با TypeScript SDK

این پست وبلاگ نشان می دهد که چگونه می توانید ساخت سرورهای MCP (مدل پروتکل زمینه) خود را شروع کنید. منابع زیادی در اینترنت وجود دارد که سعی در توضیح این موضوع دارند. اگرچه این منابع عالی هستند ، اما من نتوانستم یک راهنمای جامع پیدا کنم و تصمیم گرفتم این پست وبلاگ را با هم جمع کنم. این راهنما اطلاعات کلیدی را به یک پیاده روی واضح و گام به گام برای توسعه دهندگان آماده برای ساختن سرورهای MCP خود ، تثبیت می کند.

MCP در اصطلاح لیمن

پروتکل زمینه مدل (MCP) یک استاندارد باز است که LLM ها مانند Claude را به منابع داده شما متصل می کند. LLMS را قادر می سازد:

  • تجزیه و تحلیل پرونده های محلی (مانند سیاهههای مربوط ، PDF ، CSV در سیستم پرونده خود)
  • پایگاه داده های دسترسی و پرس و جو
  • اسناد را از Google Drive بازیابی کنید
  • اینترنت را مرور کنید

در اصل ، سرورهای MCP به عنوان پل هایی عمل می کنند که به LLM ها دسترسی به منابع داده خاص دسترسی می دهند یا آنها را قادر می سازد تا کارهای تخصصی را انجام دهند.

شما اول سرور MCP هستید

بیایید یک پروژه جدید ایجاد کنیم. یک پوشه جدید ایجاد کرده و فهرست را به آن تغییر دهید.

mkdir my-mcp-server
cd my-mcp-server
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بعد ، یک پروژه جدید را آغاز کنید

npm init -y
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نصب ، وابستگی های لازم

npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ایجاد a src دایرکتوری و الف src/index.ts پرونده

mkdir src
touch src/index.ts
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

باز کردن package.json پرونده های زیر را به آن اضافه کنید

{
    // ... rest of the code
  "type": "module",
  "bin": {
    "weather": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 build/index.js"
  },
  "files": [
    "build"
  ],

}

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

ما همچنین ایجاد خواهیم کرد tsconfig.json پرونده در ریشه پروژه ما.

// tsconfig.json

{
    "compilerOptions": {
      "target": "ES2022",
      "module": "Node16",
      "moduleResolution": "Node16",
      "outDir": "./build",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بعد ، بیایید به روز کنیم index.ts پرونده کد زیر را به اضافه کنید index.ts پرونده

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { 
  StdioServerTransport 
} from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new Server({
    name: "my-mcp-server",
    version: "1.0.0"
  }, {
    capabilities: {
      resources: {},
      tools: {},
    }
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کد فوق اولیه سرور MCP جدید است. وارد می شود Server وت StdioServerTransport از MCP SDK. در Server کلاس برای اولیه سازی یک نمونه سرور جدید استفاده می شود. در StdioServerTransport به سرور اجازه می دهد تا از طریق ورودی/خروجی استاندارد (stdin/stdout) ارتباط برقرار کند.

داده های سفارشی را به LLM با استفاده از LLM ارائه دهید منابع

منابع اصلی اصلی هستند. از آن برای ارائه داده ها به عنوان زمینه برای LLM استفاده می شود. داده ها می توانند هر یک از قالب های زیر باشند

  • محتوای پرونده (TXT ، PDF ، CSV ، تصاویر)
  • سوابق پایگاه داده
  • داده های سیستم زنده
  • گزارش ها و موارد دیگر

بازگشت به شما index.ts پرونده کد زیر را اضافه کنید:

// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
    return {
      resources: [
        {
          uri: "file:///Users/Documents/my-mcp-server/logs.txt",
          name: "Application Logs",
          mimeType: "text/plain"
        }
      ]
    };
});

// Read resource contents
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
    const uri = request.params.uri;
    if (uri === "file:///Users/Documents/my-mcp-server/logs.txt") {
        const logContents = await readLogFile();
        return {
        contents: [
            {
                uri,
                mimeType: "text/plain",
                text: logContents
            }
        ]
        };
    }
    throw new Error("Resource not found");
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در کد فوق ما دو دستگیرنده داریم.

  • در ListResourcesRequestSchema Handler لیستی از منابعی را که مشتریان می توانند به آن دسترسی پیدا کنند ، برمی گرداند. در این حالت ، فقط یک منبع را در معرض نمایش قرار می دهد – یک پرونده ورود به سیستم برنامه واقع در مسیر مطلق تعریف شده
  • در ReadResourceRequestSchema هندلر مسئول بازیابی محتوای یک منبع درخواست شده است.

سرانجام کد زیر را در index.ts پرونده:

// ... rest of the code
async function readLogFile() {
    try {
        // Get the absolute path to the log file
        // Note: You might need to adjust this path based on your system
        const logPath = 
          path.resolve('file:///Users/Documents/my-mcp-server/logs.txt');

        // Read the file
        const data = await fs.readFile(logPath, 'utf8');
        return data;
    } catch (error) {
        console.error('Error reading log file:', error);
        return "Error reading log file: " + 
          (error instanceof Error ? error.message : String(error));
    }
}

// Start the server with stdio transport
async function main() {
    try {
        const transport = new StdioServerTransport();
        await server.connect(transport);
        console.error("Server started and listening on stdio");
    } catch (error) {
        console.error("Failed to start server:", error);
        process.exit(1);
    }
}

main();
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در readLogFile عملکرد یاور عملیات خواندن پرونده را انجام می دهد. در main عملکرد اولیه a StdioServerTransport نمونه ای که ارتباط را با ورودی/خروجی استاندارد امکان پذیر می کند. سرانجام سرور قبلی تعریف شده را با حمل و نقل متصل می کند.

بعد ، اسکریپت ساخت را اجرا کنید:

npm run build
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مسیر مطلق تولید شده را کپی کنید build/index.js پرونده شما باید این را به پیکربندی مشتری Claude Desktop اضافه کنید.

پیکربندی مشتری دسک تاپ کلود

ساده ترین راه برای آزمایش سرورهای MCP در استفاده از مشتری دسک تاپ Claude. تنظیمات مشتری Desktop Claude خود را باز کرده و به توسعه دهنده> ویرایش پیکربندی بروید.

تنظیم دسک تاپ کلود

باز کردن claude_desktop_config.json پرونده JSON زیر را اضافه کرده و اضافه کنید.

{
  "mcpServers": {
        "my-mcp-server": {
            "command": "node",
            "args": [
                "/Users/Documents/my-mcp-server/build/index.js"
            ]
        }
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

حتماً جایگزین کنید /Users/Documents/my-mcp-server/build/index.js با مسیر مطلق خود index.js مسیر پرونده ای که قبلاً کپی کرده اید.

برنامه دسک تاپ Claude خود را باز کنید و باید یک نماد پلاگین جدید برای MCP مشاهده کنید.

نماد پلاگین جدید در کلود

روی دکمه کلیک کنید و باید بتوانید از منابع MCP سرور در گپ بعدی خود استفاده کنید.

انتخاب ادغام

اکنون می توانید با استفاده از LLM با داده های خود ارتباط برقرار کنید.

تعامل با LLM با منابع سفارشی به عنوان زمینه

اشکال زدایی سرورهای MCP

ما می توانیم از @modelcontextprotocol/inspector بسته بندی برای بازرسی و اشکال زدایی عملکرد سرور MCP.

دستور زیر را اجرا کنید تا بازرس اجرا شود

npx @modelcontextprotocol/inspector node /your-build-path/index.js
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در مورد بازرس اینجا بیشتر بدانید.

با استفاده از ابزارهای MCP توابع را اجرا کنید

ابزارها به LLM توانایی انجام اقدامات را می دهد. به عنوان مثال

  • LLM می تواند یک تماس API را برای پاسخ به سوالات جمع آوری داده های مورد نیاز را انجام دهد.
  • با استفاده از ابزارهایی می توانید به LLMS بدهید
  • شما می توانید LLM ها را با استفاده از ابزارها در پایگاه داده خود بنویسید و نمایش داده شود

بیایید یک سرور MCP جدید ایجاد کنیم که بتواند داده ها را از API CocktailDB جمع کند و دستور العمل های نوشیدنی را به ما می دهد.

یک پوشه جدید ایجاد کنید و یک پروژه جدید TypeScript را به دنبال مراحل قبلی تنظیم کنید. بعد ، کد زیر را به خود اضافه کنید index.ts پرونده

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { 
  StdioServerTransport 
} from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
import z from "zod";

// Create the server
const server = new Server({
  name: "cocktail-api-server",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// Define tool schema using zod
const getCocktailSchema = z.object({
  name: z.string().describe("Cocktail name to search for")
});

// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "get_cocktail",
        description: "Search for cocktail recipes by name",
        inputSchema: {
          type: "object",
          properties: {
            name: {
              type: "string",
              description: "Cocktail name to search for"
            }
          },
          required: ["name"]
        }
      }
    ]
  };
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در کد فوق ما در حال ثبت یک ابزار جدید برای سرور هستیم. شما باید تعریف کنید Tools با استفاده از یک ساختار معتبر JSON. می توانید در مورد این ساختار JSON در صفحه اسناد رسمی اطلاعات بیشتری کسب کنید.

در مرحله بعد ، یک تابعی برای رسیدگی به عملکرد ابزارها ایجاد کنید. کد زیر را اضافه کنید:

// Implement the tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_cocktail") {
    try {
      // Parse and validate arguments using zod
      const args = getCocktailSchema.parse(request.params.arguments);

      // Make the API call to CocktailDB
      const url = `
       https://www.thecocktaildb.com/api/json/v1/1/search.php?
       s=${encodeURIComponent(args.name)}
      `;
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`CocktailDB API error: ${response.statusText}`);
      }

      const data = await response.json();

      // Check if any drinks were found
      if (!data.drinks) {
        return {
          content: [
            {
              type: "text",
              text: `
               No cocktails found matching "${args.name}". 
               Try a different search term.
              `
            }
          ]
        };
      }

      // Format each cocktail recipe
      const cocktailRecipes = data.drinks.map(formatCocktail);

      // Create the formatted response
      const result = `
        Found ${data.drinks.length} cocktail(s) matching 
        "${args.name}":\n\n${cocktailRecipes.join('\n\n')}
      `;

      return {
        content: [
          {
            type: "text",
            text: result
          }
        ]
      };
    } catch (error) {
      console.error("Error in get_cocktail tool:", error);

      return {
        isError: true,
        content: [
          {
            type: "text",
            text: `Error searching for cocktail: 
${error instanceof Error ? error.message : 'Unknown error'}`
          }
        ]
      };
    }
  }

  // Handle unknown tool
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `Unknown tool: ${request.params.name}`
      }
    ]
  };
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کد فوق:

  • پارامترهای جستجوی ورودی را تأیید می کند
  • نمایش داده شد thecocktaildbAPI برای کوکتل های مطابق با نام ارائه شده
  • در صورت یافتن نتایج فرمت شده را برمی گرداند ، یا در صورت وجود پیام مفیدی وجود دارد
  • خطاها را به طور مناسب در طول فرآیند انجام می دهد
  • درخواست ابزارهای ناشناخته را رد می کند

این پاسخ سپس به LLM ارسال می شود. LLM سپس پاسخی را بر اساس این زمینه ایجاد می کند.

بیایید یک عملکرد یاور را نیز اضافه کنیم تا پاسخ از API را قالب بندی کنیم.

// ... rest of the code
function formatCocktail(drink: any) {
  // Create an array of ingredients paired with measurements
  const ingredients = [];
  for (let i = 1; i <= 15; i++) {
    const ingredient = drink[`strIngredient${i}`];
    const measure = drink[`strMeasure${i}`];

    if (ingredient) {
      ingredients.push(`${measure ? measure.trim() : ''} ${ingredient}`);
    }
  }

  return `
🍸 ${drink.strDrink} 🍸
-----------------
Category: ${drink.strCategory}
Glass: ${drink.strGlass}
Alcoholic: ${drink.strAlcoholic}

Ingredients:
${ingredients.map(i => `• ${i.trim()}`).join('\n')}

Instructions:
${drink.strInstructions}
  `.trim();
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کد کامل شما در index.ts باید به شرح زیر باشد:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { 
  StdioServerTransport 
} from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
import z from "zod";

// Create the server
const server = new Server({
  name: "cocktail-api-server",
  version: "1.0.0"
}, {
  capabilities: {
    tools: {}
  }
});

// Define tool schema using zod
const getCocktailSchema = z.object({
  name: z.string().describe("Cocktail name to search for")
});

// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "get_cocktail",
        description: "Search for cocktail recipes by name",
        inputSchema: {
          type: "object",
          properties: {
            name: {
              type: "string",
              description: "Cocktail name to search for"
            }
          },
          required: ["name"]
        }
      }
    ]
  };
});

// Helper function to format a cocktail recipe nicely
function formatCocktail(drink: any) {
  // Create an array of ingredients paired with measurements
  const ingredients = [];
  for (let i = 1; i <= 15; i++) {
    const ingredient = drink[`strIngredient${i}`];
    const measure = drink[`strMeasure${i}`];

    if (ingredient) {
      ingredients.push(`${measure ? measure.trim() : ''} ${ingredient}`);
    }
  }

  return `
🍸 ${drink.strDrink} 🍸
-----------------
Category: ${drink.strCategory}
Glass: ${drink.strGlass}
Alcoholic: ${drink.strAlcoholic}

Ingredients:
${ingredients.map(i => `• ${i.trim()}`).join('\n')}

Instructions:
${drink.strInstructions}
  `.trim();
}

// Implement the tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_cocktail") {
    try {
      // Parse and validate arguments using zod
      const args = getCocktailSchema.parse(request.params.arguments);

      // Make the API call to CocktailDB
      const url = `
       https://www.thecocktaildb.com/api/json/v1/1/search.php?
       s=${encodeURIComponent(args.name)}
      `;
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`CocktailDB API error: ${response.statusText}`);
      }

      const data = await response.json();

      // Check if any drinks were found
      if (!data.drinks) {
        return {
          content: [
            {
              type: "text",
              text: `No cocktails found matching "${args.name}". Try a different search term.`
            }
          ]
        };
      }

      // Format each cocktail recipe
      const cocktailRecipes = data.drinks.map(formatCocktail);

      // Create the formatted response
      const result = `
        Found ${data.drinks.length} cocktail(s) matching 
        "${args.name}":\n\n${cocktailRecipes.join('\n\n')}
      `;

      return {
        content: [
          {
            type: "text",
            text: result
          }
        ]
      };
    } catch (error) {
      console.error("Error in get_cocktail tool:", error);

      return {
        isError: true,
        content: [
          {
            type: "text",
            text: `Error searching for cocktail: ${error instanceof Error ? error.message : 'Unknown error'}`
          }
        ]
      };
    }
  }

  // Handle unknown tool
  return {
    isError: true,
    content: [
      {
        type: "text",
        text: `Unknown tool: ${request.params.name}`
      }
    ]
  };
});

// Connect the transport
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Cocktail API server running on stdio");
}

main().catch(err => {
  console.error("Fatal error:", err);
  process.exit(1);
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کد را با دستور زیر بسازید:

npm run build
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

مسیر مطلق خود را کپی کنید dist/index.js پرونده خود را به روز کنید claude_desktop_config.json پرونده برای شامل سرور جدید MCP.

{
    "cocktail-api-server": {
        "command": "node",
        "args": [
            "absolute-path/build/index.js"
        ]
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

دسک تاپ Claude خود را مجدداً راه اندازی کنید و می توانید در مورد دستور العمل های کوکتل سؤال کنید و Claude از داده های کشیده شده از API CocktailDB به عنوان متن استفاده می کند.

الگوی سریع

سرورهای MCP همچنین به شما امکان ذخیره اعلان های قابل استفاده مجدد را می دهند. این یک اجرای مستقیم به جلو است. می توانید در مورد این موضوع در اسناد اطلاعات بیشتری کسب کنید.

و این یک بسته بندی برای این آموزش کوتاه است. اگر فکر می کنید این پست وبلاگ مفید بود ، پس از آن در LinkedIn و X به من بدهید.

از اینجا کجا برویم؟

سایت اسناد رسمی MCPS را پرداخت کنید. اگر می خواهید برخی از دست ها را در نمونه های پرداخت ، مخزن عالی MCP GitHub مشاهده کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا