Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Den här artikeln beskriver hur du skapar en MCP-server (Model Context Protocol) med hjälp av Node.js och TypeScript. Servern kör verktyg och tjänster i en serverlös miljö. Använd den här strukturen som utgångspunkt för att skapa anpassade MCP-servrar.
Gå till koden
Utforska serverexemplet TypeScript remote Model Context Protocol (MCP). Den visar hur du använder Node.js och TypeScript för att skapa en fjärransluten MCP-server och distribuera den till Azure Container Apps.
Gå till avsnittet med kodgenomgång för att förstå hur det här exemplet fungerar.
Översikt över arkitektur
Följande diagram visar exempelappens enkla arkitektur: 
MCP-servern körs som en containerbaserad app i Azure Container Apps (ACA). Den använder en Node.js/TypeScript-serverdel för att tillhandahålla verktyg till MCP-klienten via Model Context Protocol. Alla verktyg fungerar med en SQLite-databas för serverdelen.
Kostnad
För att hålla kostnaderna låga använder det här exemplet prisnivåer för bas- eller förbrukning för de flesta resurser. Justera nivån efter behov och ta bort resurser när du är klar för att undvika avgifter.
Förutsättningar
- Visual Studio Code – den senaste versionen för mcp-serverutveckling.
 - GitHub Copilot Visual Studio Code-tillägg
 - GitHub Copilot Chat Visual Studio Code-tillägg
 - Azure Developer CLI (azd)
 
En utvecklingscontainer innehåller alla beroenden som du behöver för den här artikeln. Du kan köra den i GitHub Codespaces (i en webbläsare) eller lokalt med hjälp av Visual Studio Code.
Följ den här artikeln genom att se till att du uppfyller följande krav:
- En Azure-prenumeration – Skapa en kostnadsfritt
 - Azure-kontobehörigheter – Ditt Azure-konto måste ha 
Microsoft.Authorization/roleAssignments/writebehörigheter, till exempel administratör för rollbaserad åtkomstkontroll, administratör för användaråtkomst eller ägare. Om du inte har behörigheter på prenumerationsnivå måste du beviljas RBAC för en befintlig resursgrupp och distribuera till den gruppen.- Ditt Azure-konto behöver 
Microsoft.Resources/deployments/writeockså behörigheter på prenumerationsnivå. 
 - Ditt Azure-konto behöver 
 - GitHub-konto
 
Öppna utvecklingsmiljön
Följ de här stegen för att konfigurera en förkonfigurerad utvecklingsmiljö med alla nödvändiga beroenden.
GitHub Codespaces kör en utvecklingscontainer som hanteras av GitHub med Visual Studio Code för webben som gränssnitt. Använd GitHub Codespaces för den enklaste installationen, eftersom den levereras med nödvändiga verktyg och beroenden förinstallerade för den här artikeln.
Viktigt!
Alla GitHub-konton kan använda Codespaces i upp till 60 timmar kostnadsfritt varje månad med två kärninstanser. För mer information, se GitHub Codespaces månadsvis inkluderade lagrings- och kärntimmar.
Använd följande steg för att skapa ett nytt GitHub Codespace på grenen main av Azure-Samples/mcp-container-ts GitHub-lagringsplatsen.
Högerklicka på följande knapp och välj Öppna länk i nytt fönster. Med den här åtgärden kan du öppna utvecklingsmiljön och dokumentationen sida vid sida.
På sidan Skapa kodområde granskar du och väljer sedan Skapa nytt kodområde.
Vänta på att kodområdet startar. Det kan ta några minuter.
Logga in på Azure med Azure Developer CLI i terminalen längst ned på skärmen.
azd auth loginKopiera koden från terminalen och klistra sedan in den i en webbläsare. Följ anvisningarna för att autentisera med ditt Azure-konto.
Du utför resten av uppgifterna i den här utvecklingscontainern.
Anmärkning
Så här kör du MCP-servern lokalt:
- Konfigurera din miljö enligt beskrivningen i avsnittet Lokal miljökonfiguration i exempellagringsplatsen.
 - Konfigurera MCP-servern så att den använder den lokala miljön genom att följa anvisningarna i avsnittet Konfigurera MCP-servern i Visual Studio Code på exempellagringsplatsen.
 - Gå till avsnittet Använd TODO MCP-serververktyg i agentläge för att fortsätta.
 
