公式ドキュメントにある内容を参考にMCPサーバを作成して、Claudeで使用してみます。
今回作成するのはWindows環境になります。
MCPサーバの内容
今回作成する内容は公式ドキュメントにある内容と同じく、天気API(National Weather Service)を使ったMCPサーバになります。
使用できるツールとして、下記の2つを用意します。
- get-alerts:指定された州の天気警報を取得
- get-forecast:指定された緯度・経度の天気予報を取得
MCPサーバの作成
まずはプロジェクトディレクトリを作成して、移動します。
cd プロジェクトディレクトリ
package.jsonを作成します。
npm init -y
使用するパッケージをインストールします。
npm install @modelcontextprotocol/sdk zod npm install -D @types/node typescript
package.json内に下記を追記します。
{
~ 略 ~
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && chmod 755 build/index.js"
},
"files": [
"build"
],
~ 略 ~
}
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"]
}
srcディレクトリを作成して、その中にindex.tsファイルを作成します。
内容は以下の通りで、公式サイトのコードにコメントで説明を適宜追加しています。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 天気API(National Weather Service)のベースURL
const NWS_API_BASE = "https://api.weather.gov";
// APIにアクセスする際のUser-Agent(アクセス元情報)
const USER_AGENT = "weather-app/1.0";
// MCPサーバのインスタンスを作成
const server = new McpServer({
name: "weather",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
/**
* NWS(天気API)にリクエストを送って、JSONレスポンスを取得する関数
* @param url - アクセスするAPIのURL
* @returns - JSONとしてパースされたデータ もしくは null
*/
async function makeNWSRequest<T>(url: string): Promise<T | null> {
const headers = {
"User-Agent": USER_AGENT,
Accept: "application/geo+json",
};
try {
// APIへリクエストを送信
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// レスポンスをJSONとして返す
return (await response.json()) as T;
} catch (error) {
// 通信エラーやパースエラーが発生した場合はnullを返す
console.error("Error making NWS request:", error);
return null;
}
}
// アラート情報の型
interface AlertFeature {
properties: {
event?: string;
areaDesc?: string;
severity?: string;
status?: string;
headline?: string;
};
}
/**
* アラート情報を整形する関数
*/
function formatAlert(feature: AlertFeature): string {
const props = feature.properties;
return [
`Event: ${props.event || "Unknown"}`,
`Area: ${props.areaDesc || "Unknown"}`,
`Severity: ${props.severity || "Unknown"}`,
`Status: ${props.status || "Unknown"}`,
`Headline: ${props.headline || "No headline"}`,
"---",
].join("\n");
}
// 天気予報1期間分のデータの型
interface ForecastPeriod {
name?: string;
temperature?: number;
temperatureUnit?: string;
windSpeed?: string;
windDirection?: string;
shortForecast?: string;
}
// NWSアラートAPIのレスポンス型
interface AlertsResponse {
features: AlertFeature[];
}
// 指定座標に対するポイントAPIのレスポンス型
interface PointsResponse {
properties: {
forecast?: string;
};
}
// 予報APIのレスポンス型
interface ForecastResponse {
properties: {
periods: ForecastPeriod[];
};
}
/**
* MCPのツールに州のアラート情報を取得する機能を登録
*/
server.tool(
"get-alerts", // ツール名
"Get weather alerts for a state", // ツールの説明
{
// 入力パラメータの定義(2文字の州コード)
state: z.string().length(2).describe("Two-letter state code (e.g. CA, NY)"),
},
async ({ state }) => {
const stateCode = state.toUpperCase();
const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await makeNWSRequest<AlertsResponse>(alertsUrl);
if (!alertsData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve alerts data",
},
],
};
}
const features = alertsData.features || [];
if (features.length === 0) {
return {
content: [
{
type: "text",
text: `No active alerts for ${stateCode}`,
},
],
};
}
const formattedAlerts = features.map(formatAlert);
const alertsText = `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}`;
return {
content: [
{
type: "text",
text: alertsText,
},
],
};
},
);
/**
* MCPのツールに任意の緯度・経度における天気予報を取得する機能を登録
*/
server.tool(
"get-forecast", // ツール名
"Get weather forecast for a location", // ツールの説明
{
// 緯度と経度を入力パラメータとして受け取る
latitude: z.number().min(-90).max(90).describe("Latitude of the location"),
longitude: z.number().min(-180).max(180).describe("Longitude of the location"),
},
async ({ latitude, longitude }) => {
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest<PointsResponse>(pointsUrl);
if (!pointsData) {
return {
content: [
{
type: "text",
text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`,
},
],
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [
{
type: "text",
text: "Failed to get forecast URL from grid point data",
},
],
};
}
const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
if (!forecastData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve forecast data",
},
],
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [
{
type: "text",
text: "No forecast periods available",
},
],
};
}
// 各期間ごとの予報を整形
const formattedForecast = periods.map((period: ForecastPeriod) =>
[
`${period.name || "Unknown"}:`,
`Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`,
`Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`,
`${period.shortForecast || "No forecast available"}`,
"---",
].join("\n"),
);
const forecastText = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`;
return {
content: [
{
type: "text",
text: forecastText,
},
],
};
},
);
/**
* MCPサーバのメイン処理
*/
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
// メイン処理の実行
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
これで作成が完了したので、最後にビルドを実行します。
npm run build
buildディレクトリ内にindex.jsファイルが生成されたらOKです。
Claudeの設定
上記で作成したMCPサーバを、Claudeのデスクトップアプリで使えるように設定します。
過去にMCPサーバを使ったことがある場合、claude_desktop_config.jsonを変更します。
使ったことがない場合はおそらくclaude_desktop_config.jsonのファイルがないので、過去に投稿したClaudeでMCPを使用する記事を参考にclaude_desktop_config.jsonのファイルを作成してください。
claude_desktop_config.jsonの内容を以下のようにします。
{
"mcpServers": {
"weather": {
"command": "node",
"args": [
"\\プロジェクトディレクトリのパス\\build\\index.js"
]
}
}
}
これでClaudeの再起動を行い、ハンマーのアイコンが表示されていればOKです。
ただClaudeでMCPを使用する記事の時と同様、今回も「spawn node ENOENT」というエラーが出て最初はうまく設定されませんでした。
対応方法としては前回と同じで、”command”: “node” のnodeをフルパスに変更することでアイコンが表示されました。
ハンマーのアイコンをクリックすると、最初に紹介した「get-alerts」と「get-forecast」というツールを使用できることが確認できます。
実際に動作を試してみます。
「サクラメントの天気はどうですか?」と質問してみます。
以下のように「get-forecast」のツールの利用許可の確認が表示されました。
許可を出すと、サクラメントの天気の回答を確認できました。




コメントが承認されるまで時間がかかります。