meteoswiss-mcp 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 +199 -0
- package/dist/data/meteoswiss-content-data.js +356 -0
- package/dist/data/meteoswiss-content-data.js.map +1 -0
- package/dist/data/meteoswiss-search-data.js +215 -0
- package/dist/data/meteoswiss-search-data.js.map +1 -0
- package/dist/data/ogd-current-weather.js +95 -0
- package/dist/data/ogd-current-weather.js.map +1 -0
- package/dist/data/ogd-data-store.js +160 -0
- package/dist/data/ogd-data-store.js.map +1 -0
- package/dist/data/ogd-local-forecast.js +199 -0
- package/dist/data/ogd-local-forecast.js.map +1 -0
- package/dist/data/ogd-pollen-data.js +94 -0
- package/dist/data/ogd-pollen-data.js.map +1 -0
- package/dist/data/ogd-smn-stations.js +106 -0
- package/dist/data/ogd-smn-stations.js.map +1 -0
- package/dist/data/ogd-stac-client.js +95 -0
- package/dist/data/ogd-stac-client.js.map +1 -0
- package/dist/data/ogd-station-list.js +39 -0
- package/dist/data/ogd-station-list.js.map +1 -0
- package/dist/data/ogd-station-resolver.js +126 -0
- package/dist/data/ogd-station-resolver.js.map +1 -0
- package/dist/index.js +138 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/meteoswiss-fetch.js +25 -0
- package/dist/schemas/meteoswiss-fetch.js.map +1 -0
- package/dist/schemas/meteoswiss-search.js +46 -0
- package/dist/schemas/meteoswiss-search.js.map +1 -0
- package/dist/schemas/ogd-current-weather.js +19 -0
- package/dist/schemas/ogd-current-weather.js.map +1 -0
- package/dist/schemas/ogd-local-forecast.js +19 -0
- package/dist/schemas/ogd-local-forecast.js.map +1 -0
- package/dist/schemas/ogd-pollen-data.js +11 -0
- package/dist/schemas/ogd-pollen-data.js.map +1 -0
- package/dist/schemas/ogd-shared.js +78 -0
- package/dist/schemas/ogd-shared.js.map +1 -0
- package/dist/schemas/ogd-station-list.js +21 -0
- package/dist/schemas/ogd-station-list.js.map +1 -0
- package/dist/server.js +307 -0
- package/dist/server.js.map +1 -0
- package/dist/support/environment-validation.js +129 -0
- package/dist/support/environment-validation.js.map +1 -0
- package/dist/support/geocode.js +81 -0
- package/dist/support/geocode.js.map +1 -0
- package/dist/support/haversine.js +46 -0
- package/dist/support/haversine.js.map +1 -0
- package/dist/support/http-cache.js +170 -0
- package/dist/support/http-cache.js.map +1 -0
- package/dist/support/http-communication.js +215 -0
- package/dist/support/http-communication.js.map +1 -0
- package/dist/support/logging.js +81 -0
- package/dist/support/logging.js.map +1 -0
- package/dist/support/markdown-rendering.js +62 -0
- package/dist/support/markdown-rendering.js.map +1 -0
- package/dist/support/normalize.js +17 -0
- package/dist/support/normalize.js.map +1 -0
- package/dist/support/ogd-csv-parser.js +58 -0
- package/dist/support/ogd-csv-parser.js.map +1 -0
- package/dist/support/reverse-geocode.js +71 -0
- package/dist/support/reverse-geocode.js.map +1 -0
- package/dist/support/session-management.js +117 -0
- package/dist/support/session-management.js.map +1 -0
- package/dist/support/styles.js +5 -0
- package/dist/support/styles.js.map +1 -0
- package/dist/support/test-fixtures.js +12 -0
- package/dist/support/test-fixtures.js.map +1 -0
- package/dist/support/url-generation.js +32 -0
- package/dist/support/url-generation.js.map +1 -0
- package/dist/support/validation-errors.js +38 -0
- package/dist/support/validation-errors.js.map +1 -0
- package/dist/support/version.js +25 -0
- package/dist/support/version.js.map +1 -0
- package/dist/support/weather-icons.js +101 -0
- package/dist/support/weather-icons.js.map +1 -0
- package/dist/tools/meteoswiss-fetch.js +30 -0
- package/dist/tools/meteoswiss-fetch.js.map +1 -0
- package/dist/tools/meteoswiss-search.js +29 -0
- package/dist/tools/meteoswiss-search.js.map +1 -0
- package/dist/transports/streamable-http.js +282 -0
- package/dist/transports/streamable-http.js.map +1 -0
- package/dist/types/mcp-prompts.js +71 -0
- package/dist/types/mcp-prompts.js.map +1 -0
- package/package.json +101 -0
- package/src/views/homepage/overview.md +21 -0
- package/src/views/homepage/tools.md +63 -0
- package/src/views/homepage/usage.md +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# MeteoSwiss MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/meteoswiss-mcp)
|
|
4
|
+
[](../../LICENSE)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
|
|
7
|
+
MCP server for MeteoSwiss weather data — powered by [MeteoSwiss Open Data](https://opendatadocs.meteoswiss.ch/), the same data behind the MeteoSwiss app and website.
|
|
8
|
+
|
|
9
|
+
- **Multi-day forecasts** for ~6000 Swiss locations (postal codes, stations, place names)
|
|
10
|
+
- **Real-time measurements** from ~160 automatic weather stations, updated every 10 minutes
|
|
11
|
+
- **Station discovery** by name, canton, or GPS coordinates
|
|
12
|
+
- **Pollen monitoring** from ~15 stations across Switzerland
|
|
13
|
+
- **MeteoSwiss website** search and content retrieval
|
|
14
|
+
|
|
15
|
+
## Use the Hosted Service
|
|
16
|
+
|
|
17
|
+
No installation required — the server is hosted at `https://meteoswiss-mcp.ars.is`.
|
|
18
|
+
|
|
19
|
+
### Claude Code
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude mcp add meteoswiss https://meteoswiss-mcp.ars.is/mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Claude Desktop
|
|
26
|
+
|
|
27
|
+
Add to your configuration file (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"meteoswiss": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["mcp-remote", "https://meteoswiss-mcp.ars.is/mcp"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Claude.ai
|
|
41
|
+
|
|
42
|
+
Go to **Settings** → **Integrations** → **Add MCP Server** → paste `https://meteoswiss-mcp.ars.is/mcp`.
|
|
43
|
+
|
|
44
|
+
### Example Questions
|
|
45
|
+
|
|
46
|
+
- "What's the weather forecast for Zurich this week?"
|
|
47
|
+
- "Wie wird das Wetter in Bern morgen?"
|
|
48
|
+
- "Quelle est la météo à Genève?"
|
|
49
|
+
- "Che tempo fa a Lugano?"
|
|
50
|
+
- "How windy is it at Jungfraujoch right now?"
|
|
51
|
+
- "Show me pollen data for Zurich"
|
|
52
|
+
|
|
53
|
+
## Available Tools
|
|
54
|
+
|
|
55
|
+
| Tool | Description |
|
|
56
|
+
|------|-------------|
|
|
57
|
+
| `meteoswissLocalForecast` | Multi-day forecasts for any Swiss location by postal code, station, or place name |
|
|
58
|
+
| `meteoswissCurrentWeather` | Real-time measurements from automatic weather stations (temperature, wind, humidity, pressure) |
|
|
59
|
+
| `meteoswissStations` | Search and browse the MeteoSwiss station network by name, canton, or coordinates |
|
|
60
|
+
| `meteoswissPollenData` | Current pollen concentration data from monitoring stations |
|
|
61
|
+
| `search` | Search MeteoSwiss website content (DE, FR, IT, EN) |
|
|
62
|
+
| `fetch` | Fetch full content from MeteoSwiss pages in markdown, text, or HTML |
|
|
63
|
+
|
|
64
|
+
## Prompts
|
|
65
|
+
|
|
66
|
+
Pre-configured prompts for common weather queries:
|
|
67
|
+
|
|
68
|
+
| Prompt | Language | Description |
|
|
69
|
+
|--------|----------|-------------|
|
|
70
|
+
| `wetterNordschweiz` | German | Forecast and current weather for Northern Switzerland |
|
|
71
|
+
| `wetterSchweiz` | German | Weather for any Swiss location |
|
|
72
|
+
| `meteoSuisseRomande` | French | Forecast and current weather for Western Switzerland |
|
|
73
|
+
| `meteoTicino` | Italian | Forecast and current weather for Southern Switzerland |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Self-Hosting
|
|
78
|
+
|
|
79
|
+
### Docker
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
docker run -p 3000:3000 ghcr.io/eins78/meteoswiss-mcp:latest
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
With custom URL:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
docker run -p 3000:3000 -e PUBLIC_URL=https://your-domain.com ghcr.io/eins78/meteoswiss-mcp:latest
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Docker Compose
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
docker compose up
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### From Source
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git clone https://github.com/eins78/meteoswiss-llm-tools.git
|
|
101
|
+
cd meteoswiss-llm-tools/packages/meteoswiss-mcp
|
|
102
|
+
pnpm install
|
|
103
|
+
pnpm build
|
|
104
|
+
pnpm start
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The server will be available at `http://localhost:3000`. MCP endpoint: `http://localhost:3000/mcp`.
|
|
108
|
+
|
|
109
|
+
### Local MCP Configuration
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"mcpServers": {
|
|
114
|
+
"meteoswiss-local": {
|
|
115
|
+
"command": "npx",
|
|
116
|
+
"args": ["mcp-remote", "http://localhost:3000/mcp"]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Environment Variables
|
|
123
|
+
|
|
124
|
+
| Variable | Default | Description |
|
|
125
|
+
|----------|---------|-------------|
|
|
126
|
+
| `PORT` | `3000` | Server port |
|
|
127
|
+
| `PUBLIC_URL` | — | Full public URL for URL generation (e.g. `https://meteoswiss-mcp.ars.is`) |
|
|
128
|
+
| `USE_TEST_FIXTURES` | `false` | Use local test data instead of live API |
|
|
129
|
+
| `BIND_ADDRESS` | `0.0.0.0` | Interface to bind to |
|
|
130
|
+
| `MAX_SESSIONS` | `100` | Maximum concurrent sessions |
|
|
131
|
+
| `SESSION_TIMEOUT_MS` | `300000` | Session timeout in milliseconds |
|
|
132
|
+
| `DEBUG` | — | Debug namespaces (e.g. `mcp:*`) |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
### Prerequisites
|
|
139
|
+
|
|
140
|
+
- Node.js v22+ ([nvm](https://github.com/nvm-sh/nvm) recommended)
|
|
141
|
+
- [pnpm](https://pnpm.io/)
|
|
142
|
+
|
|
143
|
+
### Setup
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
nvm use
|
|
147
|
+
pnpm install
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Commands
|
|
151
|
+
|
|
152
|
+
| Command | Description |
|
|
153
|
+
|---------|-------------|
|
|
154
|
+
| `pnpm dev` | Start with hot reloading |
|
|
155
|
+
| `pnpm start` | Start the server |
|
|
156
|
+
| `pnpm build` | Build TypeScript |
|
|
157
|
+
| `pnpm test` | Run tests |
|
|
158
|
+
| `pnpm run lint` | Type checking + ESLint |
|
|
159
|
+
| `pnpm run fix` | Auto-fix lint errors |
|
|
160
|
+
| `pnpm run ci` | Full CI (lint + build + test) |
|
|
161
|
+
| `pnpm run dev:inspect` | Test with MCP Inspector |
|
|
162
|
+
|
|
163
|
+
### Project Structure
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
src/
|
|
167
|
+
index.ts # Entry point
|
|
168
|
+
server.ts # MCP server (factory pattern)
|
|
169
|
+
data/ # Data access and transformation
|
|
170
|
+
schemas/ # Zod schemas for validation
|
|
171
|
+
tools/ # MCP tool implementations
|
|
172
|
+
support/ # Logging, validation, HTTP, sessions
|
|
173
|
+
views/homepage/ # Server homepage content
|
|
174
|
+
test/
|
|
175
|
+
integration/ # Integration tests for all tools
|
|
176
|
+
__fixtures__/ # Test data
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Debugging
|
|
180
|
+
|
|
181
|
+
Enable debug output with the `DEBUG` environment variable:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
DEBUG=mcp:* pnpm dev # All debug output
|
|
185
|
+
DEBUG=mcp:tools pnpm dev # Tool execution only
|
|
186
|
+
DEBUG=mcp:transport pnpm dev # Transport layer only
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
See [docs/debugging-guide.md](docs/debugging-guide.md) for more.
|
|
190
|
+
|
|
191
|
+
## Contributing
|
|
192
|
+
|
|
193
|
+
Contributions are welcome! Run `pnpm run fix && pnpm run ci` before committing.
|
|
194
|
+
|
|
195
|
+
See [docs/releasing.md](docs/releasing.md) for the release process.
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
[CC0-1.0](../../LICENSE) — public domain
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { JSDOM } from 'jsdom';
|
|
6
|
+
import TurndownService from 'turndown';
|
|
7
|
+
import { gfm } from 'turndown-plugin-gfm';
|
|
8
|
+
import { fetchHtml, HttpRequestError } from '../support/http-communication.js';
|
|
9
|
+
import { debugData } from '../support/logging.js';
|
|
10
|
+
// Test fixtures location
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const TEST_FIXTURES_DEV_PATH = path.resolve(__dirname, '../../test/__fixtures__/content');
|
|
13
|
+
const TEST_FIXTURES_PROD_PATH = path.resolve(__dirname, '../test/__fixtures__/content');
|
|
14
|
+
const TEST_FIXTURES_ROOT = existsSync(TEST_FIXTURES_DEV_PATH)
|
|
15
|
+
? TEST_FIXTURES_DEV_PATH
|
|
16
|
+
: TEST_FIXTURES_PROD_PATH;
|
|
17
|
+
const USE_TEST_FIXTURES = process.env.USE_TEST_FIXTURES === 'true';
|
|
18
|
+
// Allowed MeteoSwiss domains
|
|
19
|
+
const ALLOWED_DOMAINS = [
|
|
20
|
+
'www.meteoschweiz.admin.ch',
|
|
21
|
+
'www.meteosuisse.admin.ch',
|
|
22
|
+
'www.meteosvizzera.admin.ch',
|
|
23
|
+
'www.meteoswiss.admin.ch',
|
|
24
|
+
'meteoschweiz.admin.ch',
|
|
25
|
+
'meteosuisse.admin.ch',
|
|
26
|
+
'meteosvizzera.admin.ch',
|
|
27
|
+
'meteoswiss.admin.ch',
|
|
28
|
+
];
|
|
29
|
+
// Initialize Turndown for HTML to Markdown conversion
|
|
30
|
+
const turndownService = new TurndownService({
|
|
31
|
+
headingStyle: 'atx',
|
|
32
|
+
bulletListMarker: '-',
|
|
33
|
+
codeBlockStyle: 'fenced',
|
|
34
|
+
});
|
|
35
|
+
// Add GFM plugin for better markdown support (tables, strikethrough, task lists)
|
|
36
|
+
turndownService.use(gfm);
|
|
37
|
+
/**
|
|
38
|
+
* Fetch MeteoSwiss content by ID
|
|
39
|
+
*
|
|
40
|
+
* @param params Fetch parameters
|
|
41
|
+
* @returns Content response
|
|
42
|
+
*/
|
|
43
|
+
export async function fetchMeteoSwissContent(params) {
|
|
44
|
+
const { id, format = 'markdown', includeMetadata = true } = params;
|
|
45
|
+
debugData('fetchMeteoSwissContent called with params: %o', {
|
|
46
|
+
id,
|
|
47
|
+
format,
|
|
48
|
+
includeMetadata,
|
|
49
|
+
});
|
|
50
|
+
if (USE_TEST_FIXTURES) {
|
|
51
|
+
debugData('Using test fixtures for content fetch');
|
|
52
|
+
return fetchFromTestFixtures(id, format, includeMetadata);
|
|
53
|
+
}
|
|
54
|
+
debugData('Using live API for content fetch');
|
|
55
|
+
return fetchFromWeb(id, format, includeMetadata);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Fetch content from the web
|
|
59
|
+
*/
|
|
60
|
+
async function fetchFromWeb(id, format, includeMetadata) {
|
|
61
|
+
// The ID should now be a full URL from the search tool
|
|
62
|
+
const url = id.startsWith('http')
|
|
63
|
+
? id
|
|
64
|
+
: `https://www.meteoswiss.admin.ch${id.startsWith('/') ? id : '/' + id}`; // Fallback for backward compatibility
|
|
65
|
+
debugData('Fetching content from URL: %s', url);
|
|
66
|
+
// Validate the URL is from an allowed MeteoSwiss domain
|
|
67
|
+
try {
|
|
68
|
+
const parsedUrl = new URL(url);
|
|
69
|
+
if (!ALLOWED_DOMAINS.includes(parsedUrl.hostname)) {
|
|
70
|
+
throw new Error(`Invalid domain: ${parsedUrl.hostname}. Only MeteoSwiss domains are allowed.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (error instanceof TypeError) {
|
|
75
|
+
throw new Error(`Invalid URL: ${url}`, { cause: error });
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
debugData('Making HTTP request to fetch content');
|
|
81
|
+
const html = await fetchHtml(url);
|
|
82
|
+
debugData('Content fetched successfully, size: %d bytes', html.length);
|
|
83
|
+
// Add timeout protection for HTML processing
|
|
84
|
+
const processingTimeout = new Promise((_, reject) => {
|
|
85
|
+
setTimeout(() => reject(new Error('HTML processing timeout after 10 seconds')), 10000);
|
|
86
|
+
});
|
|
87
|
+
const contentProcessing = processHtmlContent(html, id, url, format, includeMetadata);
|
|
88
|
+
return await Promise.race([contentProcessing, processingTimeout]);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
debugData('Content fetch error: %o', error);
|
|
92
|
+
if (error instanceof HttpRequestError && error.statusCode === 404) {
|
|
93
|
+
throw new Error(`Content not found: ${id}`, { cause: error });
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`Failed to fetch content: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Fetch content from test fixtures
|
|
100
|
+
*/
|
|
101
|
+
async function fetchFromTestFixtures(id, format, includeMetadata) {
|
|
102
|
+
debugData('Looking for test fixture for ID: %s', id);
|
|
103
|
+
// Extract language from URL if it's a full URL
|
|
104
|
+
let detectedLang = 'de';
|
|
105
|
+
let urlPath = id;
|
|
106
|
+
if (id.startsWith('http')) {
|
|
107
|
+
const url = new URL(id);
|
|
108
|
+
urlPath = url.pathname;
|
|
109
|
+
// Detect language from domain
|
|
110
|
+
if (url.hostname.includes('meteoschweiz')) {
|
|
111
|
+
detectedLang = 'de';
|
|
112
|
+
}
|
|
113
|
+
else if (url.hostname.includes('meteosuisse')) {
|
|
114
|
+
detectedLang = 'fr';
|
|
115
|
+
}
|
|
116
|
+
else if (url.hostname.includes('meteosvizzera')) {
|
|
117
|
+
detectedLang = 'it';
|
|
118
|
+
}
|
|
119
|
+
else if (url.hostname.includes('meteoswiss')) {
|
|
120
|
+
detectedLang = 'en';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Extract filename from path
|
|
124
|
+
const fileName = urlPath.split('/').pop() || 'index.html';
|
|
125
|
+
const baseName = fileName.replace(/\.[^.]+$/, '');
|
|
126
|
+
// Try to find the fixture file
|
|
127
|
+
const languages = [detectedLang, 'de', 'fr', 'it', 'en'];
|
|
128
|
+
for (const lang of languages) {
|
|
129
|
+
const fixtureFile = path.join(TEST_FIXTURES_ROOT, lang, `${baseName}.html`);
|
|
130
|
+
debugData('Checking for fixture file: %s', fixtureFile);
|
|
131
|
+
if (existsSync(fixtureFile)) {
|
|
132
|
+
debugData('Loading test fixture from: %s', fixtureFile);
|
|
133
|
+
const html = await fs.readFile(fixtureFile, 'utf-8');
|
|
134
|
+
const url = id.startsWith('http') ? id : `https://www.meteoswiss.admin.ch${id}`;
|
|
135
|
+
return processHtmlContent(html, id, url, format, includeMetadata);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
debugData('No test fixture found for ID: %s', id);
|
|
139
|
+
throw new Error(`Content not found: ${id}`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Process HTML content and convert to requested format
|
|
143
|
+
*/
|
|
144
|
+
function processHtmlContent(html, id, url, format, includeMetadata) {
|
|
145
|
+
debugData('Processing HTML content, format: %s, includeMetadata: %s, size: %d bytes', format, includeMetadata, html.length);
|
|
146
|
+
// Warn if HTML is very large
|
|
147
|
+
if (html.length > 500000) {
|
|
148
|
+
debugData('WARNING: Large HTML document (%d bytes), processing may be slow', html.length);
|
|
149
|
+
}
|
|
150
|
+
const dom = new JSDOM(html);
|
|
151
|
+
const document = dom.window.document;
|
|
152
|
+
// Extract main content
|
|
153
|
+
const mainContent = extractMainContent(document);
|
|
154
|
+
// Extract title - try multiple selectors
|
|
155
|
+
const title = document.querySelector('h1')?.textContent?.trim() ||
|
|
156
|
+
document.querySelector('mch-title[level="1"]')?.textContent?.trim() ||
|
|
157
|
+
document.querySelector('[heading]')?.getAttribute('heading') ||
|
|
158
|
+
document.querySelector('title')?.textContent?.trim() ||
|
|
159
|
+
'Untitled';
|
|
160
|
+
// Extract metadata
|
|
161
|
+
const metadata = includeMetadata
|
|
162
|
+
? {
|
|
163
|
+
url,
|
|
164
|
+
language: detectLanguage(document),
|
|
165
|
+
lastModified: extractLastModified(document),
|
|
166
|
+
contentType: extractContentType(document),
|
|
167
|
+
keywords: extractKeywords(document),
|
|
168
|
+
description: extractDescription(document),
|
|
169
|
+
}
|
|
170
|
+
: undefined;
|
|
171
|
+
// Convert content to requested format
|
|
172
|
+
let content;
|
|
173
|
+
switch (format) {
|
|
174
|
+
case 'markdown':
|
|
175
|
+
content = turndownService.turndown(mainContent);
|
|
176
|
+
// Prepend title as H1 if we have one and it's not already in the content
|
|
177
|
+
if (title && title !== 'Untitled' && !content.includes(`# ${title}`)) {
|
|
178
|
+
content = `# ${title}\n\n${content}`;
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
181
|
+
case 'text':
|
|
182
|
+
content = extractTextContent(mainContent);
|
|
183
|
+
// Prepend title for text format too
|
|
184
|
+
if (title && title !== 'Untitled') {
|
|
185
|
+
content = `${title}\n\n${content}`;
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
default:
|
|
189
|
+
throw new Error(`Invalid format: ${format}`);
|
|
190
|
+
}
|
|
191
|
+
debugData('Content processed successfully, content length: %d characters', content.length);
|
|
192
|
+
return {
|
|
193
|
+
id,
|
|
194
|
+
title,
|
|
195
|
+
content,
|
|
196
|
+
format,
|
|
197
|
+
metadata,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Extract main content from the page
|
|
202
|
+
*/
|
|
203
|
+
function extractMainContent(document) {
|
|
204
|
+
// Remove screenreader titles in all languages
|
|
205
|
+
const screenreaderTitles = [
|
|
206
|
+
'Inhaltsbereich', // German
|
|
207
|
+
'Contenu principal', // French
|
|
208
|
+
'Contenuto principale', // Italian
|
|
209
|
+
'Main content', // English
|
|
210
|
+
];
|
|
211
|
+
// Remove elements with screenreader-only titles
|
|
212
|
+
document.querySelectorAll('h1, h2, h3').forEach((heading) => {
|
|
213
|
+
const text = heading.textContent?.trim() || '';
|
|
214
|
+
if (screenreaderTitles.includes(text) ||
|
|
215
|
+
heading.classList.contains('a11y-description--hidden') ||
|
|
216
|
+
heading.classList.contains('sr-only') ||
|
|
217
|
+
heading.classList.contains('visually-hidden')) {
|
|
218
|
+
heading.remove();
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
// Remove share widgets and dialogs
|
|
222
|
+
const shareSelectors = [
|
|
223
|
+
'dialog.share-dialog', // Share dialog container
|
|
224
|
+
'.share-dialog', // Share dialog elements
|
|
225
|
+
'.share-dialog-button__share--dark', // Share button
|
|
226
|
+
'[data-share]',
|
|
227
|
+
'.share-widget',
|
|
228
|
+
'.social-share',
|
|
229
|
+
'mch-share-dialog',
|
|
230
|
+
'.mch-page-intro__controls', // Container that includes share button
|
|
231
|
+
];
|
|
232
|
+
shareSelectors.forEach((selector) => {
|
|
233
|
+
document.querySelectorAll(selector).forEach((el) => {
|
|
234
|
+
el.remove();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
// Also remove elements that contain share-related text but use generic classes
|
|
238
|
+
document.querySelectorAll('[class*="share"], [id*="share"]').forEach((el) => {
|
|
239
|
+
const text = el.textContent?.toLowerCase() || '';
|
|
240
|
+
if (text.includes('seite teilen') ||
|
|
241
|
+
text.includes('partager') ||
|
|
242
|
+
text.includes('condividi') ||
|
|
243
|
+
text.includes('share') ||
|
|
244
|
+
text.includes('copy link') ||
|
|
245
|
+
text.includes('link kopieren')) {
|
|
246
|
+
el.remove();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
// First, expand any shadow DOM content by looking for slot elements
|
|
250
|
+
// and moving their assigned content into the main DOM
|
|
251
|
+
// Note: JSDOM doesn't fully support shadow DOM, so this is a best-effort approach
|
|
252
|
+
document.querySelectorAll('slot[name="main"]').forEach((slot) => {
|
|
253
|
+
const parent = slot.parentElement;
|
|
254
|
+
if (parent) {
|
|
255
|
+
// In JSDOM, slots don't have assignedNodes, so we'll just look for child elements
|
|
256
|
+
// that might be slotted content
|
|
257
|
+
const children = Array.from(slot.children);
|
|
258
|
+
children.forEach((child) => {
|
|
259
|
+
parent.appendChild(child.cloneNode(true));
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
// Try different selectors for main content
|
|
264
|
+
const selectors = [
|
|
265
|
+
'main',
|
|
266
|
+
'[role="main"]',
|
|
267
|
+
'.main-content',
|
|
268
|
+
'.content',
|
|
269
|
+
'article',
|
|
270
|
+
'.mch-article',
|
|
271
|
+
'#content',
|
|
272
|
+
'.page-main__wrapper', // MeteoSwiss specific
|
|
273
|
+
'mch-detail-page', // MeteoSwiss component
|
|
274
|
+
];
|
|
275
|
+
for (const selector of selectors) {
|
|
276
|
+
const element = document.querySelector(selector);
|
|
277
|
+
if (element) {
|
|
278
|
+
// Also include any slot content that might be after the main content
|
|
279
|
+
const slotContent = element.querySelectorAll('slot[slot="main"], [slot="main"]');
|
|
280
|
+
let additionalContent = '';
|
|
281
|
+
slotContent.forEach((el) => {
|
|
282
|
+
additionalContent += el.innerHTML || el.textContent || '';
|
|
283
|
+
});
|
|
284
|
+
return element.innerHTML + additionalContent;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// Fallback to body content
|
|
288
|
+
const body = document.querySelector('body');
|
|
289
|
+
if (body) {
|
|
290
|
+
// Remove navigation, header, footer, scripts, styles
|
|
291
|
+
const toRemove = body.querySelectorAll('nav, header, footer, script, style, noscript');
|
|
292
|
+
toRemove.forEach((el) => el.remove());
|
|
293
|
+
return body.innerHTML;
|
|
294
|
+
}
|
|
295
|
+
return '';
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Extract text content from HTML
|
|
299
|
+
*/
|
|
300
|
+
function extractTextContent(html) {
|
|
301
|
+
const tempDom = new JSDOM(html);
|
|
302
|
+
const text = tempDom.window.document.body.textContent || '';
|
|
303
|
+
// Clean up whitespace
|
|
304
|
+
return text
|
|
305
|
+
.split('\n')
|
|
306
|
+
.map((line) => line.trim())
|
|
307
|
+
.filter((line) => line.length > 0)
|
|
308
|
+
.join('\n');
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Detect language from the document
|
|
312
|
+
*/
|
|
313
|
+
function detectLanguage(document) {
|
|
314
|
+
const lang = document.documentElement.getAttribute('lang') ||
|
|
315
|
+
document.querySelector('meta[property="og:locale"]')?.getAttribute('content') ||
|
|
316
|
+
'de';
|
|
317
|
+
return lang.substring(0, 2).toLowerCase();
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Extract last modified date
|
|
321
|
+
*/
|
|
322
|
+
function extractLastModified(document) {
|
|
323
|
+
const lastModified = document.querySelector('meta[property="article:modified_time"]')?.getAttribute('content') ||
|
|
324
|
+
document.querySelector('meta[name="DC.date.modified"]')?.getAttribute('content');
|
|
325
|
+
return lastModified || undefined;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Extract content type
|
|
329
|
+
*/
|
|
330
|
+
function extractContentType(document) {
|
|
331
|
+
const type = document.querySelector('meta[property="og:type"]')?.getAttribute('content') ||
|
|
332
|
+
document.querySelector('meta[name="DC.type"]')?.getAttribute('content') ||
|
|
333
|
+
'article';
|
|
334
|
+
return type;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Extract keywords
|
|
338
|
+
*/
|
|
339
|
+
function extractKeywords(document) {
|
|
340
|
+
const keywordsStr = document.querySelector('meta[name="keywords"]')?.getAttribute('content') ||
|
|
341
|
+
document.querySelector('meta[property="article:tag"]')?.getAttribute('content') ||
|
|
342
|
+
'';
|
|
343
|
+
return keywordsStr
|
|
344
|
+
.split(',')
|
|
345
|
+
.map((k) => k.trim())
|
|
346
|
+
.filter((k) => k.length > 0);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Extract description
|
|
350
|
+
*/
|
|
351
|
+
function extractDescription(document) {
|
|
352
|
+
const description = document.querySelector('meta[name="description"]')?.getAttribute('content') ||
|
|
353
|
+
document.querySelector('meta[property="og:description"]')?.getAttribute('content');
|
|
354
|
+
return description || undefined;
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=meteoswiss-content-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meteoswiss-content-data.js","sourceRoot":"","sources":["../../src/data/meteoswiss-content-data.ts"],"names":[],"mappings":"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"}
|