Driftsätta och köra
Exempellagringsplatsen innehåller alla kod- och konfigurationsfiler för MCP-serverns Azure-distribution. Följande steg beskriver exempeldistributionsprocessen för MCP-servern i Azure.
Distribuera till Azure
Viktigt!
Azure-resurser i det här avsnittet börjar kosta pengar omedelbart, även om du stoppar kommandot innan det är klart.
Kör följande Azure Developer CLI-kommando för Azure-resursetablering och källkodsdistribution:
azd upAnvänd följande tabell för att besvara anvisningarna:
Omedelbar Svar Miljönamn Håll det kort och med små bokstäver. Lägg till ditt namn eller alias. Till exempel my-mcp-server. Den används som en del av resursgruppens namn.Prenumeration Välj den prenumeration som resurserna ska skapas i. Plats (för värd) Välj en plats nära dig i listan. Plats för Azure OpenAI-modellen Välj en plats nära dig i listan. Om samma plats är tillgänglig som din första plats väljer du det. Vänta tills appen har distribuerats. Distributionen tar vanligtvis mellan 5 och 10 minuter att slutföra.
När distributionen är klar kan du komma åt MCP-servern med hjälp av URL:en som anges i utdata. URL:en ser ut så här:
https://<env-name>.<container-id>.<region>.azurecontainerapps.io
- Kopiera URL till urklipp. Du behöver det i nästa avsnitt.
 
Konfigurera MCP-servern i Visual Studio Code
Konfigurera MCP-servern i din lokala VS Code-miljö genom att lägga till URL:en i mcp.json filen i .vscode mappen.
Öppna filen
mcp.jsoni mappen.vscode.Leta upp avsnittet
mcp-server-sse-remotei filen. Det bör se ut så här:"mcp-server-sse-remote": { "type": "sse", "url": "https://<container-id>.<location>.azurecontainerapps.io/sse" }Ersätt det befintliga
urlvärdet med den URL som du kopierade i föregående steg.mcp.jsonSpara filen i.vscodemappen.
Använda TODO MCP-serververktyg i agentläge
När du har modifierat MCP-servern kan du använda de verktyg som den tillhandahåller för agentläge. Så här använder du MCP-verktyg i agentläge:
Öppna chattvyn (
Ctrl+Alt+I) och välj Agentläge i listrutan.Välj knappen Verktyg för att visa listan över tillgängliga verktyg. Du kan också välja eller avmarkera de verktyg som du vill använda. Du kan söka efter verktyg genom att skriva i sökrutan.
Ange en uppmaning som "Jag måste skicka ett e-postmeddelande till min chef på onsdag" i rutan för chattinmatning och lägg märke till hur verktyg anropas automatiskt efter behov, som i följande skärmbild:
Anmärkning
När ett verktyg anropas måste du som standard bekräfta åtgärden innan verktyget körs. Annars kan verktyg köras lokalt på datorn och kan utföra åtgärder som ändrar filer eller data.
Använd listrutan Fortsätt för att automatiskt bekräfta det specifika verktyget för den aktuella sessionen, arbetsytan eller alla framtida anrop.
Utforska exempelkoden
Det här avsnittet innehåller en översikt över nyckelfilerna och kodstrukturen i MCP-serverexemplet. Koden är uppdelad i flera huvudkomponenter:
- 
              
index.ts: Huvudinmatningspunkten för MCP-servern, som konfigurerar Express.js HTTP-server och routning. - 
              
server.ts: Transportskiktet som hanterar SSE-anslutningar (Server-Sent Events) och MCP-protokollhantering. - 
              
tools.ts: Innehåller affärslogik och verktygsfunktioner för MCP-servern. - 
              
types.ts: Definierar TypeScript-typer och gränssnitt som används i hela MCP-servern. 
              index.ts – Hur servern startar och accepterar HTTP-anslutningar
