Last active
April 12, 2025 08:03
-
-
Save yamanoku/4f31453747b1517798fb50bf965193d0 to your computer and use it in GitHub Desktop.
Baselineの情報を教えてくれるMCPサーバー
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// mcp-baseline-server.ts | |
import { McpServer } from "npm:@modelcontextprotocol/sdk/server/mcp.js"; | |
import { StdioServerTransport } from "npm:@modelcontextprotocol/sdk/server/stdio.js"; | |
import { z } from "npm:zod@^3.24.2"; | |
import process from "node:process"; | |
import type { WebFeature, WebFeatureResponse } from "./types.ts"; | |
const APIURL = "https://api.webstatus.dev/v1/features"; | |
// Baselineのカテゴリを判定する関数 | |
const determineBaselineCategory = (status: string): string | undefined => { | |
if (!status) return undefined; | |
if (status.includes("baseline")) { | |
if (status.includes("widely")) { | |
return "Widely"; | |
} else if (status.includes("newly")) { | |
return "Newly"; | |
} else if (status.includes("limited")) { | |
return "Limited"; | |
} | |
} | |
return undefined; | |
}; | |
// Web Platform APIからフィーチャー情報を取得する関数 | |
const getFeatureStatus = async ( | |
query: string | |
): Promise<WebFeature[] | undefined> => { | |
try { | |
const encodedQuery = encodeURIComponent(query); | |
const url = `${APIURL}?q=${encodedQuery}`; | |
const controller = new AbortController(); | |
const timeoutId = setTimeout(() => controller.abort(), 5000); | |
const response = await fetch(url, { | |
signal: controller.signal, | |
}); | |
clearTimeout(timeoutId); | |
if (!response.ok) { | |
throw new Error(`API request failed with status ${response.status}`); | |
} | |
const responseData: WebFeatureResponse = await response.json(); | |
const features = responseData.data; | |
// Baselineカテゴリを追加 | |
return features.map((feature) => ({ | |
...feature, | |
baselineCategory: determineBaselineCategory(feature.baseline.status), | |
})); | |
} catch (error) { | |
console.error(error); | |
} | |
}; | |
// MCPサーバーの初期化 | |
const server = new McpServer({ | |
name: "Baseline MCP Server", | |
version: "0.1.0", | |
capabilities: { | |
resource: {}, | |
tools: {}, | |
}, | |
}); | |
server.tool( | |
"getWebFeaturesApi", | |
"クエリを指定し、Web Platform Dashboardからfeatureの結果を取得します", | |
{ | |
query: z.string().describe("調べたいWeb APIの名前"), | |
}, | |
async ({ query }: { query: string }) => { | |
try { | |
// フィーチャー情報を取得 | |
const features = await getFeatureStatus(query); | |
if (features === undefined || features.length === 0) { | |
return { | |
content: [ | |
{ | |
type: "text", | |
text: `「${query}」に関する情報は見つかりませんでした。別の機能名で試してみてください。`, | |
}, | |
], | |
}; | |
} | |
// Baselineカテゴリのリストを作成 | |
const categories = features | |
.map((f) => f.baseline || "Baseline外") | |
.filter((value, index, self) => self.indexOf(value) === index); // 重複を削除 | |
// レスポンスを構築 | |
const categoryInfo = { | |
widely: | |
"広くサポートされているWeb標準機能です。ほとんどのブラウザで安全に使用できます。", | |
newly: | |
"新しく標準化されたWeb機能です。主要なブラウザでサポートされ始めていますが、まだ普及途上です。", | |
limited: | |
"限定的にサポートされているWeb機能です。一部のブラウザでは使用できないか、フラグが必要な場合があります。", | |
Baseline外: | |
"現時点ではBaselineに含まれていないWeb機能です。ブラウザのサポート状況を個別に確認する必要があります。", | |
}; | |
// 結果を整形して応答を構築 | |
const baselineInfo = features | |
.map( | |
(feature) => | |
`${feature.name}: ${feature.baseline?.status || "Baseline外"}` | |
) | |
.join("\n- "); | |
const categoryDescriptions = categories | |
.filter((category) => categoryInfo[category.status]) | |
.map((category) => `- ${query}: ${categoryInfo[category.status]}`) | |
.join("\n"); | |
const response = [ | |
categoryDescriptions, | |
features.length > 1 ? `\n具体的な機能:\n- ${baselineInfo}` : "", | |
] | |
.join("\n") | |
.trim(); | |
return { | |
content: [ | |
{ | |
type: "text", | |
text: response, | |
}, | |
], | |
}; | |
} catch (error) { | |
console.error("Error processing event:", error); | |
return { | |
content: [ | |
{ | |
type: "text", | |
text: "Web機能の情報取得中にエラーが発生しました。しばらく経ってから再試行してください。", | |
}, | |
], | |
}; | |
} | |
} | |
); | |
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); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Status = "widely" | "limited" | "newly" | "Baseline外"; | |
type BrowserName = "chrome" | "edge" | "firefox" | "safari"; | |
type Baseline = { | |
high_date?: string; | |
low_date?: string; | |
status: Status; | |
}; | |
type BrowserImplementation = { | |
date: string; | |
status: Status; | |
version: string; | |
}; | |
type BrowserImplementations = { | |
[key in BrowserName]: BrowserImplementation; | |
}; | |
type Spec = { | |
links: { | |
link: string; | |
}; | |
}; | |
type BrowserUsage = { | |
daily: number; | |
}; | |
type Usage = { | |
[key in BrowserName]?: BrowserUsage; | |
}; | |
type BrowserWptScore = { | |
score: number; | |
metadata?: { | |
status: Status; | |
}; | |
}; | |
type WptEnvironment = { | |
[key in BrowserName]: BrowserWptScore; | |
}; | |
type Wpt = { | |
experimental: WptEnvironment; | |
stable: WptEnvironment; | |
}; | |
export type WebFeature = { | |
baseline: Baseline; | |
browser_implementations: BrowserImplementations; | |
feature_id: string; | |
name: string; | |
spec: Spec; | |
usage: Usage; | |
wpt: Wpt; | |
}; | |
export type WebFeatureResponse = { | |
data: WebFeature[] | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment