UNPKG

@rfdez/pvpc-mcp-server

Version:

Fetch the Voluntary Price for the Small Consumer (PVPC) published daily by Red Eléctrica at 8:15 p.m. This includes the hourly electricity tariffs that will apply the following day for consumers billed under the 2.0 TD tariff.

112 lines (111 loc) 5.24 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { endOfToday, startOfToday } from "date-fns"; import * as z from "zod"; import { PvpcError } from "./error.js"; import { PvpcApiClient } from "./pvpc.js"; export class PvpcMcpServer { apiClient; server; constructor(apiClient) { this.apiClient = apiClient; this.server = new McpServer({ name: "pvpc-mcp-server", title: "Voluntary Price for the Small Consumer (PVPC) MCP Server", version: "2.1.0", }, { instructions: "Use this server to fetch the Voluntary Price for the Small Consumer (PVPC) for consumers billed under the 2.0 TD tariff.", }); this.registerTools(); } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); } async stop() { await this.server.close(); } registerTools() { this.server.registerTool("fetch_prices", { title: "Fetch PVPC Prices", description: "Fetches the Voluntary Price for the Small Consumer (PVPC) prices for a given date range and geographical area.", inputSchema: { locale: z .enum(["es", "en"]) .default("es") .describe("Get translations for sources. Accepted values: `es`, `en`. Defaults to `es`."), startDate: z .string() .default(startOfToday().toISOString()) .describe("Beginning of the date range to filter indicator values in iso8601 format. E.g. 2025-06-29T00:00:00.000+02:00. Defaults to the start of today."), endDate: z .string() .default(endOfToday().toISOString()) .describe("End of the date range to filter indicator values in iso8601 format. E.g. 2025-06-29T23:59:59.999+02:00. Defaults to the end of today."), timeAggregation: z .enum(["sum", "average"]) .default("sum") .describe("How to aggregate indicator values when grouping them by time. Accepted values: `sum`, `average`. Defaults to `sum`."), timeTruncation: z .enum(["hour", "day", "month", "year"]) .optional() .describe("Tells how to truncate data time series. Accepted values: `hour`, `day`, `month`, `year`."), geographicalAggregation: z .enum(["sum", "average"]) .default("sum") .describe("How to aggregate indicator values when grouping them by geographical ID. Accepted values: `sum`, `average`. Defaults to `sum`."), geographicalIds: z .array(z.number()) .default([8741, 8742, 8743, 8744, 8745]) .describe("Tells the geographical IDs to filter indicator values. Accepted values: `3` (España), `8741` (Península), `8742` (Canarias), `8743` (Baleares), `8744` (Ceuta), `8745` (Melilla). Defaults to `8741`, `8742`, `8743`, `8744`, `8745`."), geographicalTruncation: z .enum(["country", "electric_system"]) .optional() .describe("Tells how to group data at geographical level when the geographical aggregation is informed. Accepted values: `country`, `electric_system`."), }, }, async ({ locale, startDate, endDate, timeAggregation, timeTruncation, geographicalAggregation, geographicalIds, geographicalTruncation, }) => { try { const prices = await this.apiClient.fetchPrices({ locale, startDate, endDate, timeAggregation, timeTruncation, geographicalAggregation, geographicalIds, geographicalTruncation, }); return { content: [ { type: "text", text: JSON.stringify(prices, null, 2), }, ], }; } catch (error) { if (error instanceof PvpcError) { return { content: [ { type: "text", text: `Failed to fetch prices: ${error.name} - ${error.message}\nStatus: ${error.status}`, }, ], isError: true, }; } return { content: [ { type: "text", text: "Failed to fetch prices: Unknown error", }, ], isError: true, }; } }); } }