Filen index.ts är den viktigaste startpunkten för MCP-servern. Den initierar servern, konfigurerar Express.js HTTP-server och definierar routning för Server-Sent-händelser (SSE) slutpunkter.
Skapa MCP-serverinstansen
Följande kodfragment initierar MCP-servern med hjälp av StreamableHTTPServer klassen, som är en omslutning runt mcp-kärnklassen Server . Den här klassen hanterar transportlagret för Server-Sent Events (SSE) och hanterar klientanslutningar.
const server = new StreamableHTTPServer(
  new Server(
    {
      name: 'todo-http-server',
      version: '1.0.0',
    },
    {
      capabilities: {
        tools: {},
      },
    }
  )
);
Begrepp:
- 
              Kompositionsmönster: 
SSEPServeromsluter klassen på låg nivåServer - Kapacitetsdeklaration: Servern meddelar att den stöder verktyg (men inte resurser/uppmaningar)
 - Namngivningskonvention: Servernamn blir en del av MCP-identifiering
 
Konfigurera Express-vägar
Följande kodfragment konfigurerar Express.js-servern för att hantera inkommande HTTP-begäranden för SSE-anslutningar och meddelandehantering:
router.post('/messages', async (req: Request, res: Response) => {
  await server.handlePostRequest(req, res);
});
router.get('/sse', async (req: Request, res: Response) => {
  await server.handleGetRequest(req, res);
});
Begrepp:
- Mönster för två slutpunkter: GET för att upprätta SSE-anslutning, POST för att skicka meddelanden
 - 
              Delegeringsmönster: Expressvägar delegerar omedelbart till 
SSEPServer 
Processlivscykelhantering
Följande kodfragment hanterar serverns livscykel, inklusive att starta servern och stänga av den på ett korrekt sätt vid avslutningssignaler:
process.on('SIGINT', async () => {
  log.error('Shutting down server...');
  await server.close();
  process.exit(0);
});
Begrepp:
- Smidig avstängning: Rätt avslutning vid Ctrl+C
 - Asynkron rensning: Serverstängningsåtgärden är asynkron
 - Resurshantering: Viktigt för SSE-anslutningar
 
Transportskikt: server.ts
Filen server.ts implementerar transportskiktet för MCP-servern, särskilt hantering av SSE-anslutningar (Server-Sent Events) och routning av MCP-protokollmeddelanden.
Konfigurera en SSE-klientanslutning och skapa en transport
Klassen SSEPServer är det viktigaste transportskiktet för hantering av Server-Sent händelser (SSE) på MCP-servern. Den använder SSEServerTransport klassen för att hantera enskilda klientanslutningar. Den hanterar flera transporter och deras livscykel.
export class SSEPServer {
  server: Server;
  transport: SSEServerTransport | null = null;
  transports: Record<string, SSEServerTransport> = {};
  constructor(server: Server) {
    this.server = server;
    this.setupServerRequestHandlers();
  }
}
Begrepp:
- Tillståndshantering: Spårar både aktuell transport och alla transporter
 - 
              Sessionsmappning: 
transportsobjekt mappar sessions-ID:t till transportinstanser - Konstruktordelegering: Konfigurerar omedelbart begärandehanterare
 
SSE-anslutningsetablering (handleGetRequest)
Metoden handleGetRequest ansvarar för att upprätta en ny SSE-anslutning när en klient gör en GET-begäran till /sse slutpunkten.
async handleGetRequest(req: Request, res: Response) {
  log.info(`GET ${req.originalUrl} (${req.ip})`);
  try {
    log.info("Connecting transport to server...");
    this.transport = new SSEServerTransport("/messages", res);
    TransportsCache.set(this.transport.sessionId, this.transport);
    res.on("close", () => {
      if (this.transport) {
        TransportsCache.delete(this.transport.sessionId);
      }
    });
    await this.server.connect(this.transport);
    log.success("Transport connected. Handling request...");
  } catch (error) {
    // Error handling...
  }
}
Begrepp:
- 
              Skapande av transport: Ny 
SSEServerTransportför varje GET-begäran - Sessionshantering: Automatiskt genererat sessions-ID som lagras i cacheminnet
 - Händelsehanterare: Rensa vid anslutningsstängning
 - 
              MCP-anslutning: 
server.connect()upprättar protokollanslutning - Async-flöde: Anslutningskonfigurationen är asynkron med felgränser
 
