enrico-mcp 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.js CHANGED
@@ -115,3 +115,54 @@ export async function countryCoverage(_args = {}) {
115
115
  lines.push(" the countries tool prints only the code and the name and stops at 30 of them, so none of this is visible there.");
116
116
  return lines.join("\n");
117
117
  }
118
+
119
+ const HOLIDAY_TYPES = ['public_holiday', 'observance', 'other_day', 'extra_working_day', 'school_holiday', 'postal_holiday'];
120
+ const ENRICO_BASE = 'https://kayaposoft.com/enrico/json/v2.0';
121
+ async function enrico(action, params) {
122
+ const qs = new URLSearchParams({ action, ...params });
123
+ const res = await fetch(`${ENRICO_BASE}/?${qs}`, {
124
+ headers: { 'User-Agent': 'mrfentmen-enrico-mcp/1.0', Accept: 'application/json' },
125
+ signal: AbortSignal.timeout(25000),
126
+ });
127
+ if (!res.ok)
128
+ throw new Error(`Enrico returned ${res.status}`);
129
+ const d = (await res.json());
130
+ if (d && !Array.isArray(d) && d.error)
131
+ throw new Error(`Enrico error: ${d.error}`);
132
+ return d;
133
+ }
134
+ export async function countryCalendar(args = {}) {
135
+ const country = String(args?.country ?? '').trim().toLowerCase();
136
+ if (!/^[a-z]{3}$/.test(country))
137
+ throw new Error('Use a three letter country code like USA or DEU.');
138
+ const year = Number(args?.year);
139
+ if (!Number.isInteger(year) || year < 1940 || year > 2100)
140
+ throw new Error('Year must be a whole number between 1940 and 2100.');
141
+ const countries = await enrico('getSupportedCountries', {});
142
+ const info = (Array.isArray(countries) ? countries : []).find((c) => String(c.countryCode).toLowerCase() === country);
143
+ if (!info)
144
+ throw new Error(`Enrico has no data for "${args?.country}".`);
145
+ const types = (info.holidayTypes ?? []).filter((t) => HOLIDAY_TYPES.includes(t));
146
+ const days = [];
147
+ for (const type of types) {
148
+ const rows = await enrico('getHolidaysForYear', { year: String(year), country: info.countryCode, holidayType: type });
149
+ for (const row of Array.isArray(rows) ? rows : []) {
150
+ const en = (row.name ?? []).find((n) => n.lang === 'en') ?? row.name?.[0];
151
+ days.push({
152
+ date: row.date,
153
+ text: en?.text ?? '(unnamed)',
154
+ type: row.holidayType ?? type,
155
+ });
156
+ }
157
+ }
158
+ const from = info.fromDate ? `${info.fromDate.year}` : '?';
159
+ const to = info.toDate && info.toDate.year < 9999 ? `${info.toDate.year}` : 'open ended';
160
+ const regions = (info.regions ?? []).length ? info.regions.join(', ') : 'whole country';
161
+ const header = `${info.fullName ?? country.toUpperCase()} — ${year}`;
162
+ const coverage = `Coverage: ${from} to ${to} · regions: ${regions} · types: ${types.join(', ') || 'none'}`;
163
+ if (!days.length)
164
+ return [header, coverage, '', `No holidays of the covered types are on record for ${year}.`].join('\n');
165
+ days.sort((a, b) => a.date.month - b.date.month || a.date.day - b.date.day);
166
+ const lines = days.map((d) => `- ${String(d.date.day).padStart(2, '0')}/${String(d.date.month).padStart(2, '0')} (weekday ${d.date.dayOfWeek}) ${d.text} [${d.type}]`);
167
+ return [header, coverage, `${days.length} day(s) across ${new Set(days.map((d) => d.type)).size} type(s):`, '', ...lines.slice(0, 200)].join('\n');
168
+ }
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
- import { countries, holidays, countryCoverage } from "./api.js";
3
+ import { countries, holidays, countryCoverage, countryCalendar } from "./api.js";
4
4
  const text = (value) => ({ content: [{ type: "text", text: value }] });
5
5
  const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
6
6
  const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
@@ -52,9 +52,8 @@ export function createServer() {
52
52
  inputSchema: z.object({ country: z.string().describe("Three letter country code like USA or DEU."), year: z.number().describe("Year, 1940 to 2100.") }),
53
53
  annotations: READ_ONLY,
54
54
  }, async (args) => {
55
- // TODO: implement calendar locally (paywall removed)
56
55
  try {
57
- return textError("Not yet implemented: calendar");
56
+ return text(await countryCalendar(args));
58
57
  }
59
58
  catch (e) {
60
59
  return textError(String(e));
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.4",
2
+ "version": "1.0.5",
3
3
  "type": "module",
4
4
  "repository": {
5
5
  "type": "git",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/mrfentmen/enrico-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.0.1",
9
+ "version": "1.0.5",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "enrico-mcp",
14
- "version": "1.0.1",
14
+ "version": "1.0.5",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }