Skip to main content
Glama

Weather & WordPress MCP Server

by hafizrahman
index.ts9.15 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; const NWS_API_BASE = "https://api.weather.gov"; const WP_COM_API_BASE = "https://public-api.wordpress.com/wp/v2/sites/hafiz.blog"; const USER_AGENT = "wp-mcp/1.0"; // Create server instance const server = new McpServer({ name: "wp-mcp", version: "1.0.0", capabilities: { resources: {}, tools: {}, }, }); // --- Shared helpers --- async function fetchJson<T>(url: string, headers: Record<string, string> = {}): Promise<T | null> { try { const response = await fetch(url, { headers: { "User-Agent": USER_AGENT, ...headers } }); if (!response.ok) throw new Error(`HTTP error ${response.status}`); return (await response.json()) as T; } catch (err) { console.error("Fetch error:", err); return null; } } // --- NWS (Weather) functionality --- 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"); } interface ForecastPeriod { name?: string; temperature?: number; temperatureUnit?: string; windSpeed?: string; windDirection?: string; shortForecast?: string; } interface AlertsResponse { features: AlertFeature[]; } interface PointsResponse { properties: { forecast?: string; }; } interface ForecastResponse { properties: { periods: ForecastPeriod[]; }; } server.tool( "get-alerts", "Get weather alerts for a state", { 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 fetchJson<AlertsResponse>(alertsUrl, { Accept: "application/geo+json", }); 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); return { content: [{ type: "text", text: `Active alerts for ${stateCode}:\n\n${formattedAlerts.join("\n")}` }], }; } ); 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 fetchJson<PointsResponse>(pointsUrl, { Accept: "application/geo+json", }); if (!pointsData || !pointsData.properties?.forecast) { return { content: [ { type: "text", text: `Failed to get forecast data for coordinates: ${latitude}, ${longitude}`, }, ], }; } const forecastData = await fetchJson<ForecastResponse>(pointsData.properties.forecast); 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) => [ `${period.name || "Unknown"}:`, `Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`, `Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`, `${period.shortForecast || "No forecast available"}`, "---", ].join("\n") ); return { content: [{ type: "text", text: `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}` }], }; } ); // --- WordPress functionality --- interface WPPost { id: number; title: { rendered: string }; link: string; date: string; excerpt: { rendered: string }; } function stripHTML(html: string): string { return html.replace(/<\/?[^>]+(>|$)/g, "").replace(/&nbsp;/g, " "); } server.tool( "get-latest-posts", "Get the 10 most recent posts from hafiz.blog (WordPress.com)", {}, async () => { const postsUrl = `${WP_COM_API_BASE}/posts?per_page=10&_fields=id,title,link,date,excerpt`; const posts = await fetchJson<WPPost[]>(postsUrl); if (!posts || posts.length === 0) { return { content: [{ type: "text", text: "No posts found or failed to fetch." }], }; } const formattedPosts = posts.map((post) => [ `Title: ${post.title.rendered}`, `Date: ${new Date(post.date).toLocaleString()}`, `Link: ${post.link}`, `Excerpt: ${stripHTML(post.excerpt.rendered).slice(0, 200)}...`, "---", ].join("\n") ); return { content: [{ type: "text", text: `Latest posts from hafiz.blog:\n\n${formattedPosts.join("\n")}` }], }; } ); interface WPCategory { id: number; name: string; slug: string; } server.tool( "get-categories", "Get all categories available on hafiz.blog (WordPress.com)", {}, async () => { const categoriesUrl = `${WP_COM_API_BASE}/categories?per_page=50&_fields=id,name,slug`; const categories = await fetchJson<WPCategory[]>(categoriesUrl); if (!categories || categories.length === 0) { return { content: [{ type: "text", text: "No categories found" }], }; } const formattedCategories = categories.map((category) => `Category: ${category.name} (Slug: ${category.slug})` ); return { content: [ { type: "text", text: `Categories on hafiz.blog:\n\n${formattedCategories.join("\n")}`, }, ], }; } ); server.tool( "get-posts-by-category", "Get posts from a specific category on hafiz.blog (WordPress.com)", { categorySlug: z.string().describe("Slug of the category (e.g., 'technology', 'life')"), }, async ({ categorySlug }) => { // Get category ID using the category slug const categoriesUrl = `${WP_COM_API_BASE}/categories?slug=${categorySlug}`; const categories = await fetchJson<WPCategory[]>(categoriesUrl); if (!categories || categories.length === 0) { return { content: [{ type: "text", text: `No category found with slug '${categorySlug}'` }], }; } // Fetch posts based on the category ID const categoryId = categories[0].id; const postsUrl = `${WP_COM_API_BASE}/posts?categories=${categoryId}&per_page=10&_fields=id,title,link,date,excerpt`; const posts = await fetchJson<WPPost[]>(postsUrl); if (!posts || posts.length === 0) { return { content: [{ type: "text", text: `No posts found in category '${categorySlug}'` }], }; } const formattedPosts = posts.map((post) => [ `Title: ${post.title.rendered}`, `Date: ${new Date(post.date).toLocaleString()}`, `Link: ${post.link}`, `Excerpt: ${stripHTML(post.excerpt.rendered).slice(0, 200)}...`, "---", ].join("\n") ); return { content: [{ type: "text", text: `Posts in category '${categorySlug}':\n\n${formattedPosts.join("\n")}` }], }; } ); // --- Start server --- async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("WP + Weather MCP Server running on stdio"); } main().catch((err) => { console.error("Fatal error in main():", err); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hafizrahman/wp-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server