Meddelandebearbetning (handlePostRequest)
Metoden handlePostRequest bearbetar inkommande POST-begäranden för att hantera MCP-meddelanden som skickas av klienten. Den använder sessions-ID:t från frågeparametrarna för att hitta rätt transportinstans.
async handlePostRequest(req: Request, res: Response) {
  log.info(`POST ${req.originalUrl} (${req.ip}) - payload:`, req.body);
  const sessionId = req.query.sessionId as string;
  const transport = TransportsCache.get(sessionId);
  if (transport) {
    await transport.handlePostMessage(req, res, req.body);
  } else {
    log.error("Transport not initialized. Cannot handle POST request.");
    res.status(400).json(/* error response */);
  }
}
Begrepp:
- 
              Sessionssökning: Använder 
sessionIdfrågeparameter för att hitta transport - Sessionsverifiering: Verifierar SSE-anslutningen först.
 - Meddelandedelegering: Transport hanterar faktisk meddelandebearbetning
 - Felsvar: Rätt HTTP-felkoder för sessioner som saknas
 
Konfiguration av MCP-protokollhanterare (setupServerRequestHandlers)
Metoden setupServerRequestHandlers registrerar följande hanterare för MCP-protokollbegäranden:
- En hanterare för 
ListToolsRequestSchemasom returnerar listan över tillgängliga TODO-verktyg. - En hanterare för 
CallToolRequestSchemasom letar upp och kör det begärda verktyget med de angivna argumenten. 
Den här metoden använder Zod-scheman för att definiera de förväntade formaten för begäran och svar.
private setupServerRequestHandlers() {
  this.server.setRequestHandler(ListToolsRequestSchema, async (_request) => {
    return {
      tools: TodoTools,
    };
  });
  
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    
    const tool = TodoTools.find((tool) => tool.name === name);
    if (!tool) {
      return this.createJSONErrorResponse(`Tool "${name}" not found.`);
    }
    
    const response = await tool.execute(args as any);
    return { content: [{ type: "text", text: response }] };
  });
}
Begrepp:
- Schema-Based Routning: Använder Zod-scheman för hantering av typsäkra begäranden
 - Verktygsidentifiering: returnerar statisk TodoTools-matris
 - 
              Verktygskörning: 
CallToolRequestSchemahittar och kör verktyg - Felhantering: Korrekt hantering av okända verktyg
 - Svarsformat: MCP-kompatibel svarsstruktur
 - Typsäkerhet: TypeScript-typer säkerställer korrekt argumentöverföring
 
Affärslogik: tools.ts
Filen tools.ts definierar de faktiska funktioner som är tillgängliga för MCP-klienter:
- Verktygsmetadata (namn, beskrivning, scheman)
 - Valideringsscheman för indata
 - Verktygets körningslogik
 - Integrering med databasskikt
 
Den här MCP-servern definierar fyra TODO-hanteringsverktyg:
- 
              
add_todo: Skapar ett nytt TODO-objekt - 
              
complete_todo: Markerar ett TODO-objekt som slutfört - 
              
delete_todo: Tar bort ett TODO-objekt - 
              
list_todos: Visar en lista över alla TODO-objekt - 
              
update_todo_text: Uppdaterar texten i ett befintligt TODO-objekt 
Mönster för verktygsdefinition
Verktygen definieras som en matris med objekt som var och en representerar en specifik TODO-åtgärd. I följande kodfragment addTodo definieras verktyget:
{
  name: "addTodo",
  description: "Add a new TODO item to the list...",
  inputSchema: {
    type: "object",
    properties: {
      text: { type: "string" },
    },
    required: ["text"],
  },
  outputSchema: { type: "string" },
  async execute({ text }: { text: string }) {
    const info = await addTodo(text);
    return `Added TODO: ${text} (id: ${info.lastInsertRowid})`;
  },
}
Varje verktygsdefinition har:
- 
              
name: Unik identifierare för verktyget - 
              
description: Kort beskrivning av verktygets syfte - 
              
inputSchema: Zod-schema som definierar det förväntade indataformatet - 
              
outputSchema: Zod-schema som definierar det förväntade utdataformatet - 
              
