weather-agent-mcp 0.1.5 → 0.1.6
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/README.md +1 -1
- package/dist/weather-mcp.js +345 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ sudo launchctl config user path "$HOME/.volta/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
|
58
58
|
|
|
59
59
|
Без `sudo`: можно `launchctl setenv PATH "$HOME/.volta/bin:/usr/bin:/bin:/usr/sbin:/sbin"` и перезапустить Cursor (сбросится после reboot).
|
|
60
60
|
|
|
61
|
-
Reload MCP. Tools: `geocode`, `getCurrentWeather`, `getForecast`, `getHistoricalWeather`, `compareWeather`.
|
|
61
|
+
Reload MCP. Tools: `geocode`, `getCurrentWeather`, `getForecast`, `getHistoricalWeather`, `compareWeather`. Для ряда температур (прогноз, архив одного дня по годам, сравнение) к ответу прилагается PNG-график.
|
|
62
62
|
|
|
63
63
|
### GitHub Copilot / VS Code
|
|
64
64
|
|
package/dist/weather-mcp.js
CHANGED
|
@@ -25217,12 +25217,12 @@ var ReadBuffer = class {
|
|
|
25217
25217
|
constructor(options) {
|
|
25218
25218
|
this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
|
|
25219
25219
|
}
|
|
25220
|
-
append(
|
|
25221
|
-
if ((this._buffer?.length ?? 0) +
|
|
25220
|
+
append(chunk2) {
|
|
25221
|
+
if ((this._buffer?.length ?? 0) + chunk2.length > this._maxBufferSize) {
|
|
25222
25222
|
this.clear();
|
|
25223
25223
|
throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
|
|
25224
25224
|
}
|
|
25225
|
-
this._buffer = this._buffer ? Buffer.concat([this._buffer,
|
|
25225
|
+
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk2]) : chunk2;
|
|
25226
25226
|
}
|
|
25227
25227
|
readMessage() {
|
|
25228
25228
|
while (this._buffer) {
|
|
@@ -33905,9 +33905,9 @@ var StdioServerTransport = class {
|
|
|
33905
33905
|
onclose;
|
|
33906
33906
|
onerror;
|
|
33907
33907
|
onmessage;
|
|
33908
|
-
_ondata = (
|
|
33908
|
+
_ondata = (chunk2) => {
|
|
33909
33909
|
try {
|
|
33910
|
-
this._readBuffer.append(
|
|
33910
|
+
this._readBuffer.append(chunk2);
|
|
33911
33911
|
this.processReadBuffer();
|
|
33912
33912
|
} catch (error62) {
|
|
33913
33913
|
this.onerror?.(error62);
|
|
@@ -34956,6 +34956,293 @@ async function compareWeather(locations, startDate, endDate, config2 = {}) {
|
|
|
34956
34956
|
return { startDate, endDate, locations: results };
|
|
34957
34957
|
}
|
|
34958
34958
|
|
|
34959
|
+
// src/weather/temperature-chart.ts
|
|
34960
|
+
import { deflateSync } from "node:zlib";
|
|
34961
|
+
var CHART_WIDTH = 720;
|
|
34962
|
+
var CHART_HEIGHT = 360;
|
|
34963
|
+
var BG = [247, 244, 239];
|
|
34964
|
+
var PLOT = [255, 252, 247];
|
|
34965
|
+
var GRID = [220, 214, 204];
|
|
34966
|
+
var INK = [70, 64, 56];
|
|
34967
|
+
var SERIES_COLORS = [
|
|
34968
|
+
[196, 90, 58],
|
|
34969
|
+
[47, 111, 163],
|
|
34970
|
+
[62, 122, 90],
|
|
34971
|
+
[140, 90, 160],
|
|
34972
|
+
[176, 132, 48]
|
|
34973
|
+
];
|
|
34974
|
+
var PAD_L = 56;
|
|
34975
|
+
var PAD_R = 20;
|
|
34976
|
+
var PAD_T = 40;
|
|
34977
|
+
var PAD_B = 44;
|
|
34978
|
+
function pickAxisLabels(count, maxLabels = 6) {
|
|
34979
|
+
if (count <= 0) return [];
|
|
34980
|
+
if (count <= maxLabels) return Array.from({ length: count }, (_, i) => i);
|
|
34981
|
+
const indices = /* @__PURE__ */ new Set([0, count - 1]);
|
|
34982
|
+
const inner = maxLabels - 1;
|
|
34983
|
+
for (let i = 1; i < inner; i++) {
|
|
34984
|
+
indices.add(Math.round(i * (count - 1) / inner));
|
|
34985
|
+
}
|
|
34986
|
+
return [...indices].sort((a, b) => a - b);
|
|
34987
|
+
}
|
|
34988
|
+
function renderTemperatureChart(labels, series) {
|
|
34989
|
+
if (labels.length < 2 || series.length === 0) return null;
|
|
34990
|
+
const aligned = series.map((s) => ({ name: s.name, values: s.values.slice(0, labels.length) })).filter((s) => finiteCount(s.values) >= 2);
|
|
34991
|
+
if (aligned.length === 0) return null;
|
|
34992
|
+
const finite = aligned.flatMap((s) => s.values.filter((v) => isFiniteNumber(v)));
|
|
34993
|
+
let yMin = Math.min(...finite);
|
|
34994
|
+
let yMax = Math.max(...finite);
|
|
34995
|
+
if (yMax - yMin < 1) {
|
|
34996
|
+
yMin -= 1;
|
|
34997
|
+
yMax += 1;
|
|
34998
|
+
} else {
|
|
34999
|
+
const pad = (yMax - yMin) * 0.08;
|
|
35000
|
+
yMin -= pad;
|
|
35001
|
+
yMax += pad;
|
|
35002
|
+
}
|
|
35003
|
+
const pixels = new Uint8Array(CHART_WIDTH * CHART_HEIGHT * 3);
|
|
35004
|
+
fill(pixels, BG);
|
|
35005
|
+
const plotW = CHART_WIDTH - PAD_L - PAD_R;
|
|
35006
|
+
const plotH = CHART_HEIGHT - PAD_T - PAD_B;
|
|
35007
|
+
fillRect(pixels, PAD_L, PAD_T, plotW, plotH, PLOT);
|
|
35008
|
+
const yTicks = 4;
|
|
35009
|
+
for (let t = 0; t <= yTicks; t++) {
|
|
35010
|
+
const y = PAD_T + Math.round(plotH * t / yTicks);
|
|
35011
|
+
hLine(pixels, PAD_L, PAD_L + plotW, y, GRID);
|
|
35012
|
+
const value = yMax - (yMax - yMin) * t / yTicks;
|
|
35013
|
+
drawText(pixels, formatTemp(value), 4, y - 4, INK, 1);
|
|
35014
|
+
}
|
|
35015
|
+
vLine(pixels, PAD_L, PAD_T, PAD_T + plotH, INK);
|
|
35016
|
+
hLine(pixels, PAD_L, PAD_L + plotW, PAD_T + plotH, INK);
|
|
35017
|
+
const xIndex = (i) => PAD_L + Math.round(i * plotW / Math.max(labels.length - 1, 1));
|
|
35018
|
+
const yOf = (v) => PAD_T + Math.round(plotH * (1 - (v - yMin) / (yMax - yMin)));
|
|
35019
|
+
for (const index of pickAxisLabels(labels.length)) {
|
|
35020
|
+
const x = xIndex(index);
|
|
35021
|
+
vLine(pixels, x, PAD_T + plotH, PAD_T + plotH + 4, INK);
|
|
35022
|
+
const caption = labels[index] ?? "";
|
|
35023
|
+
drawText(pixels, caption, x - textWidth(caption, 1) / 2, PAD_T + plotH + 8, INK, 1);
|
|
35024
|
+
}
|
|
35025
|
+
aligned.forEach((s, seriesIndex) => {
|
|
35026
|
+
const color = SERIES_COLORS[seriesIndex % SERIES_COLORS.length];
|
|
35027
|
+
let prev = null;
|
|
35028
|
+
for (let i = 0; i < labels.length; i++) {
|
|
35029
|
+
const value = s.values[i];
|
|
35030
|
+
if (!isFiniteNumber(value)) {
|
|
35031
|
+
prev = null;
|
|
35032
|
+
continue;
|
|
35033
|
+
}
|
|
35034
|
+
const point = { x: xIndex(i), y: yOf(value) };
|
|
35035
|
+
if (prev) drawLine(pixels, prev.x, prev.y, point.x, point.y, color);
|
|
35036
|
+
fillRect(pixels, point.x - 2, point.y - 2, 5, 5, color);
|
|
35037
|
+
prev = point;
|
|
35038
|
+
}
|
|
35039
|
+
});
|
|
35040
|
+
let legendX = PAD_L;
|
|
35041
|
+
for (let i = 0; i < aligned.length; i++) {
|
|
35042
|
+
const color = SERIES_COLORS[i % SERIES_COLORS.length];
|
|
35043
|
+
const name = aligned[i].name;
|
|
35044
|
+
fillRect(pixels, legendX, 14, 14, 8, color);
|
|
35045
|
+
drawText(pixels, name, legendX + 18, 12, INK, 2);
|
|
35046
|
+
legendX += 18 + textWidth(name, 2) + 16;
|
|
35047
|
+
}
|
|
35048
|
+
return encodePng(pixels, CHART_WIDTH, CHART_HEIGHT);
|
|
35049
|
+
}
|
|
35050
|
+
function isFiniteNumber(value) {
|
|
35051
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
35052
|
+
}
|
|
35053
|
+
function finiteCount(values) {
|
|
35054
|
+
return values.filter((v) => isFiniteNumber(v)).length;
|
|
35055
|
+
}
|
|
35056
|
+
function formatTemp(value) {
|
|
35057
|
+
const rounded = Math.round(value);
|
|
35058
|
+
return `${rounded}`;
|
|
35059
|
+
}
|
|
35060
|
+
function fill(pixels, color) {
|
|
35061
|
+
for (let i = 0; i < pixels.length; i += 3) {
|
|
35062
|
+
pixels[i] = color[0];
|
|
35063
|
+
pixels[i + 1] = color[1];
|
|
35064
|
+
pixels[i + 2] = color[2];
|
|
35065
|
+
}
|
|
35066
|
+
}
|
|
35067
|
+
function setPixel(pixels, x, y, color) {
|
|
35068
|
+
if (x < 0 || y < 0 || x >= CHART_WIDTH || y >= CHART_HEIGHT) return;
|
|
35069
|
+
const i = (y * CHART_WIDTH + x) * 3;
|
|
35070
|
+
pixels[i] = color[0];
|
|
35071
|
+
pixels[i + 1] = color[1];
|
|
35072
|
+
pixels[i + 2] = color[2];
|
|
35073
|
+
}
|
|
35074
|
+
function fillRect(pixels, x, y, w, h, color) {
|
|
35075
|
+
for (let yy = y; yy < y + h; yy++) {
|
|
35076
|
+
for (let xx = x; xx < x + w; xx++) setPixel(pixels, xx, yy, color);
|
|
35077
|
+
}
|
|
35078
|
+
}
|
|
35079
|
+
function hLine(pixels, x0, x1, y, color) {
|
|
35080
|
+
const from = Math.min(x0, x1);
|
|
35081
|
+
const to = Math.max(x0, x1);
|
|
35082
|
+
for (let x = from; x <= to; x++) setPixel(pixels, x, y, color);
|
|
35083
|
+
}
|
|
35084
|
+
function vLine(pixels, x, y0, y1, color) {
|
|
35085
|
+
const from = Math.min(y0, y1);
|
|
35086
|
+
const to = Math.max(y0, y1);
|
|
35087
|
+
for (let y = from; y <= to; y++) setPixel(pixels, x, y, color);
|
|
35088
|
+
}
|
|
35089
|
+
function drawLine(pixels, x0, y0, x1, y1, color) {
|
|
35090
|
+
let x = x0;
|
|
35091
|
+
let y = y0;
|
|
35092
|
+
const dx = Math.abs(x1 - x0);
|
|
35093
|
+
const sx = x0 < x1 ? 1 : -1;
|
|
35094
|
+
const dy = -Math.abs(y1 - y0);
|
|
35095
|
+
const sy = y0 < y1 ? 1 : -1;
|
|
35096
|
+
let err = dx + dy;
|
|
35097
|
+
for (; ; ) {
|
|
35098
|
+
fillRect(pixels, x - 1, y - 1, 3, 3, color);
|
|
35099
|
+
if (x === x1 && y === y1) break;
|
|
35100
|
+
const e2 = 2 * err;
|
|
35101
|
+
if (e2 >= dy) {
|
|
35102
|
+
err += dy;
|
|
35103
|
+
x += sx;
|
|
35104
|
+
}
|
|
35105
|
+
if (e2 <= dx) {
|
|
35106
|
+
err += dx;
|
|
35107
|
+
y += sy;
|
|
35108
|
+
}
|
|
35109
|
+
}
|
|
35110
|
+
}
|
|
35111
|
+
var FONT = {
|
|
35112
|
+
" ": [0, 0, 0, 0, 0, 0, 0],
|
|
35113
|
+
"-": [0, 0, 0, 31, 0, 0, 0],
|
|
35114
|
+
".": [0, 0, 0, 0, 0, 12, 12],
|
|
35115
|
+
"/": [1, 2, 4, 8, 16, 0, 0],
|
|
35116
|
+
"0": [14, 17, 19, 21, 25, 17, 14],
|
|
35117
|
+
"1": [4, 12, 4, 4, 4, 4, 14],
|
|
35118
|
+
"2": [14, 17, 1, 2, 4, 8, 31],
|
|
35119
|
+
"3": [30, 1, 1, 14, 1, 1, 30],
|
|
35120
|
+
"4": [2, 6, 10, 18, 31, 2, 2],
|
|
35121
|
+
"5": [31, 16, 16, 30, 1, 1, 30],
|
|
35122
|
+
"6": [14, 16, 16, 30, 17, 17, 14],
|
|
35123
|
+
"7": [31, 1, 2, 4, 8, 8, 8],
|
|
35124
|
+
"8": [14, 17, 17, 14, 17, 17, 14],
|
|
35125
|
+
"9": [14, 17, 17, 15, 1, 1, 14],
|
|
35126
|
+
A: [14, 17, 17, 31, 17, 17, 17],
|
|
35127
|
+
B: [30, 17, 17, 30, 17, 17, 30],
|
|
35128
|
+
C: [14, 17, 16, 16, 16, 17, 14],
|
|
35129
|
+
D: [30, 17, 17, 17, 17, 17, 30],
|
|
35130
|
+
E: [31, 16, 16, 30, 16, 16, 31],
|
|
35131
|
+
F: [31, 16, 16, 30, 16, 16, 16],
|
|
35132
|
+
G: [14, 17, 16, 23, 17, 17, 14],
|
|
35133
|
+
H: [17, 17, 17, 31, 17, 17, 17],
|
|
35134
|
+
I: [14, 4, 4, 4, 4, 4, 14],
|
|
35135
|
+
J: [7, 2, 2, 2, 2, 18, 12],
|
|
35136
|
+
K: [17, 18, 20, 24, 20, 18, 17],
|
|
35137
|
+
L: [16, 16, 16, 16, 16, 16, 31],
|
|
35138
|
+
M: [17, 27, 21, 21, 17, 17, 17],
|
|
35139
|
+
N: [17, 25, 21, 19, 17, 17, 17],
|
|
35140
|
+
O: [14, 17, 17, 17, 17, 17, 14],
|
|
35141
|
+
P: [30, 17, 17, 30, 16, 16, 16],
|
|
35142
|
+
Q: [14, 17, 17, 17, 21, 18, 13],
|
|
35143
|
+
R: [30, 17, 17, 30, 20, 18, 17],
|
|
35144
|
+
S: [15, 16, 16, 14, 1, 1, 30],
|
|
35145
|
+
T: [31, 4, 4, 4, 4, 4, 4],
|
|
35146
|
+
U: [17, 17, 17, 17, 17, 17, 14],
|
|
35147
|
+
V: [17, 17, 17, 17, 17, 10, 4],
|
|
35148
|
+
W: [17, 17, 17, 21, 21, 21, 10],
|
|
35149
|
+
X: [17, 17, 10, 4, 10, 17, 17],
|
|
35150
|
+
Y: [17, 17, 10, 4, 4, 4, 4],
|
|
35151
|
+
Z: [31, 1, 2, 4, 8, 16, 31],
|
|
35152
|
+
a: [0, 0, 14, 1, 15, 17, 15],
|
|
35153
|
+
b: [16, 16, 30, 17, 17, 17, 30],
|
|
35154
|
+
c: [0, 0, 14, 16, 16, 17, 14],
|
|
35155
|
+
d: [1, 1, 15, 17, 17, 17, 15],
|
|
35156
|
+
e: [0, 0, 14, 17, 31, 16, 14],
|
|
35157
|
+
f: [6, 8, 28, 8, 8, 8, 8],
|
|
35158
|
+
g: [0, 0, 15, 17, 15, 1, 14],
|
|
35159
|
+
h: [16, 16, 22, 25, 17, 17, 17],
|
|
35160
|
+
i: [4, 0, 12, 4, 4, 4, 14],
|
|
35161
|
+
j: [2, 0, 6, 2, 2, 18, 12],
|
|
35162
|
+
k: [16, 16, 18, 20, 24, 20, 18],
|
|
35163
|
+
l: [12, 4, 4, 4, 4, 4, 14],
|
|
35164
|
+
m: [0, 0, 26, 21, 21, 17, 17],
|
|
35165
|
+
n: [0, 0, 22, 25, 17, 17, 17],
|
|
35166
|
+
o: [0, 0, 14, 17, 17, 17, 14],
|
|
35167
|
+
p: [0, 0, 30, 17, 30, 16, 16],
|
|
35168
|
+
q: [0, 0, 15, 17, 15, 1, 1],
|
|
35169
|
+
r: [0, 0, 22, 25, 16, 16, 16],
|
|
35170
|
+
s: [0, 0, 15, 16, 14, 1, 30],
|
|
35171
|
+
t: [8, 8, 28, 8, 8, 9, 6],
|
|
35172
|
+
u: [0, 0, 17, 17, 17, 19, 13],
|
|
35173
|
+
v: [0, 0, 17, 17, 17, 10, 4],
|
|
35174
|
+
w: [0, 0, 17, 17, 21, 21, 10],
|
|
35175
|
+
x: [0, 0, 17, 10, 4, 10, 17],
|
|
35176
|
+
y: [0, 0, 17, 17, 15, 1, 14],
|
|
35177
|
+
z: [0, 0, 31, 2, 4, 8, 31]
|
|
35178
|
+
};
|
|
35179
|
+
function glyph(ch) {
|
|
35180
|
+
return FONT[ch];
|
|
35181
|
+
}
|
|
35182
|
+
function textWidth(text, scale) {
|
|
35183
|
+
let width = 0;
|
|
35184
|
+
for (const ch of text) {
|
|
35185
|
+
if (!glyph(ch)) continue;
|
|
35186
|
+
width += 6 * scale;
|
|
35187
|
+
}
|
|
35188
|
+
return width;
|
|
35189
|
+
}
|
|
35190
|
+
function drawText(pixels, text, x, y, color, scale) {
|
|
35191
|
+
let cursor = Math.round(x);
|
|
35192
|
+
for (const ch of text) {
|
|
35193
|
+
const rows = glyph(ch);
|
|
35194
|
+
if (!rows) continue;
|
|
35195
|
+
for (let row = 0; row < rows.length; row++) {
|
|
35196
|
+
const bits = rows[row] ?? 0;
|
|
35197
|
+
for (let col = 0; col < 5; col++) {
|
|
35198
|
+
if ((bits & 1 << 4 - col) === 0) continue;
|
|
35199
|
+
fillRect(pixels, cursor + col * scale, y + row * scale, scale, scale, color);
|
|
35200
|
+
}
|
|
35201
|
+
}
|
|
35202
|
+
cursor += 6 * scale;
|
|
35203
|
+
}
|
|
35204
|
+
}
|
|
35205
|
+
function encodePng(pixels, width, height) {
|
|
35206
|
+
const stride = width * 3;
|
|
35207
|
+
const raw = Buffer.alloc((stride + 1) * height);
|
|
35208
|
+
for (let y = 0; y < height; y++) {
|
|
35209
|
+
const offset = y * (stride + 1);
|
|
35210
|
+
raw[offset] = 0;
|
|
35211
|
+
Buffer.from(pixels.buffer, pixels.byteOffset + y * stride, stride).copy(raw, offset + 1);
|
|
35212
|
+
}
|
|
35213
|
+
const compressed = deflateSync(raw);
|
|
35214
|
+
const ihdr = Buffer.alloc(13);
|
|
35215
|
+
ihdr.writeUInt32BE(width, 0);
|
|
35216
|
+
ihdr.writeUInt32BE(height, 4);
|
|
35217
|
+
ihdr[8] = 8;
|
|
35218
|
+
ihdr[9] = 2;
|
|
35219
|
+
return Buffer.concat([
|
|
35220
|
+
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
|
|
35221
|
+
chunk("IHDR", ihdr),
|
|
35222
|
+
chunk("IDAT", compressed),
|
|
35223
|
+
chunk("IEND", Buffer.alloc(0))
|
|
35224
|
+
]);
|
|
35225
|
+
}
|
|
35226
|
+
function chunk(type, data) {
|
|
35227
|
+
const length = Buffer.alloc(4);
|
|
35228
|
+
length.writeUInt32BE(data.length, 0);
|
|
35229
|
+
const name = Buffer.from(type, "ascii");
|
|
35230
|
+
const crc = Buffer.alloc(4);
|
|
35231
|
+
crc.writeUInt32BE(crc32(Buffer.concat([name, data])), 0);
|
|
35232
|
+
return Buffer.concat([length, name, data, crc]);
|
|
35233
|
+
}
|
|
35234
|
+
function crc32(data) {
|
|
35235
|
+
let crc = 4294967295;
|
|
35236
|
+
for (const byte of data) {
|
|
35237
|
+
crc ^= byte;
|
|
35238
|
+
for (let i = 0; i < 8; i++) {
|
|
35239
|
+
const mask = -(crc & 1);
|
|
35240
|
+
crc = crc >>> 1 ^ 3988292384 & mask;
|
|
35241
|
+
}
|
|
35242
|
+
}
|
|
35243
|
+
return (crc ^ 4294967295) >>> 0;
|
|
35244
|
+
}
|
|
35245
|
+
|
|
34959
35246
|
// src/mcp-server/error-envelope.ts
|
|
34960
35247
|
function toErrorText(err) {
|
|
34961
35248
|
const envelope = isAppError(err) ? err.toEnvelope() : {
|
|
@@ -34965,9 +35252,13 @@ function toErrorText(err) {
|
|
|
34965
35252
|
};
|
|
34966
35253
|
return JSON.stringify(envelope);
|
|
34967
35254
|
}
|
|
34968
|
-
function okResult(output2) {
|
|
35255
|
+
function okResult(output2, image) {
|
|
35256
|
+
const content = [{ type: "text", text: JSON.stringify(output2) }];
|
|
35257
|
+
if (image) {
|
|
35258
|
+
content.push({ type: "image", data: image.data, mimeType: image.mimeType });
|
|
35259
|
+
}
|
|
34969
35260
|
return {
|
|
34970
|
-
content
|
|
35261
|
+
content,
|
|
34971
35262
|
structuredContent: output2
|
|
34972
35263
|
};
|
|
34973
35264
|
}
|
|
@@ -35157,7 +35448,7 @@ function createWeatherMcpServer() {
|
|
|
35157
35448
|
async ({ latitude, longitude, startDate, endDate }) => {
|
|
35158
35449
|
try {
|
|
35159
35450
|
const result = await getForecast(latitude, longitude, startDate, endDate);
|
|
35160
|
-
return okResult(result);
|
|
35451
|
+
return okResult(result, chartForForecast(result));
|
|
35161
35452
|
} catch (err) {
|
|
35162
35453
|
return errResult(err);
|
|
35163
35454
|
}
|
|
@@ -35181,7 +35472,7 @@ function createWeatherMcpServer() {
|
|
|
35181
35472
|
startYear,
|
|
35182
35473
|
endYear
|
|
35183
35474
|
);
|
|
35184
|
-
return okResult(result);
|
|
35475
|
+
return okResult(result, chartForHistory(result));
|
|
35185
35476
|
} catch (err) {
|
|
35186
35477
|
return errResult(err);
|
|
35187
35478
|
}
|
|
@@ -35198,7 +35489,7 @@ function createWeatherMcpServer() {
|
|
|
35198
35489
|
async ({ locations, startDate, endDate }) => {
|
|
35199
35490
|
try {
|
|
35200
35491
|
const result = await compareWeather(locations, startDate, endDate);
|
|
35201
|
-
return okResult(result);
|
|
35492
|
+
return okResult(result, chartForCompare(result));
|
|
35202
35493
|
} catch (err) {
|
|
35203
35494
|
return errResult(err);
|
|
35204
35495
|
}
|
|
@@ -35206,6 +35497,50 @@ function createWeatherMcpServer() {
|
|
|
35206
35497
|
);
|
|
35207
35498
|
return server;
|
|
35208
35499
|
}
|
|
35500
|
+
function pngBlock(png) {
|
|
35501
|
+
if (!png) return void 0;
|
|
35502
|
+
return { data: png.toString("base64"), mimeType: "image/png" };
|
|
35503
|
+
}
|
|
35504
|
+
function shortDate(iso) {
|
|
35505
|
+
return iso.slice(5);
|
|
35506
|
+
}
|
|
35507
|
+
function chartForForecast(result) {
|
|
35508
|
+
return pngBlock(
|
|
35509
|
+
renderTemperatureChart(
|
|
35510
|
+
result.days.map((day) => shortDate(day.date)),
|
|
35511
|
+
[
|
|
35512
|
+
{ name: "min", values: result.days.map((day) => day.tempMinC) },
|
|
35513
|
+
{ name: "max", values: result.days.map((day) => day.tempMaxC) }
|
|
35514
|
+
]
|
|
35515
|
+
)
|
|
35516
|
+
);
|
|
35517
|
+
}
|
|
35518
|
+
function chartForHistory(result) {
|
|
35519
|
+
return pngBlock(
|
|
35520
|
+
renderTemperatureChart(
|
|
35521
|
+
result.years.map((year) => String(year.year)),
|
|
35522
|
+
[
|
|
35523
|
+
{ name: "min", values: result.years.map((year) => year.tempMinC) },
|
|
35524
|
+
{ name: "mean", values: result.years.map((year) => year.tempMeanC) },
|
|
35525
|
+
{ name: "max", values: result.years.map((year) => year.tempMaxC) }
|
|
35526
|
+
]
|
|
35527
|
+
)
|
|
35528
|
+
);
|
|
35529
|
+
}
|
|
35530
|
+
function chartForCompare(result) {
|
|
35531
|
+
const labels = result.locations[0]?.forecast.days.map((day) => day.date) ?? [];
|
|
35532
|
+
return pngBlock(
|
|
35533
|
+
renderTemperatureChart(
|
|
35534
|
+
labels.map(shortDate),
|
|
35535
|
+
result.locations.map((location) => ({
|
|
35536
|
+
name: location.name,
|
|
35537
|
+
values: labels.map(
|
|
35538
|
+
(date5) => location.forecast.days.find((day) => day.date === date5)?.tempMaxC ?? null
|
|
35539
|
+
)
|
|
35540
|
+
}))
|
|
35541
|
+
)
|
|
35542
|
+
);
|
|
35543
|
+
}
|
|
35209
35544
|
|
|
35210
35545
|
// src/mcp-server/index.ts
|
|
35211
35546
|
void serveStdio(() => createWeatherMcpServer(), {
|