@jablum/weather-mcp 1.7.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/LICENSE +21 -0
- package/README.md +1319 -0
- package/dist/analytics/anonymizer.d.ts +37 -0
- package/dist/analytics/anonymizer.d.ts.map +1 -0
- package/dist/analytics/anonymizer.js +112 -0
- package/dist/analytics/anonymizer.js.map +1 -0
- package/dist/analytics/collector.d.ts +72 -0
- package/dist/analytics/collector.d.ts.map +1 -0
- package/dist/analytics/collector.js +282 -0
- package/dist/analytics/collector.js.map +1 -0
- package/dist/analytics/config.d.ts +15 -0
- package/dist/analytics/config.d.ts.map +1 -0
- package/dist/analytics/config.js +172 -0
- package/dist/analytics/config.js.map +1 -0
- package/dist/analytics/index.d.ts +8 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/index.js +7 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/middleware.d.ts +33 -0
- package/dist/analytics/middleware.d.ts.map +1 -0
- package/dist/analytics/middleware.js +99 -0
- package/dist/analytics/middleware.js.map +1 -0
- package/dist/analytics/transport.d.ts +11 -0
- package/dist/analytics/transport.d.ts.map +1 -0
- package/dist/analytics/transport.js +92 -0
- package/dist/analytics/transport.js.map +1 -0
- package/dist/analytics/types.d.ts +74 -0
- package/dist/analytics/types.d.ts.map +1 -0
- package/dist/analytics/types.js +6 -0
- package/dist/analytics/types.js.map +1 -0
- package/dist/config/api.d.ts +30 -0
- package/dist/config/api.d.ts.map +1 -0
- package/dist/config/api.js +32 -0
- package/dist/config/api.js.map +1 -0
- package/dist/config/cache.d.ts +31 -0
- package/dist/config/cache.d.ts.map +1 -0
- package/dist/config/cache.js +108 -0
- package/dist/config/cache.js.map +1 -0
- package/dist/config/displayThresholds.d.ts +83 -0
- package/dist/config/displayThresholds.d.ts.map +1 -0
- package/dist/config/displayThresholds.js +83 -0
- package/dist/config/displayThresholds.js.map +1 -0
- package/dist/config/tools.d.ts +44 -0
- package/dist/config/tools.d.ts.map +1 -0
- package/dist/config/tools.js +269 -0
- package/dist/config/tools.js.map +1 -0
- package/dist/errors/ApiError.d.ts +62 -0
- package/dist/errors/ApiError.d.ts.map +1 -0
- package/dist/errors/ApiError.js +171 -0
- package/dist/errors/ApiError.js.map +1 -0
- package/dist/handlers/airQualityHandler.d.ts +11 -0
- package/dist/handlers/airQualityHandler.d.ts.map +1 -0
- package/dist/handlers/airQualityHandler.js +154 -0
- package/dist/handlers/airQualityHandler.js.map +1 -0
- package/dist/handlers/alertsHandler.d.ts +11 -0
- package/dist/handlers/alertsHandler.d.ts.map +1 -0
- package/dist/handlers/alertsHandler.js +98 -0
- package/dist/handlers/alertsHandler.js.map +1 -0
- package/dist/handlers/currentConditionsHandler.d.ts +13 -0
- package/dist/handlers/currentConditionsHandler.d.ts.map +1 -0
- package/dist/handlers/currentConditionsHandler.js +296 -0
- package/dist/handlers/currentConditionsHandler.js.map +1 -0
- package/dist/handlers/forecastHandler.d.ts +16 -0
- package/dist/handlers/forecastHandler.d.ts.map +1 -0
- package/dist/handlers/forecastHandler.js +454 -0
- package/dist/handlers/forecastHandler.js.map +1 -0
- package/dist/handlers/historicalWeatherHandler.d.ts +12 -0
- package/dist/handlers/historicalWeatherHandler.d.ts.map +1 -0
- package/dist/handlers/historicalWeatherHandler.js +188 -0
- package/dist/handlers/historicalWeatherHandler.js.map +1 -0
- package/dist/handlers/lightningHandler.d.ts +14 -0
- package/dist/handlers/lightningHandler.d.ts.map +1 -0
- package/dist/handlers/lightningHandler.js +258 -0
- package/dist/handlers/lightningHandler.js.map +1 -0
- package/dist/handlers/locationHandler.d.ts +12 -0
- package/dist/handlers/locationHandler.d.ts.map +1 -0
- package/dist/handlers/locationHandler.js +149 -0
- package/dist/handlers/locationHandler.js.map +1 -0
- package/dist/handlers/marineConditionsHandler.d.ts +13 -0
- package/dist/handlers/marineConditionsHandler.d.ts.map +1 -0
- package/dist/handlers/marineConditionsHandler.js +270 -0
- package/dist/handlers/marineConditionsHandler.js.map +1 -0
- package/dist/handlers/riverConditionsHandler.d.ts +11 -0
- package/dist/handlers/riverConditionsHandler.d.ts.map +1 -0
- package/dist/handlers/riverConditionsHandler.js +176 -0
- package/dist/handlers/riverConditionsHandler.js.map +1 -0
- package/dist/handlers/savedLocationsHandler.d.ts +50 -0
- package/dist/handlers/savedLocationsHandler.d.ts.map +1 -0
- package/dist/handlers/savedLocationsHandler.js +397 -0
- package/dist/handlers/savedLocationsHandler.js.map +1 -0
- package/dist/handlers/statusHandler.d.ts +12 -0
- package/dist/handlers/statusHandler.d.ts.map +1 -0
- package/dist/handlers/statusHandler.js +115 -0
- package/dist/handlers/statusHandler.js.map +1 -0
- package/dist/handlers/weatherImageryHandler.d.ts +14 -0
- package/dist/handlers/weatherImageryHandler.d.ts.map +1 -0
- package/dist/handlers/weatherImageryHandler.js +143 -0
- package/dist/handlers/weatherImageryHandler.js.map +1 -0
- package/dist/handlers/wildfireHandler.d.ts +11 -0
- package/dist/handlers/wildfireHandler.d.ts.map +1 -0
- package/dist/handlers/wildfireHandler.js +186 -0
- package/dist/handlers/wildfireHandler.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +735 -0
- package/dist/index.js.map +1 -0
- package/dist/services/blitzortung.d.ts +67 -0
- package/dist/services/blitzortung.d.ts.map +1 -0
- package/dist/services/blitzortung.js +475 -0
- package/dist/services/blitzortung.js.map +1 -0
- package/dist/services/geocoding.d.ts +57 -0
- package/dist/services/geocoding.d.ts.map +1 -0
- package/dist/services/geocoding.js +393 -0
- package/dist/services/geocoding.js.map +1 -0
- package/dist/services/locationStore.d.ts +62 -0
- package/dist/services/locationStore.d.ts.map +1 -0
- package/dist/services/locationStore.js +201 -0
- package/dist/services/locationStore.js.map +1 -0
- package/dist/services/ncei.d.ts +61 -0
- package/dist/services/ncei.d.ts.map +1 -0
- package/dist/services/ncei.js +126 -0
- package/dist/services/ncei.js.map +1 -0
- package/dist/services/nifc.d.ts +44 -0
- package/dist/services/nifc.d.ts.map +1 -0
- package/dist/services/nifc.js +159 -0
- package/dist/services/nifc.js.map +1 -0
- package/dist/services/noaa.d.ts +161 -0
- package/dist/services/noaa.d.ts.map +1 -0
- package/dist/services/noaa.js +681 -0
- package/dist/services/noaa.js.map +1 -0
- package/dist/services/nominatim.d.ts +62 -0
- package/dist/services/nominatim.d.ts.map +1 -0
- package/dist/services/nominatim.js +254 -0
- package/dist/services/nominatim.js.map +1 -0
- package/dist/services/openmeteo.d.ts +189 -0
- package/dist/services/openmeteo.d.ts.map +1 -0
- package/dist/services/openmeteo.js +936 -0
- package/dist/services/openmeteo.js.map +1 -0
- package/dist/services/rainviewer.d.ts +37 -0
- package/dist/services/rainviewer.d.ts.map +1 -0
- package/dist/services/rainviewer.js +115 -0
- package/dist/services/rainviewer.js.map +1 -0
- package/dist/types/imagery.d.ts +82 -0
- package/dist/types/imagery.d.ts.map +1 -0
- package/dist/types/imagery.js +6 -0
- package/dist/types/imagery.js.map +1 -0
- package/dist/types/lightning.d.ts +89 -0
- package/dist/types/lightning.d.ts.map +1 -0
- package/dist/types/lightning.js +6 -0
- package/dist/types/lightning.js.map +1 -0
- package/dist/types/noaa.d.ts +535 -0
- package/dist/types/noaa.d.ts.map +1 -0
- package/dist/types/noaa.js +5 -0
- package/dist/types/noaa.js.map +1 -0
- package/dist/types/nominatim.d.ts +72 -0
- package/dist/types/nominatim.d.ts.map +1 -0
- package/dist/types/nominatim.js +6 -0
- package/dist/types/nominatim.js.map +1 -0
- package/dist/types/openmeteo.d.ts +583 -0
- package/dist/types/openmeteo.d.ts.map +1 -0
- package/dist/types/openmeteo.js +6 -0
- package/dist/types/openmeteo.js.map +1 -0
- package/dist/types/savedLocations.d.ts +58 -0
- package/dist/types/savedLocations.d.ts.map +1 -0
- package/dist/types/savedLocations.js +5 -0
- package/dist/types/savedLocations.js.map +1 -0
- package/dist/types/wildfire.d.ts +83 -0
- package/dist/types/wildfire.d.ts.map +1 -0
- package/dist/types/wildfire.js +5 -0
- package/dist/types/wildfire.js.map +1 -0
- package/dist/utils/airQuality.d.ts +54 -0
- package/dist/utils/airQuality.d.ts.map +1 -0
- package/dist/utils/airQuality.js +251 -0
- package/dist/utils/airQuality.js.map +1 -0
- package/dist/utils/cache.d.ts +69 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +164 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/distance.d.ts +25 -0
- package/dist/utils/distance.d.ts.map +1 -0
- package/dist/utils/distance.js +40 -0
- package/dist/utils/distance.js.map +1 -0
- package/dist/utils/fireWeather.d.ts +76 -0
- package/dist/utils/fireWeather.d.ts.map +1 -0
- package/dist/utils/fireWeather.js +243 -0
- package/dist/utils/fireWeather.js.map +1 -0
- package/dist/utils/geography.d.ts +79 -0
- package/dist/utils/geography.d.ts.map +1 -0
- package/dist/utils/geography.js +266 -0
- package/dist/utils/geography.js.map +1 -0
- package/dist/utils/geohash.d.ts +62 -0
- package/dist/utils/geohash.d.ts.map +1 -0
- package/dist/utils/geohash.js +146 -0
- package/dist/utils/geohash.js.map +1 -0
- package/dist/utils/locationResolver.d.ts +34 -0
- package/dist/utils/locationResolver.d.ts.map +1 -0
- package/dist/utils/locationResolver.js +120 -0
- package/dist/utils/locationResolver.js.map +1 -0
- package/dist/utils/logger.d.ts +75 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +153 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/marine.d.ts +59 -0
- package/dist/utils/marine.d.ts.map +1 -0
- package/dist/utils/marine.js +215 -0
- package/dist/utils/marine.js.map +1 -0
- package/dist/utils/normals.d.ts +86 -0
- package/dist/utils/normals.d.ts.map +1 -0
- package/dist/utils/normals.js +223 -0
- package/dist/utils/normals.js.map +1 -0
- package/dist/utils/snow.d.ts +45 -0
- package/dist/utils/snow.d.ts.map +1 -0
- package/dist/utils/snow.js +144 -0
- package/dist/utils/snow.js.map +1 -0
- package/dist/utils/temperatureConversion.d.ts +12 -0
- package/dist/utils/temperatureConversion.d.ts.map +1 -0
- package/dist/utils/temperatureConversion.js +17 -0
- package/dist/utils/temperatureConversion.js.map +1 -0
- package/dist/utils/timezone.d.ts +56 -0
- package/dist/utils/timezone.d.ts.map +1 -0
- package/dist/utils/timezone.js +167 -0
- package/dist/utils/timezone.js.map +1 -0
- package/dist/utils/units.d.ts +69 -0
- package/dist/utils/units.d.ts.map +1 -0
- package/dist/utils/units.js +158 -0
- package/dist/utils/units.js.map +1 -0
- package/dist/utils/validation.d.ts +89 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +177 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/version.d.ts +15 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +24 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics configuration and singleton collector instance
|
|
3
|
+
* Loads settings from environment variables with secure defaults
|
|
4
|
+
*/
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
import { logger } from '../utils/logger.js';
|
|
13
|
+
import { AnalyticsCollector } from './collector.js';
|
|
14
|
+
// Read version from package.json to ensure single source of truth
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf-8'));
|
|
18
|
+
/**
|
|
19
|
+
* Default analytics endpoint (production analytics server)
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_ENDPOINT = 'https://analytics.weather-mcp.com/v1/events';
|
|
22
|
+
/**
|
|
23
|
+
* Validate analytics endpoint for security
|
|
24
|
+
* Prevents SSRF attacks and enforces HTTPS
|
|
25
|
+
*/
|
|
26
|
+
function validateAnalyticsEndpoint(endpoint) {
|
|
27
|
+
let url;
|
|
28
|
+
try {
|
|
29
|
+
url = new URL(endpoint);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new Error('Invalid ANALYTICS_ENDPOINT: must be a valid URL');
|
|
33
|
+
}
|
|
34
|
+
// SECURITY: Only allow HTTPS
|
|
35
|
+
if (url.protocol !== 'https:') {
|
|
36
|
+
throw new Error('Invalid ANALYTICS_ENDPOINT: must use HTTPS protocol');
|
|
37
|
+
}
|
|
38
|
+
// SECURITY: Prevent SSRF to internal networks
|
|
39
|
+
const hostname = url.hostname.toLowerCase();
|
|
40
|
+
if (hostname === 'localhost' ||
|
|
41
|
+
hostname === '127.0.0.1' ||
|
|
42
|
+
hostname === '::1' ||
|
|
43
|
+
hostname.startsWith('10.') ||
|
|
44
|
+
hostname.startsWith('172.16.') ||
|
|
45
|
+
hostname.startsWith('172.17.') ||
|
|
46
|
+
hostname.startsWith('172.18.') ||
|
|
47
|
+
hostname.startsWith('172.19.') ||
|
|
48
|
+
hostname.startsWith('172.20.') ||
|
|
49
|
+
hostname.startsWith('172.21.') ||
|
|
50
|
+
hostname.startsWith('172.22.') ||
|
|
51
|
+
hostname.startsWith('172.23.') ||
|
|
52
|
+
hostname.startsWith('172.24.') ||
|
|
53
|
+
hostname.startsWith('172.25.') ||
|
|
54
|
+
hostname.startsWith('172.26.') ||
|
|
55
|
+
hostname.startsWith('172.27.') ||
|
|
56
|
+
hostname.startsWith('172.28.') ||
|
|
57
|
+
hostname.startsWith('172.29.') ||
|
|
58
|
+
hostname.startsWith('172.30.') ||
|
|
59
|
+
hostname.startsWith('172.31.') ||
|
|
60
|
+
hostname.startsWith('192.168.') ||
|
|
61
|
+
hostname.startsWith('169.254.') || // Link-local
|
|
62
|
+
hostname.endsWith('.local')) {
|
|
63
|
+
throw new Error('Invalid ANALYTICS_ENDPOINT: cannot point to internal network');
|
|
64
|
+
}
|
|
65
|
+
// SECURITY: Require domain name (not IP address)
|
|
66
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
|
|
67
|
+
throw new Error('Invalid ANALYTICS_ENDPOINT: IP addresses not allowed, use domain name');
|
|
68
|
+
}
|
|
69
|
+
// SECURITY: Validate port range
|
|
70
|
+
const port = url.port ? parseInt(url.port) : 443;
|
|
71
|
+
if (port < 1 || port > 65535 || (port !== 443 && port < 1024)) {
|
|
72
|
+
throw new Error('Invalid ANALYTICS_ENDPOINT: invalid port number');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get or generate analytics salt for session ID hashing
|
|
77
|
+
* Generates a unique salt per installation and persists it
|
|
78
|
+
*/
|
|
79
|
+
function getOrGenerateAnalyticsSalt() {
|
|
80
|
+
// Check environment variable first
|
|
81
|
+
if (process.env.ANALYTICS_SALT) {
|
|
82
|
+
return process.env.ANALYTICS_SALT;
|
|
83
|
+
}
|
|
84
|
+
// Store in user's config directory (NOT in project directory)
|
|
85
|
+
const configDir = path.join(os.homedir(), '.weather-mcp');
|
|
86
|
+
const saltFile = path.join(configDir, 'analytics-salt');
|
|
87
|
+
try {
|
|
88
|
+
if (fs.existsSync(saltFile)) {
|
|
89
|
+
return fs.readFileSync(saltFile, 'utf8').trim();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
logger.warn('Could not read analytics salt file', {
|
|
94
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Generate new random salt
|
|
98
|
+
const newSalt = crypto.randomBytes(32).toString('hex');
|
|
99
|
+
try {
|
|
100
|
+
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
101
|
+
fs.writeFileSync(saltFile, newSalt, { mode: 0o600 });
|
|
102
|
+
logger.info('Generated new analytics salt');
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
logger.warn('Could not persist analytics salt', {
|
|
106
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
107
|
+
});
|
|
108
|
+
// Continue with in-memory salt (regenerates each restart)
|
|
109
|
+
}
|
|
110
|
+
return newSalt;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Load and validate analytics configuration from environment variables
|
|
114
|
+
*/
|
|
115
|
+
function loadAnalyticsConfig() {
|
|
116
|
+
// Analytics disabled by default (users must opt-in)
|
|
117
|
+
const enabled = process.env.ANALYTICS_ENABLED === 'true';
|
|
118
|
+
// Analytics level: minimal (default), standard, detailed
|
|
119
|
+
let level = 'minimal';
|
|
120
|
+
const levelEnv = process.env.ANALYTICS_LEVEL?.toLowerCase();
|
|
121
|
+
if (levelEnv === 'standard' || levelEnv === 'detailed') {
|
|
122
|
+
level = levelEnv;
|
|
123
|
+
}
|
|
124
|
+
else if (levelEnv && levelEnv !== 'minimal') {
|
|
125
|
+
logger.warn('Invalid ANALYTICS_LEVEL, using minimal', {
|
|
126
|
+
provided: levelEnv,
|
|
127
|
+
securityEvent: true,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
// Analytics endpoint (custom server or default)
|
|
131
|
+
const endpoint = process.env.ANALYTICS_ENDPOINT || DEFAULT_ENDPOINT;
|
|
132
|
+
// Validate endpoint for security
|
|
133
|
+
try {
|
|
134
|
+
validateAnalyticsEndpoint(endpoint);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
|
138
|
+
logger.error(`Invalid ANALYTICS_ENDPOINT configuration: ${errorMsg}`, error instanceof Error ? error : new Error(String(error)));
|
|
139
|
+
// Disable analytics if endpoint is invalid (fail-safe)
|
|
140
|
+
return {
|
|
141
|
+
enabled: false,
|
|
142
|
+
level: 'minimal',
|
|
143
|
+
endpoint: DEFAULT_ENDPOINT,
|
|
144
|
+
version: packageJson.version,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Salt for session ID hashing (auto-generated if not provided)
|
|
148
|
+
const salt = getOrGenerateAnalyticsSalt();
|
|
149
|
+
const config = {
|
|
150
|
+
enabled,
|
|
151
|
+
level,
|
|
152
|
+
endpoint,
|
|
153
|
+
version: packageJson.version,
|
|
154
|
+
salt,
|
|
155
|
+
};
|
|
156
|
+
if (enabled) {
|
|
157
|
+
logger.info('Analytics configuration loaded', {
|
|
158
|
+
level,
|
|
159
|
+
endpoint: endpoint === DEFAULT_ENDPOINT ? 'default' : 'custom',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
logger.info('Analytics disabled by user preference');
|
|
164
|
+
}
|
|
165
|
+
return config;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Singleton analytics collector instance
|
|
169
|
+
* Exported for use throughout the application
|
|
170
|
+
*/
|
|
171
|
+
export const analytics = new AnalyticsCollector(loadAnalyticsConfig());
|
|
172
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/analytics/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics module - Privacy-first usage tracking
|
|
3
|
+
* Implements anonymous, opt-out analytics as defined in docs/ANALYTICS_MCP_PLAN.md
|
|
4
|
+
*/
|
|
5
|
+
export { analytics } from './config.js';
|
|
6
|
+
export { withAnalytics, createMetadataExtractor } from './middleware.js';
|
|
7
|
+
export type { AnalyticsLevel, ToolExecutionMetadata } from './types.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AACzE,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics module - Privacy-first usage tracking
|
|
3
|
+
* Implements anonymous, opt-out analytics as defined in docs/ANALYTICS_MCP_PLAN.md
|
|
4
|
+
*/
|
|
5
|
+
export { analytics } from './config.js';
|
|
6
|
+
export { withAnalytics, createMetadataExtractor } from './middleware.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics middleware for tool handlers
|
|
3
|
+
* Automatically tracks tool executions with performance metrics
|
|
4
|
+
*/
|
|
5
|
+
import { ToolExecutionMetadata } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Wrapper function that adds analytics tracking to tool handlers
|
|
8
|
+
* Captures execution time, errors, and custom metadata
|
|
9
|
+
*
|
|
10
|
+
* @param toolName - Name of the MCP tool being executed
|
|
11
|
+
* @param handler - The tool handler function to wrap
|
|
12
|
+
* @param metadataExtractor - Optional function to extract metadata from handler result
|
|
13
|
+
* @returns Wrapped handler with analytics tracking
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* export async function getForecastHandler(args: ForecastArgs) {
|
|
18
|
+
* return withAnalytics('get_forecast', async () => {
|
|
19
|
+
* // ... handler implementation
|
|
20
|
+
* return result;
|
|
21
|
+
* }, (result) => ({
|
|
22
|
+
* service: result.source,
|
|
23
|
+
* cache_hit: result.cached,
|
|
24
|
+
* }));
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function withAnalytics<T>(toolName: string, handler: () => Promise<T>, metadataExtractor?: (result: T) => Partial<ToolExecutionMetadata>): Promise<T>;
|
|
29
|
+
/**
|
|
30
|
+
* Helper to create metadata extractor for common patterns
|
|
31
|
+
*/
|
|
32
|
+
export declare function createMetadataExtractor<T>(extractor: (result: T) => Partial<ToolExecutionMetadata>): (result: T) => Partial<ToolExecutionMetadata>;
|
|
33
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/analytics/middleware.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,aAAa,CAAC,CAAC,EACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,qBAAqB,CAAC,GAChE,OAAO,CAAC,CAAC,CAAC,CAgEZ;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,qBAAqB,CAAC,GACvD,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAE/C"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics middleware for tool handlers
|
|
3
|
+
* Automatically tracks tool executions with performance metrics
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { analytics } from './config.js';
|
|
7
|
+
/**
|
|
8
|
+
* Wrapper function that adds analytics tracking to tool handlers
|
|
9
|
+
* Captures execution time, errors, and custom metadata
|
|
10
|
+
*
|
|
11
|
+
* @param toolName - Name of the MCP tool being executed
|
|
12
|
+
* @param handler - The tool handler function to wrap
|
|
13
|
+
* @param metadataExtractor - Optional function to extract metadata from handler result
|
|
14
|
+
* @returns Wrapped handler with analytics tracking
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* export async function getForecastHandler(args: ForecastArgs) {
|
|
19
|
+
* return withAnalytics('get_forecast', async () => {
|
|
20
|
+
* // ... handler implementation
|
|
21
|
+
* return result;
|
|
22
|
+
* }, (result) => ({
|
|
23
|
+
* service: result.source,
|
|
24
|
+
* cache_hit: result.cached,
|
|
25
|
+
* }));
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export async function withAnalytics(toolName, handler, metadataExtractor) {
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
let metadata = {};
|
|
32
|
+
try {
|
|
33
|
+
// Execute the handler
|
|
34
|
+
const result = await handler();
|
|
35
|
+
// Calculate response time
|
|
36
|
+
const responseTimeMs = Date.now() - startTime;
|
|
37
|
+
metadata.response_time_ms = responseTimeMs;
|
|
38
|
+
// Extract additional metadata from result if extractor provided
|
|
39
|
+
if (metadataExtractor) {
|
|
40
|
+
try {
|
|
41
|
+
const extracted = metadataExtractor(result);
|
|
42
|
+
metadata = { ...metadata, ...extracted };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
logger.warn('Analytics metadata extraction error', {
|
|
46
|
+
tool: toolName,
|
|
47
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Track successful execution
|
|
52
|
+
await analytics.trackToolCall(toolName, 'success', metadata);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
// Calculate response time even for errors
|
|
57
|
+
const responseTimeMs = Date.now() - startTime;
|
|
58
|
+
metadata.response_time_ms = responseTimeMs;
|
|
59
|
+
// Categorize error type
|
|
60
|
+
let errorType = 'unknown';
|
|
61
|
+
if (error instanceof Error) {
|
|
62
|
+
const errorName = error.constructor.name;
|
|
63
|
+
// Map error classes to analytics categories
|
|
64
|
+
if (errorName.includes('Validation') || errorName.includes('Invalid')) {
|
|
65
|
+
errorType = 'validation';
|
|
66
|
+
}
|
|
67
|
+
else if (errorName.includes('NotFound') || errorName.includes('404')) {
|
|
68
|
+
errorType = 'not_found';
|
|
69
|
+
}
|
|
70
|
+
else if (errorName.includes('RateLimit')) {
|
|
71
|
+
errorType = 'rate_limit';
|
|
72
|
+
}
|
|
73
|
+
else if (errorName.includes('Timeout')) {
|
|
74
|
+
errorType = 'timeout';
|
|
75
|
+
}
|
|
76
|
+
else if (errorName.includes('Network') || errorName.includes('Connection')) {
|
|
77
|
+
errorType = 'network';
|
|
78
|
+
}
|
|
79
|
+
else if (errorName.includes('Service') || errorName.includes('API')) {
|
|
80
|
+
errorType = 'service_error';
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
errorType = errorName.toLowerCase().replace('error', '').trim() || 'unknown';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
metadata.error_type = errorType;
|
|
87
|
+
// Track error execution
|
|
88
|
+
await analytics.trackToolCall(toolName, 'error', metadata);
|
|
89
|
+
// Re-throw the error to maintain normal error handling flow
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Helper to create metadata extractor for common patterns
|
|
95
|
+
*/
|
|
96
|
+
export function createMetadataExtractor(extractor) {
|
|
97
|
+
return extractor;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/analytics/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTPS transport layer for analytics events
|
|
3
|
+
* Sends batched events to analytics collection server
|
|
4
|
+
*/
|
|
5
|
+
import { AnalyticsEvent } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Send batch of analytics events to collection server
|
|
8
|
+
* Fails silently - analytics should never break the application
|
|
9
|
+
*/
|
|
10
|
+
export declare function sendBatch(events: AnalyticsEvent[], endpoint: string, version: string): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/analytics/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,MAAM,EAAE,cAAc,EAAE,EACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAwFf"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTPS transport layer for analytics events
|
|
3
|
+
* Sends batched events to analytics collection server
|
|
4
|
+
*/
|
|
5
|
+
import https from 'https';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
/**
|
|
8
|
+
* Send batch of analytics events to collection server
|
|
9
|
+
* Fails silently - analytics should never break the application
|
|
10
|
+
*/
|
|
11
|
+
export async function sendBatch(events, endpoint, version) {
|
|
12
|
+
if (events.length === 0) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const data = JSON.stringify({ events });
|
|
17
|
+
try {
|
|
18
|
+
const url = new URL(endpoint);
|
|
19
|
+
// SECURITY: Only allow HTTPS for analytics transmission (H-1)
|
|
20
|
+
if (url.protocol !== 'https:') {
|
|
21
|
+
const error = new Error(`Analytics endpoint must use HTTPS for secure transmission (got ${url.protocol})`);
|
|
22
|
+
logger.error('Analytics endpoint must use HTTPS', error);
|
|
23
|
+
reject(error);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const options = {
|
|
27
|
+
hostname: url.hostname,
|
|
28
|
+
port: url.port || 443,
|
|
29
|
+
path: url.pathname + url.search,
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
'Content-Length': Buffer.byteLength(data),
|
|
34
|
+
'User-Agent': `weather-mcp/${version}`,
|
|
35
|
+
},
|
|
36
|
+
timeout: 5000, // 5 second timeout
|
|
37
|
+
// SECURITY: Explicitly require valid certificates (H-2)
|
|
38
|
+
rejectUnauthorized: true,
|
|
39
|
+
};
|
|
40
|
+
const req = https.request(options, (res) => {
|
|
41
|
+
let responseData = '';
|
|
42
|
+
// Consume response data
|
|
43
|
+
res.on('data', (chunk) => {
|
|
44
|
+
responseData += chunk;
|
|
45
|
+
});
|
|
46
|
+
res.on('end', () => {
|
|
47
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
48
|
+
logger.debug('Analytics batch sent successfully', {
|
|
49
|
+
count: events.length,
|
|
50
|
+
statusCode: res.statusCode,
|
|
51
|
+
});
|
|
52
|
+
resolve();
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const error = new Error(`HTTP ${res.statusCode}`);
|
|
56
|
+
logger.warn('Analytics batch failed', {
|
|
57
|
+
statusCode: res.statusCode,
|
|
58
|
+
count: events.length,
|
|
59
|
+
error: error.message,
|
|
60
|
+
});
|
|
61
|
+
reject(error);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
req.on('error', (err) => {
|
|
66
|
+
logger.warn('Analytics request error', {
|
|
67
|
+
error: err.message,
|
|
68
|
+
count: events.length,
|
|
69
|
+
});
|
|
70
|
+
reject(err);
|
|
71
|
+
});
|
|
72
|
+
req.on('timeout', () => {
|
|
73
|
+
req.destroy();
|
|
74
|
+
const error = new Error('Request timeout');
|
|
75
|
+
logger.warn('Analytics request timeout', {
|
|
76
|
+
count: events.length,
|
|
77
|
+
});
|
|
78
|
+
reject(error);
|
|
79
|
+
});
|
|
80
|
+
req.write(data);
|
|
81
|
+
req.end();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.warn('Analytics transport error', {
|
|
85
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
86
|
+
count: events.length,
|
|
87
|
+
});
|
|
88
|
+
reject(error);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/analytics/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAwB,EACxB,QAAgB,EAChB,OAAe;IAEf,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAExC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;YAE9B,8DAA8D;YAC9D,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC9B,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,kEAAkE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;gBAC3G,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;gBACzD,MAAM,CAAC,KAAK,CAAC,CAAC;gBACd,OAAO;YACT,CAAC;YAED,MAAM,OAAO,GAAyB;gBACpC,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,GAAG;gBACrB,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;gBAC/B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;oBACzC,YAAY,EAAE,eAAe,OAAO,EAAE;iBACvC;gBACD,OAAO,EAAE,IAAI,EAAE,mBAAmB;gBAClC,wDAAwD;gBACxD,kBAAkB,EAAE,IAAI;aACzB,CAAC;YAEF,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzC,IAAI,YAAY,GAAG,EAAE,CAAC;gBAEtB,wBAAwB;gBACxB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBACvB,YAAY,IAAI,KAAK,CAAC;gBACxB,CAAC,CAAC,CAAC;gBAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,GAAG,GAAG,EAAE,CAAC;wBACpE,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE;4BAChD,KAAK,EAAE,MAAM,CAAC,MAAM;4BACpB,UAAU,EAAE,GAAG,CAAC,UAAU;yBAC3B,CAAC,CAAC;wBACH,OAAO,EAAE,CAAC;oBACZ,CAAC;yBAAM,CAAC;wBACN,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;wBAClD,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;4BACpC,UAAU,EAAE,GAAG,CAAC,UAAU;4BAC1B,KAAK,EAAE,MAAM,CAAC,MAAM;4BACpB,KAAK,EAAE,KAAK,CAAC,OAAO;yBACrB,CAAC,CAAC;wBACH,MAAM,CAAC,KAAK,CAAC,CAAC;oBAChB,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACtB,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE;oBACrC,KAAK,EAAE,GAAG,CAAC,OAAO;oBAClB,KAAK,EAAE,MAAM,CAAC,MAAM;iBACrB,CAAC,CAAC;gBACH,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;gBACrB,GAAG,CAAC,OAAO,EAAE,CAAC;gBACd,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBAC3C,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE;oBACvC,KAAK,EAAE,MAAM,CAAC,MAAM;iBACrB,CAAC,CAAC;gBACH,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChB,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE;gBACvC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;gBAC/D,KAAK,EAAE,MAAM,CAAC,MAAM;aACrB,CAAC,CAAC;YACH,MAAM,CAAC,KAAK,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analytics type definitions for Weather MCP Server
|
|
3
|
+
* Implements privacy-first analytics as defined in docs/ANALYTICS_MCP_PLAN.md
|
|
4
|
+
*/
|
|
5
|
+
export type AnalyticsLevel = 'minimal' | 'standard' | 'detailed';
|
|
6
|
+
export type EventStatus = 'success' | 'error';
|
|
7
|
+
/**
|
|
8
|
+
* Base analytics event (minimal level)
|
|
9
|
+
*/
|
|
10
|
+
export interface BaseAnalyticsEvent {
|
|
11
|
+
version: string;
|
|
12
|
+
tool: string;
|
|
13
|
+
status: EventStatus;
|
|
14
|
+
timestamp_hour: string;
|
|
15
|
+
analytics_level: AnalyticsLevel;
|
|
16
|
+
error_type?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Standard level analytics event
|
|
20
|
+
* Includes performance and service metrics
|
|
21
|
+
*/
|
|
22
|
+
export interface StandardAnalyticsEvent extends BaseAnalyticsEvent {
|
|
23
|
+
analytics_level: 'standard' | 'detailed';
|
|
24
|
+
response_time_ms?: number;
|
|
25
|
+
service?: 'noaa' | 'openmeteo' | 'nifc' | 'usgs' | 'blitzortung' | 'rainviewer';
|
|
26
|
+
cache_hit?: boolean;
|
|
27
|
+
retry_count?: number;
|
|
28
|
+
country?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Detailed level analytics event
|
|
32
|
+
* Includes anonymized workflow patterns
|
|
33
|
+
*/
|
|
34
|
+
export interface DetailedAnalyticsEvent extends StandardAnalyticsEvent {
|
|
35
|
+
analytics_level: 'detailed';
|
|
36
|
+
parameters?: Record<string, unknown>;
|
|
37
|
+
session_id?: string;
|
|
38
|
+
sequence_number?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Union type for all analytics events
|
|
42
|
+
*/
|
|
43
|
+
export type AnalyticsEvent = BaseAnalyticsEvent | StandardAnalyticsEvent | DetailedAnalyticsEvent;
|
|
44
|
+
/**
|
|
45
|
+
* Analytics configuration
|
|
46
|
+
*/
|
|
47
|
+
export interface AnalyticsConfig {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
level: AnalyticsLevel;
|
|
50
|
+
endpoint: string;
|
|
51
|
+
version: string;
|
|
52
|
+
salt?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Event batch for transport
|
|
56
|
+
*/
|
|
57
|
+
export interface EventBatch {
|
|
58
|
+
events: AnalyticsEvent[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Metadata collected during tool execution
|
|
62
|
+
*/
|
|
63
|
+
export interface ToolExecutionMetadata {
|
|
64
|
+
response_time_ms?: number;
|
|
65
|
+
service?: string;
|
|
66
|
+
cache_hit?: boolean;
|
|
67
|
+
retry_count?: number;
|
|
68
|
+
country?: string;
|
|
69
|
+
parameters?: Record<string, unknown>;
|
|
70
|
+
error_type?: string;
|
|
71
|
+
session_id?: string;
|
|
72
|
+
sequence_number?: number;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AACjE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,eAAe,EAAE,UAAU,GAAG,UAAU,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,YAAY,CAAC;IAChF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,sBAAsB;IACpE,eAAe,EAAE,UAAU,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,sBAAsB,GACtB,sBAAsB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API configuration for external weather services
|
|
3
|
+
*
|
|
4
|
+
* Most APIs used by this server are free and require no authentication.
|
|
5
|
+
* Optional API tokens can be configured for enhanced features.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* NCEI (National Centers for Environmental Information) API token
|
|
9
|
+
*
|
|
10
|
+
* OPTIONAL: Get a free token at https://www.ncdc.noaa.gov/cdo-web/token
|
|
11
|
+
*
|
|
12
|
+
* Benefits of providing a token:
|
|
13
|
+
* - Access to official NOAA climate normals for US locations
|
|
14
|
+
* - More accurate than computed normals from reanalysis data
|
|
15
|
+
*
|
|
16
|
+
* If not provided:
|
|
17
|
+
* - Climate normals will be computed from Open-Meteo historical data
|
|
18
|
+
* - Works globally (not just US)
|
|
19
|
+
* - No setup required
|
|
20
|
+
*
|
|
21
|
+
* Rate limits with token:
|
|
22
|
+
* - 5 requests per second
|
|
23
|
+
* - 10,000 requests per day
|
|
24
|
+
*/
|
|
25
|
+
export declare const NCEI_API_TOKEN: string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Check if NCEI API is available (token configured)
|
|
28
|
+
*/
|
|
29
|
+
export declare function isNCEIAvailable(): boolean;
|
|
30
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/config/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,oBAA6B,CAAC;AAEzD;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API configuration for external weather services
|
|
3
|
+
*
|
|
4
|
+
* Most APIs used by this server are free and require no authentication.
|
|
5
|
+
* Optional API tokens can be configured for enhanced features.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* NCEI (National Centers for Environmental Information) API token
|
|
9
|
+
*
|
|
10
|
+
* OPTIONAL: Get a free token at https://www.ncdc.noaa.gov/cdo-web/token
|
|
11
|
+
*
|
|
12
|
+
* Benefits of providing a token:
|
|
13
|
+
* - Access to official NOAA climate normals for US locations
|
|
14
|
+
* - More accurate than computed normals from reanalysis data
|
|
15
|
+
*
|
|
16
|
+
* If not provided:
|
|
17
|
+
* - Climate normals will be computed from Open-Meteo historical data
|
|
18
|
+
* - Works globally (not just US)
|
|
19
|
+
* - No setup required
|
|
20
|
+
*
|
|
21
|
+
* Rate limits with token:
|
|
22
|
+
* - 5 requests per second
|
|
23
|
+
* - 10,000 requests per day
|
|
24
|
+
*/
|
|
25
|
+
export const NCEI_API_TOKEN = process.env.NCEI_API_TOKEN;
|
|
26
|
+
/**
|
|
27
|
+
* Check if NCEI API is available (token configured)
|
|
28
|
+
*/
|
|
29
|
+
export function isNCEIAvailable() {
|
|
30
|
+
return !!NCEI_API_TOKEN && NCEI_API_TOKEN.trim().length > 0;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/config/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;AAEzD;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,CAAC,cAAc,IAAI,cAAc,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9D,CAAC"}
|