execute: Funktion som implementerar verktygets logik 
Dessa verktygsdefinitioner importeras i server.ts och exponeras via ListToolsRequestSchema hanteraren.
Begrepp:
- Design av modulära verktyg: Varje verktyg är ett fristående objekt
 - 
              JSON-schemaverifiering: 
inputSchemadefinierar förväntade parametrar - Typsäkerhet: TypeScript-typer matchar schemadefinitioner
 - Asynkron körning: Alla verktygskörningar är asynkrona
 - Databasintegration: Anropar importerade databasfunktioner
 - Human-Readable Svar: Returnerar formaterade strängar, inte rådata
 
Export av verktygsmatris
Verktygen exporteras som en statisk matris, vilket gör dem enkla att importera och använda på servern. Varje verktyg är ett objekt med dess metadata och körningslogik. Med den här strukturen kan MCP-servern dynamiskt identifiera och köra verktyg baserat på klientbegäranden.
export const TodoTools = [
  { /* addTodo */ },
  { /* listTodos */ },
  { /* completeTodo */ },
  { /* deleteTodo */ },
  { /* updateTodoText */ },
];
Begrepp:
- Statisk registrering: Verktyg som definierats vid modulens inläsningstid
 - Matrisstruktur: Enkel matris gör verktygen enkla att iterera
 - Import/Export: En ren separation från serverlogik
 
Felhantering vid verktygskörning
Varje verktygs execute funktion hanterar fel smidigt och returnerar tydliga meddelanden i stället för att utlösa undantag. Den här metoden säkerställer att MCP-servern ger en smidig användarupplevelse.
Verktyg hanterar olika felscenarier:
async execute({ id }: { id: number }) {
  const info = await completeTodo(id);
  if (info.changes === 0) {
    return `TODO with id ${id} not found.`;
  }
  return `Marked TODO ${id} as completed.`;
}
Begrepp:
- 
              Databassvarskontroll: Används 
info.changesför att identifiera fel - Elegant nedbrytning: Returnerar beskrivande felmeddelanden istället för att kasta
 - User-Friendly Fel: Meddelanden som är lämpliga för AI-tolkning
 
Datalag: db.ts
Filen db.ts hanterar SQLite-databasanslutningen och hanterar CRUD-åtgärder för TODO-appen. Det använder biblioteket better-sqlite3 för synkron databasåtkomst.
Databasinitialisering
Databasen initieras genom att ansluta till SQLite och skapa tabeller om de inte finns. Följande kodfragment visar initieringsprocessen:
const db = new Database(":memory:", {
  verbose: log.info,
});
try {
  db.pragma("journal_mode = WAL");
  db.prepare(
    `CREATE TABLE IF NOT EXISTS ${DB_NAME} (
     id INTEGER PRIMARY KEY AUTOINCREMENT,
     text TEXT NOT NULL,
     completed INTEGER NOT NULL DEFAULT 0
   )`
  ).run();
  log.success(`Database "${DB_NAME}" initialized.`);
} catch (error) {
  log.error(`Error initializing database "${DB_NAME}":`, { error });
}
Begrepp:
- 
              In-Memory Database: 
:memory:innebär att data går förlorade vid omstart (endast demo/testning) - WAL-läge: Write-Ahead loggning för bättre prestanda
 - Schemadefinition: Enkel TODO-tabell med autoincrement-ID
 - Felhantering: Korrekt hantering av initieringsfel
 - Loggningsintegrering: Databasåtgärder loggas för felsökning
 
