10 changed files with 286 additions and 25 deletions
@ -0,0 +1,58 @@ |
|||||
|
import express from "express"; |
||||
|
import { |
||||
|
ResourceTemplate, |
||||
|
McpServer, |
||||
|
} from "@modelcontextprotocol/sdk/server/mcp.js"; |
||||
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; |
||||
|
import { z } from "zod"; |
||||
|
|
||||
|
const server = new McpServer({ |
||||
|
name: "Echo", |
||||
|
version: "1.0.0", |
||||
|
}); |
||||
|
|
||||
|
server.resource( |
||||
|
"echo", |
||||
|
new ResourceTemplate("echo://{message}", { list: undefined }), |
||||
|
async (uri, { message }) => ({ |
||||
|
contents: [ |
||||
|
{ |
||||
|
uri: uri.href, |
||||
|
text: `Resource echo: ${message}`, |
||||
|
}, |
||||
|
], |
||||
|
}) |
||||
|
); |
||||
|
|
||||
|
server.tool("echo", { message: z.string() }, async ({ message }) => ({ |
||||
|
content: [{ type: "text", text: `Tool echo: ${message}` }], |
||||
|
})); |
||||
|
|
||||
|
server.prompt("echo", { message: z.string() }, ({ message }) => ({ |
||||
|
messages: [ |
||||
|
{ |
||||
|
role: "user", |
||||
|
content: { |
||||
|
type: "text", |
||||
|
text: `Please process this message: ${message}`, |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
})); |
||||
|
|
||||
|
const app = express(); |
||||
|
let transport |
||||
|
app.get("/sse", async (req, res) => { |
||||
|
transport = new SSEServerTransport("/messages", res); |
||||
|
await server.connect(transport); |
||||
|
}); |
||||
|
|
||||
|
app.post("/messages", async (req, res) => { |
||||
|
// Note: to support multiple simultaneous connections, these messages will
|
||||
|
// need to be routed to a specific matching transport. (This logic isn't
|
||||
|
// implemented here, for simplicity.)
|
||||
|
await transport.handlePostMessage(req, res); |
||||
|
}); |
||||
|
|
||||
|
app.listen(3001); |
||||
|
console.log("Server running on http://localhost:3001"); |
@ -0,0 +1,29 @@ |
|||||
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; |
||||
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; |
||||
|
|
||||
|
const transport = new StdioClientTransport({ |
||||
|
command: "node", |
||||
|
args: ["./server.js"], |
||||
|
cwd: process.cwd(), |
||||
|
}); |
||||
|
|
||||
|
const client = new Client( |
||||
|
{ |
||||
|
name: "weather-client", |
||||
|
version: "0.0.1", |
||||
|
}, |
||||
|
{ |
||||
|
capabilities: { |
||||
|
prompts: {}, |
||||
|
resources: {}, |
||||
|
tools: {}, |
||||
|
}, |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
try { |
||||
|
await client.connect(transport); |
||||
|
console.log("Connected successfully"); |
||||
|
} catch (error) { |
||||
|
console.error("Failed to connect:", error); |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
import express from "express"; |
||||
|
import { |
||||
|
ResourceTemplate, |
||||
|
McpServer, |
||||
|
} from "@modelcontextprotocol/sdk/server/mcp.js"; |
||||
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; |
||||
|
import { z } from "zod"; |
||||
|
|
||||
|
const server = new McpServer( |
||||
|
{ |
||||
|
name: "天气工具包", |
||||
|
version: "1.0.0", |
||||
|
}, |
||||
|
{ |
||||
|
capabilities: { |
||||
|
tools: {}, |
||||
|
}, |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
server.tool("get-weather", { state: z.string() }, async ({ state }) => { |
||||
|
const url = `https://cn.apihz.cn/api/tianqi/tqyb.php?id=88888888&key=88888888&sheng=四川&place=绵阳`; |
||||
|
const data = await fetch(url).then((res) => res.json()); |
||||
|
|
||||
|
if (!data) { |
||||
|
return "无法获取天气"; |
||||
|
} |
||||
|
|
||||
|
return data; |
||||
|
}); |
||||
|
|
||||
|
const app = express(); |
||||
|
let transport; |
||||
|
app.get("/sse", async (req, res) => { |
||||
|
transport = new SSEServerTransport("/messages", res); |
||||
|
await server.connect(transport); |
||||
|
}); |
||||
|
|
||||
|
app.post("/messages", async (req, res) => { |
||||
|
// Note: to support multiple simultaneous connections, these messages will
|
||||
|
// need to be routed to a specific matching transport. (This logic isn't
|
||||
|
// implemented here, for simplicity.)
|
||||
|
await transport.handlePostMessage(req, res); |
||||
|
}); |
||||
|
|
||||
|
app.listen(3001); |
||||
|
console.log("Server running on http://localhost:3001"); |
@ -0,0 +1,76 @@ |
|||||
|
import { |
||||
|
McpServer, |
||||
|
ResourceTemplate, |
||||
|
} from "@modelcontextprotocol/sdk/server/mcp.js"; |
||||
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; |
||||
|
import { |
||||
|
makeNWSRequest, |
||||
|
formatAlert, |
||||
|
NWS_API_BASE, |
||||
|
USER_AGENT, |
||||
|
} from "./util.js"; |
||||
|
import { z } from "zod"; |
||||
|
|
||||
|
const server = new McpServer({ |
||||
|
name: "天气工具包", |
||||
|
version: "0.0.1", |
||||
|
}); |
||||
|
|
||||
|
server.tool("获取天气", { state: z.string() }, async ({ state }) => { |
||||
|
const url = `https://cn.apihz.cn/api/tianqi/tqyb.php?id=88888888&key=88888888&sheng=四川&place=绵阳`; |
||||
|
const data = await fetch(url).then(res => res.json()); |
||||
|
|
||||
|
if (!data) { |
||||
|
return "无法获取天气"; |
||||
|
} |
||||
|
|
||||
|
return data; |
||||
|
}); |
||||
|
|
||||
|
// server.tool({
|
||||
|
// name: "get-forecast",
|
||||
|
// description: "获取位置的天气预报",
|
||||
|
// parameters: {
|
||||
|
// latitude: {
|
||||
|
// type: "number",
|
||||
|
// description: "位置的纬度",
|
||||
|
// },
|
||||
|
// longitude: {
|
||||
|
// type: "number",
|
||||
|
// description: "位置的经度",
|
||||
|
// },
|
||||
|
// },
|
||||
|
// async execute({ latitude, longitude }) {
|
||||
|
// // 首先获取预报网格端点
|
||||
|
// const pointsUrl = `${NWS_API_BASE}/points/${latitude},${longitude}`;
|
||||
|
// const pointsData = await makeNWSRequest(pointsUrl);
|
||||
|
|
||||
|
// if (!pointsData) {
|
||||
|
// return "无法为此位置获取预报数据。";
|
||||
|
// }
|
||||
|
|
||||
|
// // 从点响应中获取预报URL
|
||||
|
// const forecastUrl = pointsData.properties.forecast;
|
||||
|
// const forecastData = await makeNWSRequest(forecastUrl);
|
||||
|
|
||||
|
// if (!forecastData) {
|
||||
|
// return "无法获取详细预报。";
|
||||
|
// }
|
||||
|
|
||||
|
// // 将时段格式化为可读预报
|
||||
|
// const periods = forecastData.properties.periods;
|
||||
|
// const forecasts = periods.slice(0, 5).map(
|
||||
|
// (period) => `
|
||||
|
// ${period.name}:
|
||||
|
// Temperature: ${period.temperature}°${period.temperatureUnit}
|
||||
|
// Wind: ${period.windSpeed} ${period.windDirection}
|
||||
|
// Forecast: ${period.detailedForecast}
|
||||
|
// `
|
||||
|
// );
|
||||
|
|
||||
|
// return forecasts.join("\n---\n");
|
||||
|
// },
|
||||
|
// });
|
||||
|
|
||||
|
const transport = new StdioServerTransport(); |
||||
|
await server.connect(transport); |
@ -0,0 +1,4 @@ |
|||||
|
|
||||
|
fetch("https://cn.apihz.cn/api/tianqi/tqyb.php?id=88888888&key=88888888&sheng=四川&place=绵阳").then(res => res.json()).then(data => { |
||||
|
console.log(data) |
||||
|
}) |
@ -0,0 +1,28 @@ |
|||||
|
// 常量
|
||||
|
export const NWS_API_BASE = "https://api.weather.gov"; |
||||
|
export const USER_AGENT = "weather-app/1.0"; |
||||
|
|
||||
|
export async function makeNWSRequest(url) { |
||||
|
const headers = { |
||||
|
// "User-Agent": USER_AGENT,
|
||||
|
// Accept: "application/geo+json",
|
||||
|
}; |
||||
|
try { |
||||
|
const response = await fetch(url); |
||||
|
if (!response.ok) throw new Error("API request failed"); |
||||
|
return await response.json(); |
||||
|
} catch (error) { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function formatAlert(feature) { |
||||
|
const props = feature.properties; |
||||
|
return ` |
||||
|
Event: ${props.event || "Unknown"} |
||||
|
Area: ${props.areaDesc || "Unknown"} |
||||
|
Severity: ${props.severity || "Unknown"} |
||||
|
Description: ${props.description || "No description available"} |
||||
|
Instructions: ${props.instruction || "No specific instructions provided"} |
||||
|
`;
|
||||
|
} |
Loading…
Reference in new issue