はじめに: プロトコルからコードへ
以前の記事では、 モデルコンテキストプロトコル、3時 プリミティブ (ツール、リソース、プロンプト) とモノリポジトリ アーキテクチャ。ここでコードを記述します。 このチュートリアルでは、 TypeScript で完成した MCP サーバー、プロジェクトから始める Claude Desktop でテストできる動作するサーバーに到達するまでは空です。
私たちが作成するサーバーは、次のシステムを管理します。 記憶の中のメモ、追加するツールを公開する、 メモを読んだり、検索したり、削除したりできます。高度な検証を実装します。 ゾッド、 構造化されたエラー処理と、URI 経由でメモにアクセスするためのリソース。すべてのコードが利用可能です リポジトリ内で GitHub 上の Tech-MCP.
この記事で学べること
- MCP サーバー用の TypeScript プロジェクトをセットアップする方法 (package.json、tsconfig.json)
- クラスを使用してサーバーを作成する方法
McpServer公式SDKの - ツールを登録する方法
server.tool()Zod スキームを使用して入力を検証します - リソースを公開する方法
server.resource()および URI テンプレート - パターンでのエラーの処理方法
isError: true - トランスポートの設定方法
StdioServerTransport - MCP Inspector と Claude Desktop を使用してサーバーをテストする方法
- デバッグ ルール: なぜ使用しないのか
console.log()
前提条件
始める前に、システムに次のツールがインストールされていることを確認してください。
- Node.js 18+: JavaScript/TypeScript ランタイム (
node --version確認するため) - npm o pnpm: 依存関係を管理するパッケージマネージャー
- TypeScript 5.x: サーバー全体で使用する型付き言語
- クロードデスクトップ (オプション): 実際の MCP クライアントを使用してサーバーをテストするには
ステップ 1: プロジェクトの初期化
プロジェクトの構造をゼロから作成します。私たちのサーバーが呼び出されます mcp-notes-server
TypeScript を使用した Node.js プロジェクトになります。
ディレクトリの作成と初期化
# Crea la directory del progetto
mkdir mcp-notes-server && cd mcp-notes-server
# Inizializza il progetto Node.js
npm init -y
# Installa le dipendenze di produzione
npm install @modelcontextprotocol/sdk zod
# Installa le dipendenze di sviluppo
npm install -D typescript @types/node
# Crea la directory per il codice sorgente
mkdir -p src
インストールされた依存関係には特定の役割があります。
- @モデルコンテキストプロトコル/SDK: 公式 MCP SDK が提供する
McpServer、トランスポートおよびプロトコル ユーティリティ - ゾッド: MCP がツール パラメータの定義と検証に使用するスキーマ検証ライブラリ
- タイプスクリプト: 静的型付け用の TypeScript コンパイラ
- @タイプ/ノード: Node.js API の型定義
Package.json の構成
ファイルを更新する package.json MCP サーバーに必要な構成で生成されます。
フィールド "type": "module" MCP SDK は ESM (ECMAScript Modules) を使用するため、これは基本的なものです。
{
"name": "mcp-notes-server",
"version": "1.0.0",
"description": "Server MCP per la gestione di note",
"type": "module",
"main": "dist/index.js",
"bin": {
"mcp-notes": "dist/index.js"
},
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"start": "node dist/index.js",
"inspect": "npx @modelcontextprotocol/inspector dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"zod": "^3.22.0"
},
"devDependencies": {
"typescript": "^5.5.0",
"@types/node": "^20.0.0"
}
}
フィールドノートの「タイプ」: 「モジュール」
フィールド "type": "module" そして必須です。これがないと、Node.js がファイルを処理します。 .js
CommonJS や MCP SDK のインポートは構文エラーで失敗します。このフィールドを忘れた場合は、
次のようなエラーが表示されます SyntaxError: Cannot use import statement in a module.
TypeScript 構成 (tsconfig.json)
ファイルを作成する tsconfig.json プロジェクトのルートにあります。構成には互換性がある必要があります
ESM および Node.js 16 以降の場合:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"declaration": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
最も重要な構成フィールドは次のとおりです。
- ターゲット:「ES2022」: トップレベルの待機をサポートする最新バージョンの JavaScript にコンパイルします。
- モジュール:「Node16」: Node.js 16+ ESM システムと互換性のあるモジュールを生成します
- モジュール解像度: "Node16": Node.js 16 ルールに従ってモジュールを解決します (インポートには .js 拡張子が必要です)
- 厳密: 真: セキュリティを強化するためにすべての厳密な型チェックを有効にします
- 宣言: true: ファイルを生成します
.d.ts外部型チェック用
プロジェクトの最終構造
セットアップが完了すると、プロジェクト構造は次のようになります。
mcp-notes-server/
src/
index.ts # Entry point e logica del server
package.json # Configurazione npm con type: module
tsconfig.json # Configurazione TypeScript
node_modules/ # Dipendenze installate
ステップ 2: 最小限の MCP サーバーを作成する
単一のツールを登録する最小限のサーバーから始めます。ファイルを作成する src/index.ts:
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// ============================================
// Stato in memoria
// ============================================
const notes: Map<string, string> = new Map();
// ============================================
// Creazione del server MCP
// ============================================
const server = new McpServer({
name: "notes-server",
version: "1.0.0",
});
// ============================================
// Tool: aggiungere una nota
// ============================================
server.tool(
"add-note",
"Aggiunge una nuova nota con titolo e contenuto",
{
title: z.string().describe("Titolo della nota"),
content: z.string().describe("Contenuto della nota"),
},
async ({ title, content }) => {
notes.set(title, content);
return {
content: [
{
type: "text",
text: `Nota "${title}" salvata con successo.`,
},
],
};
},
);
// ============================================
// Avvio del server con transport STDIO
// ============================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Notes MCP Server avviato su stdio");
}
main().catch((error) => {
console.error("Errore fatale:", error);
process.exit(1);
});
コードの構造
コードの各セクションを分析して、各コンポーネントの役割を理解しましょう。
1. シバンと輸入品
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
Lo シバン (#!/usr/bin/env node) としてファイルを直接実行できます。
コマンドラインからスクリプトを作成します。インポートでは、次の 3 つの重要なコンポーネントがロードされます。
- Mcpサーバー: プロトコルのライフサイクル、ツールの登録、機能のネゴシエーションを管理するメイン クラス
- Stdioサーバートランスポート: プロセスの stdin/stdout 経由で通信するトランスポート
- z (Zod): パラメーター スキームを定義するための検証ライブラリ
2. サーバーインスタンスの作成
const server = new McpServer({
name: "notes-server", // Identificativo univoco del server
version: "1.0.0", // Versione seguendo il formato semver
});
インスタンス McpServer 構成オブジェクトを受け入れます name (識別子
クライアントがサーバーを認識するために使用する一意のコード) e version (semver 形式のバージョン)。
このデータは、プロトコルの初期化フェーズ中にクライアントに送信されます。
3.server.tool()の署名
方法 server.tool() そしてツール登録の核心。 4 つの引数を受け入れます。
server.tool() への引数
| 主題 | タイプ | 説明 |
|---|---|---|
| 名前 | string |
ツールの一意の識別子 (例: 「add-note」) |
| 説明 | string |
AI モデルが読み取ってツールをいつ呼び出すかを決定する説明 |
| 入力スキーマ | object |
受け入れられるパラメータを定義する Zod キー付きオブジェクト |
| ハンドラ | async function |
型指定されたパラメータを受け取り、結果を返す非同期関数 |
4. 結果の形式
各ツール ハンドラーはフィールドを持つオブジェクトを返す必要があります。 content、要素の配列です
典型的な。 MCP は、結果で 3 種類のコンテンツをサポートします。
// Tipo 1: Testo semplice
return {
content: [
{ type: "text", text: "Il risultato dell'operazione" }
],
};
// Tipo 2: Immagine (base64)
return {
content: [
{ type: "image", data: "iVBORw0KGgo...", mimeType: "image/png" }
],
};
// Tipo 3: Risorsa embedded
return {
content: [
{ type: "resource", resource: { uri: "note://lista", text: "..." } }
],
};
5. トランスポート STDIO
const transport = new StdioServerTransport();
await server.connect(transport);
StdioServerTransport 経由で通信するようにサーバーを構成します 標準入力 (クライアントからのメッセージを受信)
e 標準出力 (クライアントに応答を送信します)。方法 connect() ~のループを開始する
読み取り/書き込みが行われ、その瞬間からサーバーは JSON-RPC メッセージをリッスンします。
基本ルール: stdout には決して書き込まない
STDIO トランスポートでは、チャネル stdout JSON-RPC メッセージ専用に予約されています
プロトコルの。標準出力へのその他の出力 ( console.log()) 破損する可能性があります
プロトコルに問題があり、クライアントで解析エラーが発生する可能性があります。ログ記録とデバッグには、次を使用します。
いつも console.error()について書いている人 stderr.
ステップ 3: 完全なツールを追加する (CRUD)
ノートに対する CRUD 操作の完全なセットを使用してサーバーを拡張してみましょう。以下のツールを追加します
ツールの後 add-note ファイルの中に src/index.ts:
ツール: メモを読む
server.tool(
"get-note",
"Recupera il contenuto di una nota dal titolo",
{
title: z.string().describe("Titolo della nota da leggere"),
},
async ({ title }) => {
const content = notes.get(title);
if (!content) {
return {
content: [{ type: "text", text: `Nota "${title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: content }],
};
},
);
ツール: すべてのメモをリストする
server.tool(
"list-notes",
"Elenca tutte le note salvate con i rispettivi titoli",
{}, // Nessun parametro richiesto: schema vuoto
async () => {
if (notes.size === 0) {
return {
content: [{ type: "text", text: "Nessuna nota salvata." }],
};
}
const list = Array.from(notes.entries())
.map(([title, content], i) =>
`${i + 1}. ${title} (${content.length} caratteri)`
)
.join("\n");
return {
content: [{ type: "text", text: `Note salvate:\n${list}` }],
};
},
);
ツール: メモの削除
server.tool(
"delete-note",
"Cancella una nota esistente dal titolo",
{
title: z.string().describe("Titolo della nota da cancellare"),
},
async ({ title }) => {
const deleted = notes.delete(title);
if (!deleted) {
return {
content: [{ type: "text", text: `Nota "${title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: `Nota "${title}" cancellata con successo.` }],
};
},
);
ステップ 4: Zod を使用した高度な検証
Zod は単なる検証ライブラリではありません。ツールのパラメータを記述するための言語です。
AIモデルに。各 Zod スキーマは JSON スキーマに変換され、フェーズ中にクライアントに送信されます。
発見の(tools/list)。方法 .describe() そして特に重要な
テキストはスキーマに含まれており、AI が引数として何を提供するかを理解するのに役立つからです。
Zod の機能を実証するために、高度な検証を備えた検索ツールを追加してみましょう。
server.tool(
"search-notes",
"Cerca note per parola chiave con opzioni di filtro avanzate",
{
query: z.string()
.min(2)
.describe("Testo da cercare (minimo 2 caratteri)"),
caseSensitive: z.boolean()
.optional()
.default(false)
.describe("Se true, la ricerca e case-sensitive"),
limit: z.number()
.int()
.min(1)
.max(100)
.optional()
.default(10)
.describe("Numero massimo di risultati (1-100)"),
},
async ({ query, caseSensitive, limit }) => {
const results: string[] = [];
for (const [title, content] of notes) {
const haystack = caseSensitive ? content : content.toLowerCase();
const needle = caseSensitive ? query : query.toLowerCase();
if (haystack.includes(needle)) {
results.push(title);
}
if (results.length >= limit) break;
}
return {
content: [{
type: "text",
text: results.length > 0
? `Trovate ${results.length} note:\n${results.join("\n")}`
: `Nessun risultato per "${query}".`,
}],
};
},
);
MCP の一般的な Zod パターン
MCP ツールの回路図を定義する際に最も使用される Zod パターンのクイック リファレンスを次に示します。
MCP の Zod クイック リファレンス
| パターン | コード | 使用 |
|---|---|---|
| 必須の文字列 | z.string() |
必須のテキストパラメータ |
| 空でない文字列 | z.string().min(1) |
空にすることはできないパラメータ |
| 有効な電子メール | z.string().email() |
電子メール形式の検証 |
| 有効なURL | z.string().url() |
URL形式の検証 |
| 正の整数 | z.number().int().positive() |
ID、カウンター |
| 数値範囲 | z.number().min(0).max(100) |
パーセンテージ、スコア |
| ブール値 | z.boolean() |
フラグのオン/オフ |
| 文字列列挙型 | z.enum(["low", "medium", "high"]) |
デフォルト値 |
| デフォルトではオプション | z.string().optional().default("valore") |
デフォルト値を持つパラメータ |
| 文字列配列 | z.array(z.string()) |
リスト、タグ |
| AIの説明 | z.string().describe("spiegazione") |
AI モデルのコンパイルをガイドします |
方法 .describe() AI エクスペリエンスにとって重要なのは、テキストが
JSON スキーマは検出中にモデルに送信され、何を提供するかを正確に判断できるようになります。
各パラメータの値として。明確かつ簡潔な説明を書きます。
ステップ 5: isError によるエラー処理
MCP は、ツール レベルでのエラー処理の明確なパターンを定義します。 2つのカテゴリーがあります 処理するエラーの数:
実行エラー (ツールによって管理)
ツールで予測可能なエラーが発生した場合 (リソースが見つからない、検証が失敗した、サービスが見つからない)
利用可能)、次の結果を返す必要があります isError: true。これは AI モデルに信号を送ります
作戦が失敗したため、彼は適切に対応できたということです。
server.tool(
"get-note",
"Recupera una nota dal titolo",
{
title: z.string().describe("Titolo della nota"),
},
async ({ title }) => {
try {
const content = notes.get(title);
if (!content) {
// Errore prevedibile: nota non trovata
return {
content: [{
type: "text",
text: `Errore: la nota "${title}" non esiste.`,
}],
isError: true,
};
}
return {
content: [{ type: "text", text: content }],
};
} catch (error) {
// Errore imprevisto: catturato e gestito
return {
content: [{
type: "text",
text: `Errore: ${error instanceof Error ? error.message : String(error)}`,
}],
isError: true,
};
}
},
);
プロトコル エラー (未処理の例外)
ツール ハンドラーが未処理の例外をスローした場合、SDK は自動的に例外をエラーに変換します。
コードを含む JSON-RPC -32603 (内部エラー)。常にエラーを処理することをお勧めします
try/catch を明示的に使用して、より有益なエラー メッセージをモデルに提供します。
ベスト プラクティス: 常にエラーを処理する
推奨されるパターンは、すべてのツール ロジックを 1 つのブロックにラップすることです try/catch、
戻る isError: true エラーの場合は説明メッセージが表示されます。これ
AI モデルがエラーをコンテキストとして受け取り、続行方法を決定できるようにします (再試行、
ユーザーに尋ねるか、別のアプローチを試してください)。
ステップ 6:server.resource() を使用してリソースを公開する
ツールに加えて、MCP サーバーは リソース: からアクセス可能なコンテキスト データ URI経由のクライアント。リソースは、プロンプトを表示せずに読み取り専用の情報を提供するのに役立ちます。 モデルの明示的なアクション。
メモの完全なリストを公開するリソースを追加しましょう。
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
// Risorsa: elenco di tutte le note
server.resource(
"notes-list",
"note://list",
"Elenco completo di tutte le note salvate",
async (uri) => {
const allNotes = Array.from(notes.entries())
.map(([title, content]) => `## ${title}\n${content}`)
.join("\n\n---\n\n");
return {
contents: [
{
uri: uri.href,
mimeType: "text/markdown",
text: allNotes || "Nessuna nota disponibile.",
},
],
};
},
);
// Risorsa con template URI: singola nota
server.resource(
"note-by-title",
new ResourceTemplate("note://{title}", { list: undefined }),
"Accede a una singola nota tramite il titolo",
async (uri, { title }) => {
const content = notes.get(title as string);
return {
contents: [
{
uri: uri.href,
mimeType: "text/plain",
text: content ?? `Nota "${title}" non trovata.`,
},
],
};
},
);
ツールとリソースの違いは、MCP プロトコルの基本です。
- ツール: AI モデルによって呼び出されるアクション、副作用がある可能性があります (データの作成、変更、削除)
- リソース: クライアント アプリケーションからアクセスできる読み取り専用データ。副作用はありません。
ステップ 7: 完全なサーバー
すべてのツール、リソース、エラー処理を含む完全なサーバー コードを次に示します。
これがファイルです src/index.ts ファイナル:
#!/usr/bin/env node
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// ============================================
// Stato in memoria
// ============================================
const notes: Map<string, string> = new Map();
// ============================================
// Creazione del server MCP
// ============================================
const server = new McpServer({
name: "notes-server",
version: "1.0.0",
});
// ============================================
// TOOL: Aggiungere una nota
// ============================================
server.tool(
"add-note",
"Aggiunge una nuova nota con titolo e contenuto",
{
title: z.string().min(1).describe("Titolo della nota"),
content: z.string().min(1).describe("Contenuto della nota"),
},
async ({ title, content }) => {
if (notes.has(title)) {
return {
content: [{
type: "text",
text: `Nota "${title}" già esistente. Usa un titolo diverso.`,
}],
isError: true,
};
}
notes.set(title, content);
return {
content: [{
type: "text",
text: `Nota "${title}" salvata con successo (${content.length} caratteri).`,
}],
};
},
);
// ============================================
// TOOL: Leggere una nota
// ============================================
server.tool(
"get-note",
"Recupera il contenuto di una nota dal titolo",
{
title: z.string().describe("Titolo della nota da leggere"),
},
async ({ title }) => {
const content = notes.get(title);
if (!content) {
return {
content: [{ type: "text", text: `Nota "${title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: content }],
};
},
);
// ============================================
// TOOL: Elencare tutte le note
// ============================================
server.tool(
"list-notes",
"Elenca tutte le note salvate con i rispettivi titoli",
{},
async () => {
if (notes.size === 0) {
return {
content: [{ type: "text", text: "Nessuna nota salvata." }],
};
}
const list = Array.from(notes.entries())
.map(([title, content], i) =>
`${i + 1}. ${title} (${content.length} caratteri)`
)
.join("\n");
return {
content: [{ type: "text", text: `Note salvate:\n${list}` }],
};
},
);
// ============================================
// TOOL: Cancellare una nota
// ============================================
server.tool(
"delete-note",
"Cancella una nota esistente dal titolo",
{
title: z.string().describe("Titolo della nota da cancellare"),
},
async ({ title }) => {
const deleted = notes.delete(title);
if (!deleted) {
return {
content: [{ type: "text", text: `Nota "${title}" non trovata.` }],
isError: true,
};
}
return {
content: [{ type: "text", text: `Nota "${title}" cancellata.` }],
};
},
);
// ============================================
// TOOL: Cercare note con filtri avanzati
// ============================================
server.tool(
"search-notes",
"Cerca note per parola chiave con opzioni di filtro",
{
query: z.string().min(2).describe("Testo da cercare (minimo 2 caratteri)"),
caseSensitive: z.boolean().optional().default(false)
.describe("Se true, la ricerca e case-sensitive"),
limit: z.number().int().min(1).max(100).optional().default(10)
.describe("Numero massimo di risultati (1-100)"),
},
async ({ query, caseSensitive, limit }) => {
const results: string[] = [];
for (const [title, content] of notes) {
const haystack = caseSensitive ? content : content.toLowerCase();
const needle = caseSensitive ? query : query.toLowerCase();
if (haystack.includes(needle)) {
results.push(title);
}
if (results.length >= limit) break;
}
return {
content: [{
type: "text",
text: results.length > 0
? `Trovate ${results.length} note:\n${results.join("\n")}`
: `Nessun risultato per "${query}".`,
}],
};
},
);
// ============================================
// RISORSA: Elenco completo note
// ============================================
server.resource(
"notes-list",
"note://list",
"Elenco completo di tutte le note salvate",
async (uri) => {
const allNotes = Array.from(notes.entries())
.map(([title, content]) => `## ${title}\n${content}`)
.join("\n\n---\n\n");
return {
contents: [{
uri: uri.href,
mimeType: "text/markdown",
text: allNotes || "Nessuna nota disponibile.",
}],
};
},
);
// ============================================
// Avvio del server con transport STDIO
// ============================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Notes MCP Server avviato su stdio");
}
main().catch((error) => {
console.error("Errore fatale:", error);
process.exit(1);
});
ステップ 8: MCP Inspector を使用してビルドしてテストする
プロジェクトをコンパイルし、MCP サーバー用の公式デバッグ ツールである MCP Inspector を使用してサーバーをテストします。
# Compila il progetto TypeScript
npm run build
# Testa con MCP Inspector (apre un'interfaccia web)
npx @modelcontextprotocol/inspector dist/index.js
MCP Inspector はブラウザで Web インターフェイスを開き、次のことを行うことができます。
- 登録されているすべてのツールをそれぞれのスキームとともに表示します
- テストパラメータを指定してツールを手動で呼び出す
- 解答の形式を確認する
- URI 経由でリソースをテストする
- 交換される JSON-RPC メッセージを監視する
ステップ 9: Claude デスクトップの構成
Claude Desktop (最も人気のある MCP クライアント) でサーバーを使用するには、次の構成を追加します。 オペレーティング システムに適したファイルに次のように記述します。
設定ファイルのパス
| オペレーティング·システム | パス |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/claude/claude_desktop_config.json |
サーバー構成を JSON ファイルに追加します。
{
"mcpServers": {
"notes": {
"command": "node",
"args": ["/percorso/assoluto/mcp-notes-server/dist/index.js"]
}
}
}
ファイルを保存して Claude Desktop を再起動すると、モデルにメモ サーバー ツールが表示されます。 会話中に自動的に呼び出すことができます。サーバーが Claude Desktop の下部バーにあるツール アイコンを探して、正しく接続してください。
環境変数の追加
サーバーに環境変数が必要な場合 (API キーや構成など)、
フィールドを使用して構成に追加できます。 env:
{
"mcpServers": {
"notes": {
"command": "node",
"args": ["/percorso/assoluto/mcp-notes-server/dist/index.js"],
"env": {
"STORAGE_PATH": "/home/utente/notes",
"MAX_NOTES": "1000"
}
}
}
}
デバッグ: ルールとヒント
MCP サーバーのデバッグには、従来のアプリケーションと比較して特有の課題があります。 最も一般的な問題を解決するための基本的なルールと提案は次のとおりです。
ルール 1: console.log() を決して使用しない
STDIOトランスポートを使用すると、 console.log() 書きます stdout、チャンネルです
JSON-RPC メッセージ用に予約されています。シングル console.log() 全体を腐敗させる可能性があります
プロトコル。アメリカ合衆国 いつも console.error() デバッグ用:
// SBAGLIATO - corrompe il protocollo STDIO
console.log("Debug: nota aggiunta");
// CORRETTO - scrive su stderr, non interferisce
console.error("Debug: nota aggiunta");
console.error("[DEBUG]", JSON.stringify({ title, content }));
ルール 2: ビルドを確認する
よくある間違いは、変更後に再コンパイルを忘れることです。アメリカ合衆国 npm run dev
監視モードでの自動コンパイルの場合:
# Compilazione automatica ad ogni modifica
npm run dev
# In un altro terminale, testa con l'inspector
npx @modelcontextprotocol/inspector dist/index.js
ルール 3: サーバー ログを確認する
に書かれたメッセージは、 stderr これらは Claude デスクトップ端末に表示されます
([開発者] > [コンソールを開く])、MCP インスペクター内。構造化ログを使用してデバッグを容易にします。
async ({ title, content }) => {
console.error(`[add-note] Ricevuta richiesta: title=${title}`);
notes.set(title, content);
console.error(`[add-note] Nota salvata. Totale note: ${notes.size}`);
return {
content: [{ type: "text", text: `Nota "${title}" salvata.` }],
};
}
デバッグチェックリスト
| 問題 | 考えられる原因 | 解決 |
|---|---|---|
| サーバーが起動しない | 不足しているもの "type": "module" package.json内 |
package.jsonにフィールドを追加します。 |
| インポートが見つかりません | 不足しているもの .js インポートパス内 |
拡張機能を追加します。 "...mcp.js" |
| クロードにはツールが見えません | JSON 構成内のパスが間違っています | フィールドで絶対パスを使用する args |
| プロトコルエラー | console.log() コードの中で |
と置き換えます console.error() |
| パラメータが検証されていません | 不完全なゾッド図 | 追加 .describe() あらゆる分野へ |
まとめ
この記事では、完全な MCP サーバーを最初から構築しました。私たちがカバーした内容は次のとおりです。
- プロジェクトのセットアップ: npm の初期化、ESM による TypeScript の設定、フォルダー構造
- Mcpサーバー: 名前とバージョンを使用したサーバー インスタンスの作成
- サーバー.ツール(): 名前、説明、Zod スキーマ、および非同期ハンドラーを使用したツールの登録
- ゾッドの検証: 表現力豊かなパターンを定義するための高度なパターン (min、max、enum、optional、default、describe)
- エラー処理: パターン
isError: true処理されたエラーの場合は try/catch、予期しない例外の場合は try/catch - サーバー.リソース(): URI および URI テンプレートを介したコンテキスト データの公開
- Stdioサーバートランスポート: stdin/stdout 経由の通信用のトランスポート設定
- テスト: MCP Inspector で確認し、Claude Desktop をセットアップします
- デバッグ: 標準出力には決して書き込まないという基本ルール、ログには console.error() を使用する
次の記事
シリーズの次の記事では、プロトコルの反対側について説明します。 MCPクライアントを作成する TypeScript で。サーバーに接続し、機能をネゴシエートし、リストし、呼び出す方法を見ていきます。 ツールをプログラム的に使用し、リモート接続の HTTP トランスポートを管理します。私たちは構築することを学びます 標準プロトコルと互換性のある任意の MCP サーバーと通信できるクライアント。
すべての例を含む完全なコードはリポジトリで入手できます。 GitHub 上の Tech-MCP.







