@ryancardin/noaa-tides-currents-mcp-server 1.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.
Files changed (91) hide show
  1. package/.claude/settings.local.json +29 -0
  2. package/CLAUDE.md +71 -0
  3. package/Dockerfile +14 -0
  4. package/LICENSE +21 -0
  5. package/README.md +234 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +9 -0
  8. package/dist/interfaces/config.d.ts +6 -0
  9. package/dist/interfaces/config.js +1 -0
  10. package/dist/interfaces/moon.d.ts +65 -0
  11. package/dist/interfaces/moon.js +35 -0
  12. package/dist/interfaces/noaa.d.ts +334 -0
  13. package/dist/interfaces/noaa.js +98 -0
  14. package/dist/interfaces/parameters.d.ts +18 -0
  15. package/dist/interfaces/parameters.js +5 -0
  16. package/dist/interfaces/sun.d.ts +103 -0
  17. package/dist/interfaces/sun.js +45 -0
  18. package/dist/mcp-server.d.ts +12 -0
  19. package/dist/mcp-server.js +103 -0
  20. package/dist/moon-phase-service.d.ts +122 -0
  21. package/dist/moon-phase-service.js +187 -0
  22. package/dist/noaa-service.d.ts +60 -0
  23. package/dist/noaa-service.js +159 -0
  24. package/dist/schemas/common.d.ts +14 -0
  25. package/dist/schemas/common.js +18 -0
  26. package/dist/schemas/dpapi.d.ts +198 -0
  27. package/dist/schemas/dpapi.js +89 -0
  28. package/dist/server/config.d.ts +9 -0
  29. package/dist/server/config.js +40 -0
  30. package/dist/server/mcp-server.d.ts +12 -0
  31. package/dist/server/mcp-server.js +103 -0
  32. package/dist/services/dpapi-service.d.ts +72 -0
  33. package/dist/services/dpapi-service.js +164 -0
  34. package/dist/services/moon-phase-service.d.ts +34 -0
  35. package/dist/services/moon-phase-service.js +147 -0
  36. package/dist/services/noaa-parameters-service.d.ts +76 -0
  37. package/dist/services/noaa-parameters-service.js +128 -0
  38. package/dist/services/noaa-service.d.ts +52 -0
  39. package/dist/services/noaa-service.js +151 -0
  40. package/dist/services/sun-service.d.ts +60 -0
  41. package/dist/services/sun-service.js +234 -0
  42. package/dist/sun-service.d.ts +184 -0
  43. package/dist/sun-service.js +218 -0
  44. package/dist/tools/derived-product-tools.d.ts +6 -0
  45. package/dist/tools/derived-product-tools.js +168 -0
  46. package/dist/tools/index.d.ts +16 -0
  47. package/dist/tools/index.js +36 -0
  48. package/dist/tools/moon-tools.d.ts +6 -0
  49. package/dist/tools/moon-tools.js +69 -0
  50. package/dist/tools/parameter-tools.d.ts +6 -0
  51. package/dist/tools/parameter-tools.js +77 -0
  52. package/dist/tools/station-tools.d.ts +6 -0
  53. package/dist/tools/station-tools.js +51 -0
  54. package/dist/tools/sun-tools.d.ts +6 -0
  55. package/dist/tools/sun-tools.js +109 -0
  56. package/dist/tools/water-tools.d.ts +6 -0
  57. package/dist/tools/water-tools.js +150 -0
  58. package/dist/types/moon.d.ts +26 -0
  59. package/dist/types/moon.js +14 -0
  60. package/dist/types/sun.d.ts +49 -0
  61. package/dist/types/sun.js +19 -0
  62. package/dist/types.d.ts +337 -0
  63. package/dist/types.js +98 -0
  64. package/package.json +56 -0
  65. package/smithery.yaml +16 -0
  66. package/src/index.ts +13 -0
  67. package/src/interfaces/moon.ts +44 -0
  68. package/src/interfaces/noaa.ts +130 -0
  69. package/src/interfaces/parameters.ts +20 -0
  70. package/src/interfaces/sun.ts +57 -0
  71. package/src/schemas/common.ts +23 -0
  72. package/src/schemas/dpapi.ts +99 -0
  73. package/src/server/config.ts +43 -0
  74. package/src/server/mcp-server.ts +135 -0
  75. package/src/services/dpapi-service.ts +187 -0
  76. package/src/services/moon-phase-service.ts +167 -0
  77. package/src/services/noaa-parameters-service.ts +139 -0
  78. package/src/services/noaa-service.ts +171 -0
  79. package/src/services/sun-service.ts +275 -0
  80. package/src/tools/derived-product-tools.ts +180 -0
  81. package/src/tools/index.ts +40 -0
  82. package/src/tools/moon-tools.ts +79 -0
  83. package/src/tools/parameter-tools.ts +82 -0
  84. package/src/tools/station-tools.ts +57 -0
  85. package/src/tools/sun-tools.ts +120 -0
  86. package/src/tools/water-tools.ts +166 -0
  87. package/src/types/moon.ts +27 -0
  88. package/src/types/sun.ts +51 -0
  89. package/src/types/suncalc.d.ts +110 -0
  90. package/test-dpapi.js +0 -0
  91. package/tsconfig.json +15 -0
@@ -0,0 +1,103 @@
1
+ import { GetWaterLevelsSchema, GetTidePredictionsSchema, GetCurrentsSchema, GetCurrentPredictionsSchema, GetMeteorologicalDataSchema, GetStationsSchema, GetStationDetailsSchema } from './types.js';
2
+ // Class for the MCP server
3
+ export class McpServer {
4
+ tools;
5
+ noaaService;
6
+ constructor(noaaService) {
7
+ this.noaaService = noaaService;
8
+ this.tools = this.initializeTools();
9
+ }
10
+ // Initialize the tools
11
+ initializeTools() {
12
+ // Water Levels tool
13
+ const getWaterLevels = {
14
+ name: "get_water_levels",
15
+ description: "Get water level data for a station",
16
+ inputSchema: GetWaterLevelsSchema,
17
+ handler: async (params) => {
18
+ return this.noaaService.getWaterLevels(params);
19
+ }
20
+ };
21
+ // Tide Predictions tool
22
+ const getTidePredictions = {
23
+ name: "get_tide_predictions",
24
+ description: "Get tide prediction data",
25
+ inputSchema: GetTidePredictionsSchema,
26
+ handler: async (params) => {
27
+ return this.noaaService.getTidePredictions(params);
28
+ }
29
+ };
30
+ // Currents tool
31
+ const getCurrents = {
32
+ name: "get_currents",
33
+ description: "Get currents data for a station",
34
+ inputSchema: GetCurrentsSchema,
35
+ handler: async (params) => {
36
+ return this.noaaService.getCurrents(params);
37
+ }
38
+ };
39
+ // Current Predictions tool
40
+ const getCurrentPredictions = {
41
+ name: "get_current_predictions",
42
+ description: "Get current predictions",
43
+ inputSchema: GetCurrentPredictionsSchema,
44
+ handler: async (params) => {
45
+ return this.noaaService.getCurrentPredictions(params);
46
+ }
47
+ };
48
+ // Meteorological Data tool
49
+ const getMeteorologicalData = {
50
+ name: "get_meteorological_data",
51
+ description: "Get meteorological data",
52
+ inputSchema: GetMeteorologicalDataSchema,
53
+ handler: async (params) => {
54
+ return this.noaaService.getMeteorologicalData(params);
55
+ }
56
+ };
57
+ // Stations tool
58
+ const getStations = {
59
+ name: "get_stations",
60
+ description: "Get list of stations",
61
+ inputSchema: GetStationsSchema,
62
+ handler: async (params) => {
63
+ return this.noaaService.getStations(params);
64
+ }
65
+ };
66
+ // Station Details tool
67
+ const getStationDetails = {
68
+ name: "get_station_details",
69
+ description: "Get detailed information about a station",
70
+ inputSchema: GetStationDetailsSchema,
71
+ handler: async (params) => {
72
+ return this.noaaService.getStationDetails(params);
73
+ }
74
+ };
75
+ return [
76
+ getWaterLevels,
77
+ getTidePredictions,
78
+ getCurrents,
79
+ getCurrentPredictions,
80
+ getMeteorologicalData,
81
+ getStations,
82
+ getStationDetails
83
+ ];
84
+ }
85
+ // Method to get all tools
86
+ getTools() {
87
+ return this.tools.map(tool => ({
88
+ name: tool.name,
89
+ description: tool.description
90
+ }));
91
+ }
92
+ // Method to handle tool execution
93
+ async executeTool(toolName, params) {
94
+ const tool = this.tools.find(t => t.name === toolName);
95
+ if (!tool) {
96
+ throw new Error(`Tool '${toolName}' not found`);
97
+ }
98
+ // Validate the parameters against the schema
99
+ const validatedParams = tool.inputSchema.parse(params);
100
+ // Execute the tool handler
101
+ return tool.handler(validatedParams);
102
+ }
103
+ }
@@ -0,0 +1,122 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Moon phase names and their approximate ranges
4
+ */
5
+ export declare enum MoonPhaseName {
6
+ NEW_MOON = "New Moon",
7
+ WAXING_CRESCENT = "Waxing Crescent",
8
+ FIRST_QUARTER = "First Quarter",
9
+ WAXING_GIBBOUS = "Waxing Gibbous",
10
+ FULL_MOON = "Full Moon",
11
+ WANING_GIBBOUS = "Waning Gibbous",
12
+ LAST_QUARTER = "Last Quarter",
13
+ WANING_CRESCENT = "Waning Crescent"
14
+ }
15
+ /**
16
+ * Moon phase information
17
+ */
18
+ export interface MoonPhaseInfo {
19
+ date: string;
20
+ phase: number;
21
+ phaseName: MoonPhaseName;
22
+ illumination: number;
23
+ age: number;
24
+ distance: number;
25
+ diameter: number;
26
+ isWaxing: boolean;
27
+ }
28
+ /**
29
+ * Parameters for getting moon phase
30
+ */
31
+ export declare const MoonPhaseParamsSchema: z.ZodObject<{
32
+ date: z.ZodOptional<z.ZodString>;
33
+ latitude: z.ZodOptional<z.ZodNumber>;
34
+ longitude: z.ZodOptional<z.ZodNumber>;
35
+ format: z.ZodOptional<z.ZodEnum<["json", "text"]>>;
36
+ }, "strip", z.ZodTypeAny, {
37
+ date?: string | undefined;
38
+ format?: "text" | "json" | undefined;
39
+ latitude?: number | undefined;
40
+ longitude?: number | undefined;
41
+ }, {
42
+ date?: string | undefined;
43
+ format?: "text" | "json" | undefined;
44
+ latitude?: number | undefined;
45
+ longitude?: number | undefined;
46
+ }>;
47
+ export type MoonPhaseParams = z.infer<typeof MoonPhaseParamsSchema>;
48
+ /**
49
+ * Parameters for getting moon phases for a date range
50
+ */
51
+ export declare const MoonPhasesRangeParamsSchema: z.ZodObject<{
52
+ start_date: z.ZodString;
53
+ end_date: z.ZodString;
54
+ latitude: z.ZodOptional<z.ZodNumber>;
55
+ longitude: z.ZodOptional<z.ZodNumber>;
56
+ format: z.ZodOptional<z.ZodEnum<["json", "text"]>>;
57
+ }, "strip", z.ZodTypeAny, {
58
+ start_date: string;
59
+ end_date: string;
60
+ format?: "text" | "json" | undefined;
61
+ latitude?: number | undefined;
62
+ longitude?: number | undefined;
63
+ }, {
64
+ start_date: string;
65
+ end_date: string;
66
+ format?: "text" | "json" | undefined;
67
+ latitude?: number | undefined;
68
+ longitude?: number | undefined;
69
+ }>;
70
+ export type MoonPhasesRangeParams = z.infer<typeof MoonPhasesRangeParamsSchema>;
71
+ /**
72
+ * Parameters for getting next moon phase
73
+ */
74
+ export declare const NextMoonPhaseParamsSchema: z.ZodObject<{
75
+ phase: z.ZodEnum<[MoonPhaseName.NEW_MOON, MoonPhaseName.FIRST_QUARTER, MoonPhaseName.FULL_MOON, MoonPhaseName.LAST_QUARTER]>;
76
+ date: z.ZodOptional<z.ZodString>;
77
+ count: z.ZodOptional<z.ZodNumber>;
78
+ format: z.ZodOptional<z.ZodEnum<["json", "text"]>>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ phase: MoonPhaseName.NEW_MOON | MoonPhaseName.FIRST_QUARTER | MoonPhaseName.FULL_MOON | MoonPhaseName.LAST_QUARTER;
81
+ date?: string | undefined;
82
+ format?: "text" | "json" | undefined;
83
+ count?: number | undefined;
84
+ }, {
85
+ phase: MoonPhaseName.NEW_MOON | MoonPhaseName.FIRST_QUARTER | MoonPhaseName.FULL_MOON | MoonPhaseName.LAST_QUARTER;
86
+ date?: string | undefined;
87
+ format?: "text" | "json" | undefined;
88
+ count?: number | undefined;
89
+ }>;
90
+ export type NextMoonPhaseParams = z.infer<typeof NextMoonPhaseParamsSchema>;
91
+ /**
92
+ * Service for moon phase calculations
93
+ */
94
+ export declare class MoonPhaseService {
95
+ /**
96
+ * Get the moon phase for a specific date
97
+ * @param params Parameters for the request
98
+ * @returns Moon phase information
99
+ */
100
+ getMoonPhase(params: MoonPhaseParams): MoonPhaseInfo;
101
+ /**
102
+ * Get moon phases for a date range
103
+ * @param params Parameters for the request
104
+ * @returns Array of moon phase information
105
+ */
106
+ getMoonPhasesRange(params: MoonPhasesRangeParams): MoonPhaseInfo[];
107
+ /**
108
+ * Get the next occurrence(s) of a specific moon phase
109
+ * @param params Parameters for the request
110
+ * @returns Array of dates for the next occurrences of the specified phase
111
+ */
112
+ getNextMoonPhase(params: NextMoonPhaseParams): {
113
+ date: string;
114
+ phase: string;
115
+ }[];
116
+ /**
117
+ * Get the name of the moon phase based on the phase value
118
+ * @param phase Phase value (0-1)
119
+ * @returns Moon phase name
120
+ */
121
+ private getMoonPhaseName;
122
+ }
@@ -0,0 +1,187 @@
1
+ import SunCalc from 'suncalc';
2
+ import { z } from 'zod';
3
+ /**
4
+ * Moon phase names and their approximate ranges
5
+ */
6
+ export var MoonPhaseName;
7
+ (function (MoonPhaseName) {
8
+ MoonPhaseName["NEW_MOON"] = "New Moon";
9
+ MoonPhaseName["WAXING_CRESCENT"] = "Waxing Crescent";
10
+ MoonPhaseName["FIRST_QUARTER"] = "First Quarter";
11
+ MoonPhaseName["WAXING_GIBBOUS"] = "Waxing Gibbous";
12
+ MoonPhaseName["FULL_MOON"] = "Full Moon";
13
+ MoonPhaseName["WANING_GIBBOUS"] = "Waning Gibbous";
14
+ MoonPhaseName["LAST_QUARTER"] = "Last Quarter";
15
+ MoonPhaseName["WANING_CRESCENT"] = "Waning Crescent";
16
+ })(MoonPhaseName || (MoonPhaseName = {}));
17
+ /**
18
+ * Parameters for getting moon phase
19
+ */
20
+ export const MoonPhaseParamsSchema = z.object({
21
+ date: z.string().optional().describe('Date to get moon phase for (YYYY-MM-DD format). Defaults to current date.'),
22
+ latitude: z.number().min(-90).max(90).optional().describe('Latitude for location-specific calculations'),
23
+ longitude: z.number().min(-180).max(180).optional().describe('Longitude for location-specific calculations'),
24
+ format: z.enum(['json', 'text']).optional().describe('Output format (json or text)')
25
+ });
26
+ /**
27
+ * Parameters for getting moon phases for a date range
28
+ */
29
+ export const MoonPhasesRangeParamsSchema = z.object({
30
+ start_date: z.string().describe('Start date (YYYY-MM-DD format)'),
31
+ end_date: z.string().describe('End date (YYYY-MM-DD format)'),
32
+ latitude: z.number().min(-90).max(90).optional().describe('Latitude for location-specific calculations'),
33
+ longitude: z.number().min(-180).max(180).optional().describe('Longitude for location-specific calculations'),
34
+ format: z.enum(['json', 'text']).optional().describe('Output format (json or text)')
35
+ });
36
+ /**
37
+ * Parameters for getting next moon phase
38
+ */
39
+ export const NextMoonPhaseParamsSchema = z.object({
40
+ phase: z.enum([
41
+ MoonPhaseName.NEW_MOON,
42
+ MoonPhaseName.FIRST_QUARTER,
43
+ MoonPhaseName.FULL_MOON,
44
+ MoonPhaseName.LAST_QUARTER
45
+ ]).describe('Moon phase to find'),
46
+ date: z.string().optional().describe('Starting date (YYYY-MM-DD format). Defaults to current date.'),
47
+ count: z.number().positive().optional().describe('Number of occurrences to return. Defaults to 1.'),
48
+ format: z.enum(['json', 'text']).optional().describe('Output format (json or text)')
49
+ });
50
+ /**
51
+ * Service for moon phase calculations
52
+ */
53
+ export class MoonPhaseService {
54
+ /**
55
+ * Get the moon phase for a specific date
56
+ * @param params Parameters for the request
57
+ * @returns Moon phase information
58
+ */
59
+ getMoonPhase(params) {
60
+ const date = params.date ? new Date(params.date) : new Date();
61
+ // Get moon illumination data
62
+ const illuminationData = SunCalc.getMoonIllumination(date);
63
+ // Get moon position data (requires location)
64
+ const latitude = params.latitude ?? 0;
65
+ const longitude = params.longitude ?? 0;
66
+ const positionData = SunCalc.getMoonPosition(date, latitude, longitude);
67
+ // Calculate moon phase name
68
+ const phaseName = this.getMoonPhaseName(illuminationData.phase);
69
+ // Calculate if the moon is waxing (increasing illumination)
70
+ const isWaxing = illuminationData.phase < 0.5;
71
+ // Calculate approximate moon age (0-29.53 days)
72
+ const lunarMonth = 29.53; // days
73
+ const age = illuminationData.phase * lunarMonth;
74
+ // Calculate apparent diameter (in degrees)
75
+ const diameter = 0.5181 * (384400 / positionData.distance);
76
+ return {
77
+ date: date.toISOString().split('T')[0],
78
+ phase: illuminationData.phase,
79
+ phaseName,
80
+ illumination: illuminationData.fraction,
81
+ age,
82
+ distance: positionData.distance,
83
+ diameter,
84
+ isWaxing
85
+ };
86
+ }
87
+ /**
88
+ * Get moon phases for a date range
89
+ * @param params Parameters for the request
90
+ * @returns Array of moon phase information
91
+ */
92
+ getMoonPhasesRange(params) {
93
+ const startDate = new Date(params.start_date);
94
+ const endDate = new Date(params.end_date);
95
+ if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
96
+ throw new Error('Invalid date format. Please use YYYY-MM-DD format.');
97
+ }
98
+ if (startDate > endDate) {
99
+ throw new Error('Start date must be before end date.');
100
+ }
101
+ const result = [];
102
+ const currentDate = new Date(startDate);
103
+ while (currentDate <= endDate) {
104
+ result.push(this.getMoonPhase({
105
+ date: currentDate.toISOString().split('T')[0],
106
+ latitude: params.latitude,
107
+ longitude: params.longitude
108
+ }));
109
+ // Move to next day
110
+ currentDate.setDate(currentDate.getDate() + 1);
111
+ }
112
+ return result;
113
+ }
114
+ /**
115
+ * Get the next occurrence(s) of a specific moon phase
116
+ * @param params Parameters for the request
117
+ * @returns Array of dates for the next occurrences of the specified phase
118
+ */
119
+ getNextMoonPhase(params) {
120
+ const startDate = params.date ? new Date(params.date) : new Date();
121
+ const count = params.count || 1;
122
+ const targetPhase = params.phase;
123
+ // Map phase names to their approximate values
124
+ const phaseValues = {
125
+ [MoonPhaseName.NEW_MOON]: 0,
126
+ [MoonPhaseName.FIRST_QUARTER]: 0.25,
127
+ [MoonPhaseName.FULL_MOON]: 0.5,
128
+ [MoonPhaseName.LAST_QUARTER]: 0.75
129
+ };
130
+ const targetPhaseValue = phaseValues[targetPhase];
131
+ const results = [];
132
+ let currentDate = new Date(startDate);
133
+ // Find the next occurrences
134
+ while (results.length < count) {
135
+ // Check every day (could be optimized with better algorithms)
136
+ const illuminationData = SunCalc.getMoonIllumination(currentDate);
137
+ const prevDate = new Date(currentDate);
138
+ prevDate.setDate(prevDate.getDate() - 1);
139
+ const prevIlluminationData = SunCalc.getMoonIllumination(prevDate);
140
+ // Check if we've passed the target phase between yesterday and today
141
+ const prevDiff = Math.abs(prevIlluminationData.phase - targetPhaseValue);
142
+ const currentDiff = Math.abs(illuminationData.phase - targetPhaseValue);
143
+ // If we're getting closer to the target phase and then further away, we've passed it
144
+ // Or if we're very close to the target phase (within 0.01)
145
+ if ((prevDiff > currentDiff && currentDiff < 0.01) || currentDiff < 0.005) {
146
+ results.push({
147
+ date: currentDate.toISOString().split('T')[0],
148
+ phase: targetPhase
149
+ });
150
+ }
151
+ // Move to next day
152
+ currentDate.setDate(currentDate.getDate() + 1);
153
+ // Safety check to prevent infinite loops
154
+ if (results.length === 0 && currentDate.getTime() - startDate.getTime() > 366 * 24 * 60 * 60 * 1000) {
155
+ throw new Error('Could not find the specified moon phase within a year.');
156
+ }
157
+ }
158
+ return results;
159
+ }
160
+ /**
161
+ * Get the name of the moon phase based on the phase value
162
+ * @param phase Phase value (0-1)
163
+ * @returns Moon phase name
164
+ */
165
+ getMoonPhaseName(phase) {
166
+ // Normalize phase to 0-1 range
167
+ phase = phase % 1;
168
+ if (phase < 0)
169
+ phase += 1;
170
+ // Determine phase name based on value ranges
171
+ if (phase < 0.0625 || phase >= 0.9375)
172
+ return MoonPhaseName.NEW_MOON;
173
+ if (phase < 0.1875)
174
+ return MoonPhaseName.WAXING_CRESCENT;
175
+ if (phase < 0.3125)
176
+ return MoonPhaseName.FIRST_QUARTER;
177
+ if (phase < 0.4375)
178
+ return MoonPhaseName.WAXING_GIBBOUS;
179
+ if (phase < 0.5625)
180
+ return MoonPhaseName.FULL_MOON;
181
+ if (phase < 0.6875)
182
+ return MoonPhaseName.WANING_GIBBOUS;
183
+ if (phase < 0.8125)
184
+ return MoonPhaseName.LAST_QUARTER;
185
+ return MoonPhaseName.WANING_CRESCENT;
186
+ }
187
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Configuration options for the NOAA service
3
+ */
4
+ export interface NoaaConfig {
5
+ applicationName?: string;
6
+ }
7
+ /**
8
+ * Service for interacting with NOAA Tides and Currents APIs
9
+ */
10
+ export declare class NoaaService {
11
+ private config;
12
+ constructor(config?: NoaaConfig);
13
+ /**
14
+ * Build parameters for the API request
15
+ * @param params Parameters for the request
16
+ * @returns URL-encoded parameters string
17
+ */
18
+ private buildParams;
19
+ /**
20
+ * Make a request to the Data API
21
+ * @param params Parameters for the request
22
+ * @returns Response data
23
+ */
24
+ fetchDataApi(params: Record<string, any>): Promise<any>;
25
+ /**
26
+ * Make a request to the Metadata API
27
+ * @param endpoint Endpoint path
28
+ * @param params Parameters for the request
29
+ * @returns Response data
30
+ */
31
+ fetchMetadataApi(endpoint: string, params?: Record<string, any>): Promise<any>;
32
+ /**
33
+ * Get water level data
34
+ */
35
+ getWaterLevels(params: Record<string, any>): Promise<any>;
36
+ /**
37
+ * Get tide predictions
38
+ */
39
+ getTidePredictions(params: Record<string, any>): Promise<any>;
40
+ /**
41
+ * Get currents data
42
+ */
43
+ getCurrents(params: Record<string, any>): Promise<any>;
44
+ /**
45
+ * Get current predictions
46
+ */
47
+ getCurrentPredictions(params: Record<string, any>): Promise<any>;
48
+ /**
49
+ * Get meteorological data (air_temperature, wind, etc.)
50
+ */
51
+ getMeteorologicalData(params: Record<string, any>): Promise<any>;
52
+ /**
53
+ * Get list of stations
54
+ */
55
+ getStations(params: Record<string, any>): Promise<any>;
56
+ /**
57
+ * Get station details
58
+ */
59
+ getStationDetails(params: Record<string, any>): Promise<any>;
60
+ }
@@ -0,0 +1,159 @@
1
+ import axios from 'axios';
2
+ // Base URLs for the different NOAA APIs
3
+ const DATA_API_BASE_URL = 'https://api.tidesandcurrents.noaa.gov/api/prod/datagetter';
4
+ const METADATA_API_BASE_URL = 'https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi';
5
+ /**
6
+ * Service for interacting with NOAA Tides and Currents APIs
7
+ */
8
+ export class NoaaService {
9
+ config;
10
+ constructor(config = {}) {
11
+ this.config = config;
12
+ }
13
+ /**
14
+ * Build parameters for the API request
15
+ * @param params Parameters for the request
16
+ * @returns URL-encoded parameters string
17
+ */
18
+ buildParams(params) {
19
+ // Add application name if provided in config
20
+ if (this.config.applicationName) {
21
+ params.application = this.config.applicationName;
22
+ }
23
+ // Remove undefined and null values
24
+ const filteredParams = Object.entries(params)
25
+ .filter(([_, value]) => value !== undefined && value !== null)
26
+ .reduce((acc, [key, value]) => {
27
+ acc[key] = value;
28
+ return acc;
29
+ }, {});
30
+ // Convert to URL parameters
31
+ return new URLSearchParams(filteredParams).toString();
32
+ }
33
+ /**
34
+ * Make a request to the Data API
35
+ * @param params Parameters for the request
36
+ * @returns Response data
37
+ */
38
+ async fetchDataApi(params) {
39
+ try {
40
+ const queryParams = this.buildParams(params);
41
+ const url = `${DATA_API_BASE_URL}?${queryParams}`;
42
+ const response = await axios.get(url);
43
+ return response.data;
44
+ }
45
+ catch (error) {
46
+ if (axios.isAxiosError(error) && error.response) {
47
+ throw new Error(`NOAA API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
48
+ }
49
+ throw error;
50
+ }
51
+ }
52
+ /**
53
+ * Make a request to the Metadata API
54
+ * @param endpoint Endpoint path
55
+ * @param params Parameters for the request
56
+ * @returns Response data
57
+ */
58
+ async fetchMetadataApi(endpoint, params = {}) {
59
+ try {
60
+ const queryParams = this.buildParams(params);
61
+ const url = `${METADATA_API_BASE_URL}${endpoint}${queryParams ? '?' + queryParams : ''}`;
62
+ const response = await axios.get(url);
63
+ return response.data;
64
+ }
65
+ catch (error) {
66
+ if (axios.isAxiosError(error) && error.response) {
67
+ throw new Error(`NOAA API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
68
+ }
69
+ throw error;
70
+ }
71
+ }
72
+ /**
73
+ * Get water level data
74
+ */
75
+ async getWaterLevels(params) {
76
+ return this.fetchDataApi({
77
+ ...params,
78
+ product: 'water_level'
79
+ });
80
+ }
81
+ /**
82
+ * Get tide predictions
83
+ */
84
+ async getTidePredictions(params) {
85
+ return this.fetchDataApi({
86
+ ...params,
87
+ product: 'predictions'
88
+ });
89
+ }
90
+ /**
91
+ * Get currents data
92
+ */
93
+ async getCurrents(params) {
94
+ return this.fetchDataApi({
95
+ ...params,
96
+ product: 'currents'
97
+ });
98
+ }
99
+ /**
100
+ * Get current predictions
101
+ */
102
+ async getCurrentPredictions(params) {
103
+ return this.fetchDataApi({
104
+ ...params,
105
+ product: 'currents_predictions'
106
+ });
107
+ }
108
+ /**
109
+ * Get meteorological data (air_temperature, wind, etc.)
110
+ */
111
+ async getMeteorologicalData(params) {
112
+ const { product, ...rest } = params;
113
+ return this.fetchDataApi({
114
+ ...rest,
115
+ product
116
+ });
117
+ }
118
+ /**
119
+ * Get list of stations
120
+ */
121
+ async getStations(params) {
122
+ const { type, name, lat_min, lat_max, lon_min, lon_max, state, limit, offset, sort_by, sort_order, ...rest } = params;
123
+ const endpoint = '/stations.' + (rest.format || 'json');
124
+ // Build query parameters with all the filters
125
+ const queryParams = { ...rest };
126
+ // Add filters only if they are defined
127
+ if (type)
128
+ queryParams.type = type;
129
+ if (name)
130
+ queryParams.name = name;
131
+ if (lat_min !== undefined)
132
+ queryParams.lat_min = lat_min;
133
+ if (lat_max !== undefined)
134
+ queryParams.lat_max = lat_max;
135
+ if (lon_min !== undefined)
136
+ queryParams.lon_min = lon_min;
137
+ if (lon_max !== undefined)
138
+ queryParams.lon_max = lon_max;
139
+ if (state)
140
+ queryParams.state = state;
141
+ if (limit !== undefined)
142
+ queryParams.limit = limit;
143
+ if (offset !== undefined)
144
+ queryParams.offset = offset;
145
+ if (sort_by)
146
+ queryParams.sort_by = sort_by;
147
+ if (sort_order)
148
+ queryParams.sort_order = sort_order;
149
+ return this.fetchMetadataApi(endpoint, queryParams);
150
+ }
151
+ /**
152
+ * Get station details
153
+ */
154
+ async getStationDetails(params) {
155
+ const { station, ...rest } = params;
156
+ const endpoint = `/stations/${station}/details.` + (rest.format || 'json');
157
+ return this.fetchMetadataApi(endpoint, rest);
158
+ }
159
+ }
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod';
2
+ export declare const StationSchema: z.ZodString;
3
+ export declare const DateSchema: z.ZodOptional<z.ZodString>;
4
+ export declare const BeginDateSchema: z.ZodOptional<z.ZodString>;
5
+ export declare const EndDateSchema: z.ZodOptional<z.ZodString>;
6
+ export declare const RangeSchema: z.ZodOptional<z.ZodNumber>;
7
+ export declare const DatumSchema: z.ZodOptional<z.ZodString>;
8
+ export declare const UnitsSchema: z.ZodOptional<z.ZodEnum<["english", "metric"]>>;
9
+ export declare const TimeZoneSchema: z.ZodOptional<z.ZodEnum<["gmt", "lst", "lst_ldt"]>>;
10
+ export declare const FormatSchema: z.ZodOptional<z.ZodEnum<["json", "xml", "csv"]>>;
11
+ export declare const BinSchema: z.ZodOptional<z.ZodNumber>;
12
+ export declare const IntervalSchema: z.ZodOptional<z.ZodString>;
13
+ export declare const refineDateParams: (data: any) => any;
14
+ export declare const dateRefinementMessage = "You must provide either 'date', 'begin_date' and 'end_date', 'begin_date' and 'range', 'end_date' and 'range', or just 'range'";
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ // Common parameter schemas
3
+ export const StationSchema = z.string().min(1).describe('Station ID');
4
+ export const DateSchema = z.string().optional().describe('Date to retrieve data for ("today", "latest", "recent", or specific date)');
5
+ export const BeginDateSchema = z.string().optional().describe('Start date (YYYYMMDD or MM/DD/YYYY)');
6
+ export const EndDateSchema = z.string().optional().describe('End date (YYYYMMDD or MM/DD/YYYY)');
7
+ export const RangeSchema = z.number().optional().describe('Number of hours to retrieve data for');
8
+ export const DatumSchema = z.string().optional().describe('Datum to use (MLLW, MSL, etc.)');
9
+ export const UnitsSchema = z.enum(['english', 'metric']).optional().describe('Units to use ("english" or "metric")');
10
+ export const TimeZoneSchema = z.enum(['gmt', 'lst', 'lst_ldt']).optional().describe('Time zone (gmt, lst, lst_ldt)');
11
+ export const FormatSchema = z.enum(['json', 'xml', 'csv']).optional().describe('Output format (json, xml, csv)');
12
+ export const BinSchema = z.number().optional().describe('Bin number');
13
+ export const IntervalSchema = z.string().optional().describe('Interval (hilo, hl, h, or a number for minutes)');
14
+ // Schema refinement function for date parameters
15
+ export const refineDateParams = (data) => (data.date || (data.begin_date && data.end_date) ||
16
+ (data.begin_date && data.range) || (data.end_date && data.range) ||
17
+ data.range);
18
+ export const dateRefinementMessage = "You must provide either 'date', 'begin_date' and 'end_date', 'begin_date' and 'range', 'end_date' and 'range', or just 'range'";