@ryancardin/noaa-tides-currents-mcp-server 1.0.0 → 2.0.0
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 +164 -199
- package/dist/client/cache.d.ts +15 -0
- package/dist/client/cache.js +44 -0
- package/dist/client/http.d.ts +28 -0
- package/dist/client/http.js +178 -0
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +28 -0
- package/dist/format/respond.d.ts +25 -0
- package/dist/format/respond.js +40 -0
- package/dist/format/series.d.ts +16 -0
- package/dist/format/series.js +42 -0
- package/dist/format/units.d.ts +16 -0
- package/dist/format/units.js +37 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +78 -8
- package/dist/interfaces/moon.d.ts +4 -4
- package/dist/interfaces/moon.js +58 -17
- package/dist/interfaces/sun.d.ts +4 -4
- package/dist/interfaces/sun.js +94 -25
- package/dist/prompts/index.d.ts +6 -0
- package/dist/prompts/index.js +89 -0
- package/dist/reference/content.d.ts +10 -0
- package/dist/reference/content.js +202 -0
- package/dist/resources/index.d.ts +7 -0
- package/dist/resources/index.js +73 -0
- package/dist/schemas/common.d.ts +38 -14
- package/dist/schemas/common.js +82 -18
- package/dist/services/data-api.d.ts +81 -0
- package/dist/services/data-api.js +117 -0
- package/dist/services/dpapi.d.ts +55 -0
- package/dist/services/dpapi.js +60 -0
- package/dist/services/metadata-api.d.ts +62 -0
- package/dist/services/metadata-api.js +105 -0
- package/dist/services/moon-phase-service.d.ts +2 -2
- package/dist/services/moon-phase-service.js +23 -25
- package/dist/services/sun-service.d.ts +2 -2
- package/dist/services/sun-service.js +53 -31
- package/dist/tools/astronomy.d.ts +7 -0
- package/dist/tools/astronomy.js +270 -0
- package/dist/tools/currents.d.ts +5 -0
- package/dist/tools/currents.js +168 -0
- package/dist/tools/derived.d.ts +6 -0
- package/dist/tools/derived.js +296 -0
- package/dist/tools/index.d.ts +3 -14
- package/dist/tools/index.js +17 -32
- package/dist/tools/met.d.ts +5 -0
- package/dist/tools/met.js +110 -0
- package/dist/tools/reference.d.ts +6 -0
- package/dist/tools/reference.js +33 -0
- package/dist/tools/station-metadata.d.ts +6 -0
- package/dist/tools/station-metadata.js +208 -0
- package/dist/tools/stations.d.ts +5 -0
- package/dist/tools/stations.js +265 -0
- package/dist/tools/water.d.ts +5 -0
- package/dist/tools/water.js +270 -0
- package/dist/validation/dates.d.ts +50 -0
- package/dist/validation/dates.js +139 -0
- package/package.json +27 -14
- package/.claude/settings.local.json +0 -29
- package/CLAUDE.md +0 -71
- package/Dockerfile +0 -14
- package/smithery.yaml +0 -16
- package/src/index.ts +0 -13
- package/src/interfaces/moon.ts +0 -44
- package/src/interfaces/noaa.ts +0 -130
- package/src/interfaces/parameters.ts +0 -20
- package/src/interfaces/sun.ts +0 -57
- package/src/schemas/common.ts +0 -23
- package/src/schemas/dpapi.ts +0 -99
- package/src/server/config.ts +0 -43
- package/src/server/mcp-server.ts +0 -135
- package/src/services/dpapi-service.ts +0 -187
- package/src/services/moon-phase-service.ts +0 -167
- package/src/services/noaa-parameters-service.ts +0 -139
- package/src/services/noaa-service.ts +0 -171
- package/src/services/sun-service.ts +0 -275
- package/src/tools/derived-product-tools.ts +0 -180
- package/src/tools/index.ts +0 -40
- package/src/tools/moon-tools.ts +0 -79
- package/src/tools/parameter-tools.ts +0 -82
- package/src/tools/station-tools.ts +0 -57
- package/src/tools/sun-tools.ts +0 -120
- package/src/tools/water-tools.ts +0 -166
- package/src/types/moon.ts +0 -27
- package/src/types/sun.ts +0 -51
- package/src/types/suncalc.d.ts +0 -110
- package/test-dpapi.js +0 -0
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for the NOAA Tides and Currents MCP server.
|
|
3
|
+
*/
|
|
4
|
+
/** NOAA CO-OPS Data Retrieval API (observations, predictions, currents, met). */
|
|
5
|
+
export declare const DATA_API_BASE_URL = "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter";
|
|
6
|
+
/** NOAA CO-OPS Metadata API (station metadata, datums, harmonic constituents...). */
|
|
7
|
+
export declare const METADATA_API_BASE_URL = "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi";
|
|
8
|
+
/** NOAA CO-OPS Derived Product API (sea level trends, high tide flooding...). */
|
|
9
|
+
export declare const DPAPI_BASE_URL = "https://api.tidesandcurrents.noaa.gov/dpapi/prod";
|
|
10
|
+
/**
|
|
11
|
+
* Sent as the `application` parameter on every Data API call so NOAA can
|
|
12
|
+
* attribute traffic in their logs (not an API key; the API is open).
|
|
13
|
+
*/
|
|
14
|
+
export declare const APPLICATION_NAME = "noaa-tides-currents-mcp-server";
|
|
15
|
+
export declare const SERVER_NAME = "noaa-tides-currents-mcp-server";
|
|
16
|
+
/** Maximum characters returned by a single tool response before truncation. */
|
|
17
|
+
export declare const CHARACTER_LIMIT = 25000;
|
|
18
|
+
/** HTTP request timeout in milliseconds. */
|
|
19
|
+
export declare const REQUEST_TIMEOUT_MS = 30000;
|
|
20
|
+
/** Number of retries for transient failures (network errors, 5xx, 429). */
|
|
21
|
+
export declare const MAX_RETRIES = 2;
|
|
22
|
+
/** Cache TTLs (milliseconds). */
|
|
23
|
+
export declare const CACHE_TTL: {
|
|
24
|
+
/** Full station directory listings change rarely. */
|
|
25
|
+
readonly stationList: number;
|
|
26
|
+
/** Individual station metadata (datums, sensors, harcon...). */
|
|
27
|
+
readonly stationResource: number;
|
|
28
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for the NOAA Tides and Currents MCP server.
|
|
3
|
+
*/
|
|
4
|
+
/** NOAA CO-OPS Data Retrieval API (observations, predictions, currents, met). */
|
|
5
|
+
export const DATA_API_BASE_URL = "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter";
|
|
6
|
+
/** NOAA CO-OPS Metadata API (station metadata, datums, harmonic constituents...). */
|
|
7
|
+
export const METADATA_API_BASE_URL = "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi";
|
|
8
|
+
/** NOAA CO-OPS Derived Product API (sea level trends, high tide flooding...). */
|
|
9
|
+
export const DPAPI_BASE_URL = "https://api.tidesandcurrents.noaa.gov/dpapi/prod";
|
|
10
|
+
/**
|
|
11
|
+
* Sent as the `application` parameter on every Data API call so NOAA can
|
|
12
|
+
* attribute traffic in their logs (not an API key; the API is open).
|
|
13
|
+
*/
|
|
14
|
+
export const APPLICATION_NAME = "noaa-tides-currents-mcp-server";
|
|
15
|
+
export const SERVER_NAME = "noaa-tides-currents-mcp-server";
|
|
16
|
+
/** Maximum characters returned by a single tool response before truncation. */
|
|
17
|
+
export const CHARACTER_LIMIT = 25_000;
|
|
18
|
+
/** HTTP request timeout in milliseconds. */
|
|
19
|
+
export const REQUEST_TIMEOUT_MS = 30_000;
|
|
20
|
+
/** Number of retries for transient failures (network errors, 5xx, 429). */
|
|
21
|
+
export const MAX_RETRIES = 2;
|
|
22
|
+
/** Cache TTLs (milliseconds). */
|
|
23
|
+
export const CACHE_TTL = {
|
|
24
|
+
/** Full station directory listings change rarely. */
|
|
25
|
+
stationList: 6 * 60 * 60 * 1000,
|
|
26
|
+
/** Individual station metadata (datums, sensors, harcon...). */
|
|
27
|
+
stationResource: 60 * 60 * 1000,
|
|
28
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response shaping shared by every tool.
|
|
3
|
+
*
|
|
4
|
+
* Each tool supports `response_format`: "markdown" (default, human-readable
|
|
5
|
+
* tables with units spelled out) or "json" (complete structured payload).
|
|
6
|
+
* Structured content is attached in both modes so MCP clients that support
|
|
7
|
+
* it can consume typed output. Responses longer than CHARACTER_LIMIT are
|
|
8
|
+
* truncated with guidance rather than flooding the agent's context.
|
|
9
|
+
*/
|
|
10
|
+
export type ResponseFormat = "markdown" | "json";
|
|
11
|
+
export interface ToolResult {
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
content: Array<{
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}>;
|
|
17
|
+
structuredContent?: Record<string, unknown>;
|
|
18
|
+
isError?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Build a successful tool response in the requested format. */
|
|
21
|
+
export declare function respond(format: ResponseFormat, structured: Record<string, unknown>, markdown: string): ToolResult;
|
|
22
|
+
/** Build an error tool response (kept inside the result per MCP guidance). */
|
|
23
|
+
export declare function respondError(error: unknown): ToolResult;
|
|
24
|
+
/** Render an array of records as a compact GitHub-flavored markdown table. */
|
|
25
|
+
export declare function markdownTable(headers: string[], rows: Array<Array<string | number | null | undefined>>): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response shaping shared by every tool.
|
|
3
|
+
*
|
|
4
|
+
* Each tool supports `response_format`: "markdown" (default, human-readable
|
|
5
|
+
* tables with units spelled out) or "json" (complete structured payload).
|
|
6
|
+
* Structured content is attached in both modes so MCP clients that support
|
|
7
|
+
* it can consume typed output. Responses longer than CHARACTER_LIMIT are
|
|
8
|
+
* truncated with guidance rather than flooding the agent's context.
|
|
9
|
+
*/
|
|
10
|
+
import { CHARACTER_LIMIT } from "../constants.js";
|
|
11
|
+
/** Build a successful tool response in the requested format. */
|
|
12
|
+
export function respond(format, structured, markdown) {
|
|
13
|
+
let text = format === "json" ? JSON.stringify(structured, null, 2) : markdown;
|
|
14
|
+
if (text.length > CHARACTER_LIMIT) {
|
|
15
|
+
text =
|
|
16
|
+
text.slice(0, CHARACTER_LIMIT) +
|
|
17
|
+
"\n\n…[truncated: response exceeded the size limit. Narrow the date range, lower the limit parameter, or request fewer fields.]";
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text }],
|
|
21
|
+
structuredContent: structured,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Build an error tool response (kept inside the result per MCP guidance). */
|
|
25
|
+
export function respondError(error) {
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
return {
|
|
28
|
+
isError: true,
|
|
29
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Render an array of records as a compact GitHub-flavored markdown table. */
|
|
33
|
+
export function markdownTable(headers, rows) {
|
|
34
|
+
const lines = [
|
|
35
|
+
`| ${headers.join(" | ")} |`,
|
|
36
|
+
`|${headers.map(() => "---").join("|")}|`,
|
|
37
|
+
...rows.map((row) => `| ${row.map((cell) => (cell === null || cell === undefined || cell === "" ? "—" : String(cell))).join(" | ")} |`),
|
|
38
|
+
];
|
|
39
|
+
return lines.join("\n");
|
|
40
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown rendering for time-series tool responses: a metadata header
|
|
3
|
+
* (station, units, datum, time zone, count) followed by a data table and an
|
|
4
|
+
* optional flag legend, so units and quality semantics are always visible.
|
|
5
|
+
*/
|
|
6
|
+
export interface SeriesMeta {
|
|
7
|
+
title: string;
|
|
8
|
+
station: string;
|
|
9
|
+
stationName?: string;
|
|
10
|
+
unitsLabel?: string;
|
|
11
|
+
datum?: string;
|
|
12
|
+
timeZone?: string;
|
|
13
|
+
extra?: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function seriesMarkdown(meta: SeriesMeta, headers: string[], rows: Array<Array<string | number | null | undefined>>, legend?: string): string;
|
|
16
|
+
export declare function timeZoneLabel(tz: string): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown rendering for time-series tool responses: a metadata header
|
|
3
|
+
* (station, units, datum, time zone, count) followed by a data table and an
|
|
4
|
+
* optional flag legend, so units and quality semantics are always visible.
|
|
5
|
+
*/
|
|
6
|
+
import { markdownTable } from "./respond.js";
|
|
7
|
+
export function seriesMarkdown(meta, headers, rows, legend) {
|
|
8
|
+
const lines = [`# ${meta.title}`, ""];
|
|
9
|
+
const facts = [
|
|
10
|
+
`**Station**: ${meta.station}${meta.stationName ? ` (${meta.stationName})` : ""}`,
|
|
11
|
+
];
|
|
12
|
+
if (meta.unitsLabel)
|
|
13
|
+
facts.push(`**Units**: ${meta.unitsLabel}`);
|
|
14
|
+
if (meta.datum)
|
|
15
|
+
facts.push(`**Datum**: ${meta.datum}`);
|
|
16
|
+
if (meta.timeZone)
|
|
17
|
+
facts.push(`**Time zone**: ${meta.timeZone}`);
|
|
18
|
+
facts.push(`**Records**: ${rows.length}`);
|
|
19
|
+
lines.push(facts.join(" · "));
|
|
20
|
+
if (meta.extra?.length) {
|
|
21
|
+
lines.push("", ...meta.extra);
|
|
22
|
+
}
|
|
23
|
+
lines.push("");
|
|
24
|
+
if (rows.length === 0) {
|
|
25
|
+
lines.push("_No data returned for this request. The station may not collect this product, or the window may fall outside its record — check noaa_get_station_info._");
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
lines.push(markdownTable(headers, rows));
|
|
29
|
+
}
|
|
30
|
+
if (legend && rows.length > 0) {
|
|
31
|
+
lines.push("", `_${legend}_`);
|
|
32
|
+
}
|
|
33
|
+
return lines.join("\n");
|
|
34
|
+
}
|
|
35
|
+
const TIME_ZONE_LABELS = {
|
|
36
|
+
gmt: "GMT (UTC)",
|
|
37
|
+
lst: "local standard time (no DST)",
|
|
38
|
+
lst_ldt: "local time (DST-aware)",
|
|
39
|
+
};
|
|
40
|
+
export function timeZoneLabel(tz) {
|
|
41
|
+
return TIME_ZONE_LABELS[tz] ?? tz;
|
|
42
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit labeling for NOAA CO-OPS measurements.
|
|
3
|
+
*
|
|
4
|
+
* The `units` request parameter (english|metric) changes the physical units
|
|
5
|
+
* per measurement type — and NOT uniformly:
|
|
6
|
+
* - wind speed: english = knots, metric = m/s
|
|
7
|
+
* - current speed: english = knots, metric = cm/s (NOT m/s!)
|
|
8
|
+
* - air pressure: millibars in BOTH systems
|
|
9
|
+
* - salinity: PSU in BOTH systems
|
|
10
|
+
* Every tool response labels its values so the agent never has to guess.
|
|
11
|
+
*/
|
|
12
|
+
export type UnitSystem = "english" | "metric";
|
|
13
|
+
export type MeasurementKind = "water_level" | "air_temperature" | "water_temperature" | "wind" | "air_pressure" | "air_gap" | "conductivity" | "visibility" | "humidity" | "salinity" | "currents";
|
|
14
|
+
export declare function unitLabel(kind: MeasurementKind, units: UnitSystem): string;
|
|
15
|
+
/** Quality/data flags per product family, decoded for humans. */
|
|
16
|
+
export declare const FLAG_LEGENDS: Record<string, string>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit labeling for NOAA CO-OPS measurements.
|
|
3
|
+
*
|
|
4
|
+
* The `units` request parameter (english|metric) changes the physical units
|
|
5
|
+
* per measurement type — and NOT uniformly:
|
|
6
|
+
* - wind speed: english = knots, metric = m/s
|
|
7
|
+
* - current speed: english = knots, metric = cm/s (NOT m/s!)
|
|
8
|
+
* - air pressure: millibars in BOTH systems
|
|
9
|
+
* - salinity: PSU in BOTH systems
|
|
10
|
+
* Every tool response labels its values so the agent never has to guess.
|
|
11
|
+
*/
|
|
12
|
+
const UNIT_LABELS = {
|
|
13
|
+
water_level: { english: "feet", metric: "meters" },
|
|
14
|
+
air_temperature: { english: "°F", metric: "°C" },
|
|
15
|
+
water_temperature: { english: "°F", metric: "°C" },
|
|
16
|
+
wind: { english: "knots", metric: "m/s" },
|
|
17
|
+
air_pressure: { english: "millibars", metric: "millibars" },
|
|
18
|
+
air_gap: { english: "feet", metric: "meters" },
|
|
19
|
+
conductivity: { english: "mS/cm", metric: "mS/cm" },
|
|
20
|
+
visibility: { english: "nautical miles", metric: "kilometers" },
|
|
21
|
+
humidity: { english: "%", metric: "%" },
|
|
22
|
+
salinity: { english: "PSU", metric: "PSU" },
|
|
23
|
+
currents: { english: "knots", metric: "cm/s" },
|
|
24
|
+
};
|
|
25
|
+
export function unitLabel(kind, units) {
|
|
26
|
+
return UNIT_LABELS[kind][units];
|
|
27
|
+
}
|
|
28
|
+
/** Quality/data flags per product family, decoded for humans. */
|
|
29
|
+
export const FLAG_LEGENDS = {
|
|
30
|
+
water_level_preliminary: "f flags (count of 1-sec samples flagged): O=outside 3-sigma band, F=flat tolerance exceeded, R=rate-of-change limit exceeded, L=max/min limit exceeded",
|
|
31
|
+
water_level_verified: "f flags: I=inferred value, F=flat tolerance exceeded, R=rate-of-change limit exceeded, T=max/min limit exceeded",
|
|
32
|
+
hourly_height: "f flags: I=inferred, L=max/min limit exceeded",
|
|
33
|
+
high_low: "f flags: I=inferred, L=max/min limit exceeded; ty: HH=higher high, H=high, L=low, LL=lower low",
|
|
34
|
+
met: "f flags: X=max limit exceeded, N=min limit exceeded, R=rate-of-change limit exceeded",
|
|
35
|
+
wind: "f flags: X=max wind speed exceeded, R=rate-of-change limit exceeded",
|
|
36
|
+
air_gap: "f flags: O=outside 3-sigma band, F=flat tolerance exceeded, R=rate-of-change exceeded, A=max/min limit exceeded",
|
|
37
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* NOAA Tides and Currents MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Exposes NOAA CO-OPS data (water levels, tide predictions, currents,
|
|
6
|
+
* meteorology), station metadata (datums, harmonic constituents, offsets),
|
|
7
|
+
* derived products (sea level trends/projections, extreme water levels,
|
|
8
|
+
* high tide flooding), and local sun/moon calculations.
|
|
9
|
+
*
|
|
10
|
+
* Transport: stdio by default (for MCP client integration); pass --http
|
|
11
|
+
* [--port N] for a stateless streamable-HTTP endpoint at /mcp.
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT: never write to stdout in stdio mode — it is the JSON-RPC
|
|
14
|
+
* channel. All logging goes to stderr.
|
|
15
|
+
*/
|
|
2
16
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
/**
|
|
3
|
+
* NOAA Tides and Currents MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Exposes NOAA CO-OPS data (water levels, tide predictions, currents,
|
|
6
|
+
* meteorology), station metadata (datums, harmonic constituents, offsets),
|
|
7
|
+
* derived products (sea level trends/projections, extreme water levels,
|
|
8
|
+
* high tide flooding), and local sun/moon calculations.
|
|
9
|
+
*
|
|
10
|
+
* Transport: stdio by default (for MCP client integration); pass --http
|
|
11
|
+
* [--port N] for a stateless streamable-HTTP endpoint at /mcp.
|
|
12
|
+
*
|
|
13
|
+
* IMPORTANT: never write to stdout in stdio mode — it is the JSON-RPC
|
|
14
|
+
* channel. All logging goes to stderr.
|
|
15
|
+
*/
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
20
|
+
import express from "express";
|
|
21
|
+
import { SERVER_NAME } from "./constants.js";
|
|
22
|
+
import { registerAllTools } from "./tools/index.js";
|
|
23
|
+
import { registerResources } from "./resources/index.js";
|
|
24
|
+
import { registerPrompts } from "./prompts/index.js";
|
|
25
|
+
const require = createRequire(import.meta.url);
|
|
26
|
+
const { version } = require("../package.json");
|
|
27
|
+
function buildServer() {
|
|
28
|
+
const server = new McpServer({ name: SERVER_NAME, version });
|
|
29
|
+
registerAllTools(server);
|
|
30
|
+
registerResources(server);
|
|
31
|
+
registerPrompts(server);
|
|
32
|
+
return server;
|
|
33
|
+
}
|
|
34
|
+
async function runStdio() {
|
|
35
|
+
const server = buildServer();
|
|
36
|
+
const transport = new StdioServerTransport();
|
|
37
|
+
await server.connect(transport);
|
|
38
|
+
console.error(`${SERVER_NAME} v${version} running on stdio`);
|
|
39
|
+
}
|
|
40
|
+
async function runHttp(port) {
|
|
41
|
+
const app = express();
|
|
42
|
+
app.use(express.json());
|
|
43
|
+
app.post("/mcp", async (req, res) => {
|
|
44
|
+
// Stateless: a fresh server+transport per request avoids request-ID
|
|
45
|
+
// collisions across clients.
|
|
46
|
+
const server = buildServer();
|
|
47
|
+
const transport = new StreamableHTTPServerTransport({
|
|
48
|
+
sessionIdGenerator: undefined,
|
|
49
|
+
enableJsonResponse: true,
|
|
50
|
+
});
|
|
51
|
+
res.on("close", () => {
|
|
52
|
+
void transport.close();
|
|
53
|
+
void server.close();
|
|
54
|
+
});
|
|
55
|
+
await server.connect(transport);
|
|
56
|
+
await transport.handleRequest(req, res, req.body);
|
|
57
|
+
});
|
|
58
|
+
app.listen(port, () => {
|
|
59
|
+
console.error(`${SERVER_NAME} v${version} running at http://localhost:${port}/mcp`);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const args = process.argv.slice(2);
|
|
63
|
+
if (args.includes("--http")) {
|
|
64
|
+
const portIndex = args.indexOf("--port");
|
|
65
|
+
const portRaw = portIndex !== -1 ? Number(args[portIndex + 1]) : 3000;
|
|
66
|
+
const port = Number.isInteger(portRaw) && portRaw > 0 && portRaw < 65536
|
|
67
|
+
? portRaw
|
|
68
|
+
: 3000;
|
|
69
|
+
runHttp(port).catch((error) => {
|
|
70
|
+
console.error("Server error:", error);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
runStdio().catch((error) => {
|
|
76
|
+
console.error("Server error:", error);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { MoonPhaseName } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { MoonPhaseName } from "../types/moon.js";
|
|
3
3
|
/**
|
|
4
4
|
* Parameters for getting moon phase
|
|
5
5
|
*/
|
|
@@ -30,14 +30,14 @@ export declare const MoonPhasesRangeParamsSchema: z.ZodObject<{
|
|
|
30
30
|
longitude: z.ZodOptional<z.ZodNumber>;
|
|
31
31
|
format: z.ZodOptional<z.ZodEnum<["json", "text"]>>;
|
|
32
32
|
}, "strip", z.ZodTypeAny, {
|
|
33
|
-
start_date: string;
|
|
34
33
|
end_date: string;
|
|
34
|
+
start_date: string;
|
|
35
35
|
format?: "text" | "json" | undefined;
|
|
36
36
|
latitude?: number | undefined;
|
|
37
37
|
longitude?: number | undefined;
|
|
38
38
|
}, {
|
|
39
|
-
start_date: string;
|
|
40
39
|
end_date: string;
|
|
40
|
+
start_date: string;
|
|
41
41
|
format?: "text" | "json" | undefined;
|
|
42
42
|
latitude?: number | undefined;
|
|
43
43
|
longitude?: number | undefined;
|
package/dist/interfaces/moon.js
CHANGED
|
@@ -1,35 +1,76 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { MoonPhaseName } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { MoonPhaseName } from "../types/moon.js";
|
|
3
3
|
/**
|
|
4
4
|
* Parameters for getting moon phase
|
|
5
5
|
*/
|
|
6
6
|
export const MoonPhaseParamsSchema = z.object({
|
|
7
|
-
date: z
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
date: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Date to get moon phase for (YYYY-MM-DD format). Defaults to current date."),
|
|
11
|
+
latitude: z
|
|
12
|
+
.number()
|
|
13
|
+
.min(-90)
|
|
14
|
+
.max(90)
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Latitude for location-specific calculations"),
|
|
17
|
+
longitude: z
|
|
18
|
+
.number()
|
|
19
|
+
.min(-180)
|
|
20
|
+
.max(180)
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Longitude for location-specific calculations"),
|
|
23
|
+
format: z
|
|
24
|
+
.enum(["json", "text"])
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Output format (json or text)"),
|
|
11
27
|
});
|
|
12
28
|
/**
|
|
13
29
|
* Parameters for getting moon phases for a date range
|
|
14
30
|
*/
|
|
15
31
|
export const MoonPhasesRangeParamsSchema = z.object({
|
|
16
|
-
start_date: z.string().describe(
|
|
17
|
-
end_date: z.string().describe(
|
|
18
|
-
latitude: z
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
start_date: z.string().describe("Start date (YYYY-MM-DD format)"),
|
|
33
|
+
end_date: z.string().describe("End date (YYYY-MM-DD format)"),
|
|
34
|
+
latitude: z
|
|
35
|
+
.number()
|
|
36
|
+
.min(-90)
|
|
37
|
+
.max(90)
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Latitude for location-specific calculations"),
|
|
40
|
+
longitude: z
|
|
41
|
+
.number()
|
|
42
|
+
.min(-180)
|
|
43
|
+
.max(180)
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Longitude for location-specific calculations"),
|
|
46
|
+
format: z
|
|
47
|
+
.enum(["json", "text"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Output format (json or text)"),
|
|
21
50
|
});
|
|
22
51
|
/**
|
|
23
52
|
* Parameters for getting next moon phase
|
|
24
53
|
*/
|
|
25
54
|
export const NextMoonPhaseParamsSchema = z.object({
|
|
26
|
-
phase: z
|
|
55
|
+
phase: z
|
|
56
|
+
.enum([
|
|
27
57
|
MoonPhaseName.NEW_MOON,
|
|
28
58
|
MoonPhaseName.FIRST_QUARTER,
|
|
29
59
|
MoonPhaseName.FULL_MOON,
|
|
30
|
-
MoonPhaseName.LAST_QUARTER
|
|
31
|
-
])
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
60
|
+
MoonPhaseName.LAST_QUARTER,
|
|
61
|
+
])
|
|
62
|
+
.describe("Moon phase to find"),
|
|
63
|
+
date: z
|
|
64
|
+
.string()
|
|
65
|
+
.optional()
|
|
66
|
+
.describe("Starting date (YYYY-MM-DD format). Defaults to current date."),
|
|
67
|
+
count: z
|
|
68
|
+
.number()
|
|
69
|
+
.positive()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Number of occurrences to return. Defaults to 1."),
|
|
72
|
+
format: z
|
|
73
|
+
.enum(["json", "text"])
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("Output format (json or text)"),
|
|
35
76
|
});
|
package/dist/interfaces/sun.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { SunEventType } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SunEventType } from "../types/sun.js";
|
|
3
3
|
/**
|
|
4
4
|
* Parameters for getting sun times
|
|
5
5
|
*/
|
|
@@ -34,17 +34,17 @@ export declare const SunTimesRangeParamsSchema: z.ZodObject<{
|
|
|
34
34
|
format: z.ZodOptional<z.ZodEnum<["json", "text"]>>;
|
|
35
35
|
timezone: z.ZodOptional<z.ZodString>;
|
|
36
36
|
}, "strip", z.ZodTypeAny, {
|
|
37
|
+
end_date: string;
|
|
37
38
|
latitude: number;
|
|
38
39
|
longitude: number;
|
|
39
40
|
start_date: string;
|
|
40
|
-
end_date: string;
|
|
41
41
|
format?: "text" | "json" | undefined;
|
|
42
42
|
timezone?: string | undefined;
|
|
43
43
|
}, {
|
|
44
|
+
end_date: string;
|
|
44
45
|
latitude: number;
|
|
45
46
|
longitude: number;
|
|
46
47
|
start_date: string;
|
|
47
|
-
end_date: string;
|
|
48
48
|
format?: "text" | "json" | undefined;
|
|
49
49
|
timezone?: string | undefined;
|
|
50
50
|
}>;
|
package/dist/interfaces/sun.js
CHANGED
|
@@ -1,45 +1,114 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { SunEventType } from
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SunEventType } from "../types/sun.js";
|
|
3
3
|
/**
|
|
4
4
|
* Parameters for getting sun times
|
|
5
5
|
*/
|
|
6
6
|
export const SunTimesParamsSchema = z.object({
|
|
7
|
-
date: z
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
date: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Date to get sun times for (YYYY-MM-DD format). Defaults to current date."),
|
|
11
|
+
latitude: z
|
|
12
|
+
.number()
|
|
13
|
+
.min(-90)
|
|
14
|
+
.max(90)
|
|
15
|
+
.describe("Latitude for location-specific calculations"),
|
|
16
|
+
longitude: z
|
|
17
|
+
.number()
|
|
18
|
+
.min(-180)
|
|
19
|
+
.max(180)
|
|
20
|
+
.describe("Longitude for location-specific calculations"),
|
|
21
|
+
format: z
|
|
22
|
+
.enum(["json", "text"])
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Output format (json or text)"),
|
|
25
|
+
timezone: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Timezone for the results. Defaults to UTC."),
|
|
12
29
|
});
|
|
13
30
|
/**
|
|
14
31
|
* Parameters for getting sun times for a date range
|
|
15
32
|
*/
|
|
16
33
|
export const SunTimesRangeParamsSchema = z.object({
|
|
17
|
-
start_date: z.string().describe(
|
|
18
|
-
end_date: z.string().describe(
|
|
19
|
-
latitude: z
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
start_date: z.string().describe("Start date (YYYY-MM-DD format)"),
|
|
35
|
+
end_date: z.string().describe("End date (YYYY-MM-DD format)"),
|
|
36
|
+
latitude: z
|
|
37
|
+
.number()
|
|
38
|
+
.min(-90)
|
|
39
|
+
.max(90)
|
|
40
|
+
.describe("Latitude for location-specific calculations"),
|
|
41
|
+
longitude: z
|
|
42
|
+
.number()
|
|
43
|
+
.min(-180)
|
|
44
|
+
.max(180)
|
|
45
|
+
.describe("Longitude for location-specific calculations"),
|
|
46
|
+
format: z
|
|
47
|
+
.enum(["json", "text"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Output format (json or text)"),
|
|
50
|
+
timezone: z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Timezone for the results. Defaults to UTC."),
|
|
23
54
|
});
|
|
24
55
|
/**
|
|
25
56
|
* Parameters for getting sun position
|
|
26
57
|
*/
|
|
27
58
|
export const SunPositionParamsSchema = z.object({
|
|
28
|
-
date: z
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
date: z
|
|
60
|
+
.string()
|
|
61
|
+
.optional()
|
|
62
|
+
.describe("Date to get sun position for (YYYY-MM-DD format). Defaults to current date."),
|
|
63
|
+
time: z
|
|
64
|
+
.string()
|
|
65
|
+
.optional()
|
|
66
|
+
.describe("Time to get sun position for (HH:MM:SS format). Defaults to current time."),
|
|
67
|
+
latitude: z
|
|
68
|
+
.number()
|
|
69
|
+
.min(-90)
|
|
70
|
+
.max(90)
|
|
71
|
+
.describe("Latitude for location-specific calculations"),
|
|
72
|
+
longitude: z
|
|
73
|
+
.number()
|
|
74
|
+
.min(-180)
|
|
75
|
+
.max(180)
|
|
76
|
+
.describe("Longitude for location-specific calculations"),
|
|
77
|
+
format: z
|
|
78
|
+
.enum(["json", "text"])
|
|
79
|
+
.optional()
|
|
80
|
+
.describe("Output format (json or text)"),
|
|
33
81
|
});
|
|
34
82
|
/**
|
|
35
83
|
* Parameters for finding the next sun event
|
|
36
84
|
*/
|
|
37
85
|
export const NextSunEventParamsSchema = z.object({
|
|
38
|
-
event: z.nativeEnum(SunEventType).describe(
|
|
39
|
-
date: z
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
86
|
+
event: z.nativeEnum(SunEventType).describe("Sun event to find"),
|
|
87
|
+
date: z
|
|
88
|
+
.string()
|
|
89
|
+
.optional()
|
|
90
|
+
.describe("Starting date (YYYY-MM-DD format). Defaults to current date."),
|
|
91
|
+
latitude: z
|
|
92
|
+
.number()
|
|
93
|
+
.min(-90)
|
|
94
|
+
.max(90)
|
|
95
|
+
.describe("Latitude for location-specific calculations"),
|
|
96
|
+
longitude: z
|
|
97
|
+
.number()
|
|
98
|
+
.min(-180)
|
|
99
|
+
.max(180)
|
|
100
|
+
.describe("Longitude for location-specific calculations"),
|
|
101
|
+
count: z
|
|
102
|
+
.number()
|
|
103
|
+
.positive()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe("Number of occurrences to return. Defaults to 1."),
|
|
106
|
+
format: z
|
|
107
|
+
.enum(["json", "text"])
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("Output format (json or text)"),
|
|
110
|
+
timezone: z
|
|
111
|
+
.string()
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("Timezone for the results. Defaults to UTC."),
|
|
45
114
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP prompts: reusable workflow templates that orchestrate the server's
|
|
3
|
+
* tools for the most common NOAA questions.
|
|
4
|
+
*/
|
|
5
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
export declare function registerPrompts(server: McpServer): void;
|