CRUD-åtgärdsmönster
Filen db.ts innehåller fyra huvudsakliga CRUD-åtgärder för att hantera TODO-objekt:
Skapa åtgärd:
export async function addTodo(text: string) {
  log.info(`Adding TODO: ${text}`);
  const stmt = db.prepare(`INSERT INTO todos (text, completed) VALUES (?, 0)`);
  return stmt.run(text);
}
Läsoperation:
export async function listTodos() {
  log.info("Listing all TODOs...");
  const todos = db.prepare(`SELECT id, text, completed FROM todos`).all() as Array<{
    id: number;
    text: string;
    completed: number;
  }>;
  return todos.map(todo => ({
    ...todo,
    completed: Boolean(todo.completed),
  }));
}
Uppdateringsåtgärd:
export async function completeTodo(id: number) {
  log.info(`Completing TODO with ID: ${id}`);
  const stmt = db.prepare(`UPDATE todos SET completed = 1 WHERE id = ?`);
  return stmt.run(id);
}
Ta bort åtgärd:
export async function deleteTodo(id: number) {
  log.info(`Deleting TODO with ID: ${id}`);
  const row = db.prepare(`SELECT text FROM todos WHERE id = ?`).get(id) as
    | { text: string }
    | undefined;
  if (!row) {
    log.error(`TODO with ID ${id} not found`);
    return null;
  }
  db.prepare(`DELETE FROM todos WHERE id = ?`).run(id);
  log.success(`TODO with ID ${id} deleted`);
  return row;
}
Begrepp:
- Förberedda instruktioner: Skydd mot SQL-inmatning
 - Typgjutning: Tydligt definierade TypeScript-typer för frågeresultat
 - Datatransformering: Konvertera SQLite-heltal till booleska värden
 - Atomiska åtgärder: Varje funktion är en enskild databastransaktion
 - Returvärdeskonsekvens: Funktioner returnerar operationsmetadata
 - Defensiv programmering: Mönster för kontroll före borttagning
 
Schemadesign
Databasschemat definieras i db.ts filen med hjälp av en enkel SQL-instruktion. Tabellen todos har tre fält:
CREATE TABLE todos (
  id INTEGER PRIMARY KEY AUTOINCREMENT,  -- Unique identifier
  text TEXT NOT NULL,                    -- TODO description  
  completed INTEGER NOT NULL DEFAULT 0   -- Boolean as integer
);
Hjälpverktyg: helpers/ katalog
Katalogen helpers/ innehåller verktygsfunktioner och klasser för servern.
Strukturerad loggning för felsökning och övervakning: helpers/logs.ts
Filen helpers/logs.ts innehåller ett strukturerat loggningsverktyg för MCP-servern. Det använder biblioteket debug för loggning och chalk för färgkodade utdata i konsolen.
export const logger = (namespace: string) => {
  const dbg = debug('mcp:' + namespace);
  const log = (colorize: ChalkInstance, ...args: any[]) => {
    const timestamp = new Date().toISOString();
    const formattedArgs = [timestamp, ...args].map((arg) => {
      if (typeof arg === 'object') {
        return JSON.stringify(arg, null, 2);
      }
      return arg;
    });
    dbg(colorize(formattedArgs.join(' ')));
  };
  return {
    info(...args: any[]) { log(chalk.cyan, ...args); },
    success(...args: any[]) { log(chalk.green, ...args); },
    warn(...args: any[]) { log(chalk.yellow, ...args); },
    error(...args: any[]) { log(chalk.red, ...args); },
  };
};
Sessionshantering för SSE-överföringar: helpers/cache.ts
Filen helpers/cache.ts använder en Map för att lagra SSE-transporter med sessions-ID. Med den här metoden kan servern snabbt hitta och hantera aktiva anslutningar.
import type { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse";
export const TransportsCache = new Map<string, SSEServerTransport>();
Anmärkning
              TransportsCache är en enkel minnesintern cache. Överväg att använda en mer robust lösning som Redis eller en databas för sessionshantering i produktion.
Sammanfattning av exekveringsflöde
Följande diagram visar den fullständiga begäranderesan från klienten till MCP-servern och tillbaka, inklusive verktygskörning och databasåtgärder:
Städa upp GitHub Codespaces
Ta bort GitHub Codespaces-miljön för att maximera dina kostnadsfria kärntimmar.
Viktigt!
Mer information om ditt GitHub-kontos kostnadsfria lagrings- och kärntimmar finns i GitHub Codespaces månadsvis inkluderade lagrings- och kärntimmar.
Logga in på GitHub Codespaces-kontrollpanelen.
Hitta dina aktiva Codespaces som skapats från GitHub-lagret
Azure-Samples//mcp-container-ts.Öppna snabbmenyn för kodområdet och välj Ta bort.
Få hjälp
Rapportera ditt ärende i lagringsplatsens ärenden.