schism-mcp 0.1.0__tar.gz
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.
- schism_mcp-0.1.0/.gitignore +45 -0
- schism_mcp-0.1.0/LICENSE +21 -0
- schism_mcp-0.1.0/PKG-INFO +78 -0
- schism_mcp-0.1.0/README.md +65 -0
- schism_mcp-0.1.0/pyproject.toml +33 -0
- schism_mcp-0.1.0/server.json +22 -0
- schism_mcp-0.1.0/smithery.yaml +8 -0
- schism_mcp-0.1.0/src/schism_mcp/__init__.py +0 -0
- schism_mcp-0.1.0/src/schism_mcp/__main__.py +5 -0
- schism_mcp-0.1.0/src/schism_mcp/client.py +191 -0
- schism_mcp-0.1.0/src/schism_mcp/models.py +472 -0
- schism_mcp-0.1.0/src/schism_mcp/server.py +32 -0
- schism_mcp-0.1.0/src/schism_mcp/tools/__init__.py +0 -0
- schism_mcp-0.1.0/src/schism_mcp/tools/docs.py +105 -0
- schism_mcp-0.1.0/src/schism_mcp/tools/parsing.py +262 -0
- schism_mcp-0.1.0/src/schism_mcp/tools/reference.py +162 -0
- schism_mcp-0.1.0/src/schism_mcp/tools/validation.py +166 -0
- schism_mcp-0.1.0/src/schism_mcp/utils.py +502 -0
- schism_mcp-0.1.0/tests/__init__.py +0 -0
- schism_mcp-0.1.0/tests/conftest.py +13 -0
- schism_mcp-0.1.0/tests/fixtures/bctides_sample.txt +32 -0
- schism_mcp-0.1.0/tests/fixtures/hgrid_header.txt +7 -0
- schism_mcp-0.1.0/tests/fixtures/param_nml_errors.txt +23 -0
- schism_mcp-0.1.0/tests/fixtures/param_nml_minimal.txt +28 -0
- schism_mcp-0.1.0/tests/fixtures/vgrid_sample.txt +18 -0
- schism_mcp-0.1.0/tests/test_live.py +35 -0
- schism_mcp-0.1.0/tests/test_mcp_protocol.py +35 -0
- schism_mcp-0.1.0/tests/test_parsers.py +256 -0
- schism_mcp-0.1.0/tests/test_tools.py +213 -0
- schism_mcp-0.1.0/tests/test_validation.py +119 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.whl
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
env/
|
|
15
|
+
|
|
16
|
+
# Testing
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
.tox/
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
*.swp
|
|
26
|
+
*.swo
|
|
27
|
+
*~
|
|
28
|
+
|
|
29
|
+
# OS
|
|
30
|
+
.DS_Store
|
|
31
|
+
Thumbs.db
|
|
32
|
+
|
|
33
|
+
# Linting
|
|
34
|
+
.ruff_cache/
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
|
|
37
|
+
# Lock files (each server manages its own)
|
|
38
|
+
uv.lock
|
|
39
|
+
|
|
40
|
+
# Environment
|
|
41
|
+
.env
|
|
42
|
+
.env.local
|
|
43
|
+
|
|
44
|
+
# Registry tokens
|
|
45
|
+
.mcpregistry_*
|
schism_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Mansur Ali Jisan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: schism-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for SCHISM model setup debugging, parameter lookup, and configuration validation
|
|
5
|
+
Author-email: Mansur Ali Jisan <mansur.jisan@noaa.gov>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Requires-Dist: httpx>=0.27.0
|
|
10
|
+
Requires-Dist: mcp[cli]>=1.0.0
|
|
11
|
+
Requires-Dist: pydantic>=2.0.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# schism-mcp
|
|
15
|
+
|
|
16
|
+
<!-- mcp-name: io.github.mansurjisan/schism-mcp -->
|
|
17
|
+
|
|
18
|
+
MCP server for SCHISM model setup debugging, parameter lookup, and configuration validation.
|
|
19
|
+
|
|
20
|
+
Unlike the other ocean-mcp servers that query remote NOAA APIs, schism-mcp **parses local model input files** and provides **embedded domain knowledge** to help debug and understand SCHISM configurations.
|
|
21
|
+
|
|
22
|
+
## Tools (10)
|
|
23
|
+
|
|
24
|
+
### Parameter Reference
|
|
25
|
+
- **`schism_explain_parameter`** — Look up any param.nml parameter, tidal constituent, vertical grid type, or BC type
|
|
26
|
+
- **`schism_list_parameters`** — List all parameters grouped by section (CORE, OPT, SCHOUT)
|
|
27
|
+
|
|
28
|
+
### File Parsing
|
|
29
|
+
- **`schism_parse_param_nml`** — Parse FORTRAN namelist file into structured summary
|
|
30
|
+
- **`schism_parse_hgrid`** — Parse hgrid.gr3 header (node/element counts, bounding box, boundaries)
|
|
31
|
+
- **`schism_parse_vgrid`** — Parse vgrid.in (LSC2/SZ type, level count, layer distribution)
|
|
32
|
+
- **`schism_parse_bctides`** — Parse bctides.in (tidal constituents, boundary segments)
|
|
33
|
+
|
|
34
|
+
### Validation & Debugging
|
|
35
|
+
- **`schism_validate_config`** — Comprehensive validation with cross-file checks
|
|
36
|
+
- **`schism_diagnose_error`** — Match error text against known SCHISM failure patterns
|
|
37
|
+
|
|
38
|
+
### Documentation
|
|
39
|
+
- **`schism_fetch_docs`** — Fetch pages from the SCHISM documentation site
|
|
40
|
+
- **`schism_search_docs`** — Search SCHISM documentation
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Using uvx (recommended)
|
|
46
|
+
uvx schism-mcp
|
|
47
|
+
|
|
48
|
+
# Or install from source
|
|
49
|
+
cd servers/schism-mcp
|
|
50
|
+
uv sync
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Add to your MCP client config:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"schism": {
|
|
61
|
+
"command": "uvx",
|
|
62
|
+
"args": ["schism-mcp"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cd servers/schism-mcp
|
|
72
|
+
uv sync --group dev
|
|
73
|
+
uv run pytest tests/ --ignore=tests/test_live.py --ignore=tests/test_mcp_protocol.py -v
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# schism-mcp
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: io.github.mansurjisan/schism-mcp -->
|
|
4
|
+
|
|
5
|
+
MCP server for SCHISM model setup debugging, parameter lookup, and configuration validation.
|
|
6
|
+
|
|
7
|
+
Unlike the other ocean-mcp servers that query remote NOAA APIs, schism-mcp **parses local model input files** and provides **embedded domain knowledge** to help debug and understand SCHISM configurations.
|
|
8
|
+
|
|
9
|
+
## Tools (10)
|
|
10
|
+
|
|
11
|
+
### Parameter Reference
|
|
12
|
+
- **`schism_explain_parameter`** — Look up any param.nml parameter, tidal constituent, vertical grid type, or BC type
|
|
13
|
+
- **`schism_list_parameters`** — List all parameters grouped by section (CORE, OPT, SCHOUT)
|
|
14
|
+
|
|
15
|
+
### File Parsing
|
|
16
|
+
- **`schism_parse_param_nml`** — Parse FORTRAN namelist file into structured summary
|
|
17
|
+
- **`schism_parse_hgrid`** — Parse hgrid.gr3 header (node/element counts, bounding box, boundaries)
|
|
18
|
+
- **`schism_parse_vgrid`** — Parse vgrid.in (LSC2/SZ type, level count, layer distribution)
|
|
19
|
+
- **`schism_parse_bctides`** — Parse bctides.in (tidal constituents, boundary segments)
|
|
20
|
+
|
|
21
|
+
### Validation & Debugging
|
|
22
|
+
- **`schism_validate_config`** — Comprehensive validation with cross-file checks
|
|
23
|
+
- **`schism_diagnose_error`** — Match error text against known SCHISM failure patterns
|
|
24
|
+
|
|
25
|
+
### Documentation
|
|
26
|
+
- **`schism_fetch_docs`** — Fetch pages from the SCHISM documentation site
|
|
27
|
+
- **`schism_search_docs`** — Search SCHISM documentation
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Using uvx (recommended)
|
|
33
|
+
uvx schism-mcp
|
|
34
|
+
|
|
35
|
+
# Or install from source
|
|
36
|
+
cd servers/schism-mcp
|
|
37
|
+
uv sync
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
Add to your MCP client config:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"schism": {
|
|
48
|
+
"command": "uvx",
|
|
49
|
+
"args": ["schism-mcp"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Development
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd servers/schism-mcp
|
|
59
|
+
uv sync --group dev
|
|
60
|
+
uv run pytest tests/ --ignore=tests/test_live.py --ignore=tests/test_mcp_protocol.py -v
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
MIT
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "schism-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP server for SCHISM model setup debugging, parameter lookup, and configuration validation"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [
|
|
9
|
+
{name = "Mansur Ali Jisan", email = "mansur.jisan@noaa.gov"},
|
|
10
|
+
]
|
|
11
|
+
dependencies = [
|
|
12
|
+
"mcp[cli]>=1.0.0",
|
|
13
|
+
"httpx>=0.27.0",
|
|
14
|
+
"pydantic>=2.0.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
schism-mcp = "schism_mcp.server:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["hatchling"]
|
|
22
|
+
build-backend = "hatchling.build"
|
|
23
|
+
|
|
24
|
+
[tool.pytest.ini_options]
|
|
25
|
+
testpaths = ["tests"]
|
|
26
|
+
asyncio_mode = "auto"
|
|
27
|
+
|
|
28
|
+
[dependency-groups]
|
|
29
|
+
dev = [
|
|
30
|
+
"pytest>=8.0.0",
|
|
31
|
+
"pytest-asyncio>=0.24.0",
|
|
32
|
+
"respx>=0.22.0",
|
|
33
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.mansurjisan/schism-mcp",
|
|
4
|
+
"title": "SCHISM Model Setup MCP Server",
|
|
5
|
+
"description": "SCHISM model configuration debugging, parameter lookup, and validation",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"url": "https://github.com/mansurjisan/ocean-mcp",
|
|
9
|
+
"source": "github"
|
|
10
|
+
},
|
|
11
|
+
"packages": [
|
|
12
|
+
{
|
|
13
|
+
"registryType": "pypi",
|
|
14
|
+
"identifier": "schism-mcp",
|
|
15
|
+
"version": "0.1.0",
|
|
16
|
+
"runtimeHint": "uvx",
|
|
17
|
+
"transport": {
|
|
18
|
+
"type": "stdio"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Dual-purpose client: local file reader + SCHISM documentation fetcher."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
SCHISM_DOCS_BASE = "https://schism-dev.github.io/schism"
|
|
8
|
+
SCHISM_REPO_URL = "https://raw.githubusercontent.com/schism-dev/schism/master"
|
|
9
|
+
|
|
10
|
+
MAX_FILE_SIZE = 100 * 1024 * 1024 # 100 MB
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SchismClientError(Exception):
|
|
14
|
+
"""Custom exception for SCHISM client errors."""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SchismClient:
|
|
20
|
+
"""Async client for local file reading and SCHISM documentation access."""
|
|
21
|
+
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
self._client: httpx.AsyncClient | None = None
|
|
24
|
+
|
|
25
|
+
async def _get_client(self) -> httpx.AsyncClient:
|
|
26
|
+
if self._client is None or self._client.is_closed:
|
|
27
|
+
self._client = httpx.AsyncClient(timeout=30.0)
|
|
28
|
+
return self._client
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def read_file(file_path: str) -> str:
|
|
32
|
+
"""Read a local file with size limit."""
|
|
33
|
+
import os
|
|
34
|
+
|
|
35
|
+
size = os.path.getsize(file_path)
|
|
36
|
+
if size > MAX_FILE_SIZE:
|
|
37
|
+
raise SchismClientError(
|
|
38
|
+
f"File too large ({size / 1024 / 1024:.1f} MB). "
|
|
39
|
+
f"Max is {MAX_FILE_SIZE / 1024 / 1024:.0f} MB."
|
|
40
|
+
)
|
|
41
|
+
with open(file_path) as f:
|
|
42
|
+
return f.read()
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def read_file_header(file_path: str, max_lines: int = 100) -> str:
|
|
46
|
+
"""Read only the first N lines of a file."""
|
|
47
|
+
lines = []
|
|
48
|
+
with open(file_path) as f:
|
|
49
|
+
for i, line in enumerate(f):
|
|
50
|
+
if i >= max_lines:
|
|
51
|
+
break
|
|
52
|
+
lines.append(line)
|
|
53
|
+
return "".join(lines)
|
|
54
|
+
|
|
55
|
+
async def fetch_doc_page(self, path: str) -> str:
|
|
56
|
+
"""Fetch a documentation page from SCHISM docs site."""
|
|
57
|
+
client = await self._get_client()
|
|
58
|
+
url = f"{SCHISM_DOCS_BASE}/{path}"
|
|
59
|
+
response = await client.get(url)
|
|
60
|
+
response.raise_for_status()
|
|
61
|
+
html = response.text
|
|
62
|
+
return strip_html_to_text(html)
|
|
63
|
+
|
|
64
|
+
async def search_docs(self, query: str) -> list[dict]:
|
|
65
|
+
"""Search SCHISM documentation by fetching the search index.
|
|
66
|
+
|
|
67
|
+
Since SCHISM docs are static (GitHub Pages), we search known page titles.
|
|
68
|
+
"""
|
|
69
|
+
query_lower = query.lower()
|
|
70
|
+
known_pages = [
|
|
71
|
+
{
|
|
72
|
+
"title": "Getting Started",
|
|
73
|
+
"path": "getting-started/overview.html",
|
|
74
|
+
"description": "Overview and quick start guide",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"title": "Input Files",
|
|
78
|
+
"path": "input-output/input-files.html",
|
|
79
|
+
"description": "All SCHISM input files reference",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"title": "param.nml",
|
|
83
|
+
"path": "input-output/param.nml.html",
|
|
84
|
+
"description": "Main parameter namelist reference",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"title": "hgrid.gr3",
|
|
88
|
+
"path": "input-output/hgrid.html",
|
|
89
|
+
"description": "Horizontal grid format",
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"title": "vgrid.in",
|
|
93
|
+
"path": "input-output/vgrid.html",
|
|
94
|
+
"description": "Vertical grid format",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"title": "bctides.in",
|
|
98
|
+
"path": "input-output/bctides.html",
|
|
99
|
+
"description": "Tidal boundary condition file",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"title": "Output Files",
|
|
103
|
+
"path": "input-output/output-files.html",
|
|
104
|
+
"description": "Output file descriptions",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"title": "Troubleshooting",
|
|
108
|
+
"path": "getting-started/troubleshooting.html",
|
|
109
|
+
"description": "Common issues and solutions",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"title": "SCHOUT",
|
|
113
|
+
"path": "input-output/schout.html",
|
|
114
|
+
"description": "SCHISM output control",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"title": "WWM",
|
|
118
|
+
"path": "modules/wwm.html",
|
|
119
|
+
"description": "Wind Wave Model coupling",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"title": "SED",
|
|
123
|
+
"path": "modules/sed.html",
|
|
124
|
+
"description": "Sediment transport module",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"title": "ICM",
|
|
128
|
+
"path": "modules/icm.html",
|
|
129
|
+
"description": "Water quality module",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"title": "Vertical Grid",
|
|
133
|
+
"path": "mesh-generation/vertical-grid.html",
|
|
134
|
+
"description": "Vertical grid generation",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"title": "Horizontal Grid",
|
|
138
|
+
"path": "mesh-generation/horizontal-grid.html",
|
|
139
|
+
"description": "Mesh generation guide",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"title": "Pre-processing",
|
|
143
|
+
"path": "getting-started/pre-processing.html",
|
|
144
|
+
"description": "Pre-processing tools",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"title": "Hotstart",
|
|
148
|
+
"path": "input-output/hotstart.html",
|
|
149
|
+
"description": "Hot start and restart",
|
|
150
|
+
},
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
results = []
|
|
154
|
+
for page in known_pages:
|
|
155
|
+
score = 0
|
|
156
|
+
for word in query_lower.split():
|
|
157
|
+
if word in page["title"].lower():
|
|
158
|
+
score += 2
|
|
159
|
+
if word in page["description"].lower():
|
|
160
|
+
score += 1
|
|
161
|
+
if score > 0:
|
|
162
|
+
results.append(
|
|
163
|
+
{
|
|
164
|
+
"title": page["title"],
|
|
165
|
+
"url": f"{SCHISM_DOCS_BASE}/{page['path']}",
|
|
166
|
+
"description": page["description"],
|
|
167
|
+
"score": score,
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
results.sort(key=lambda x: x["score"], reverse=True)
|
|
172
|
+
return results
|
|
173
|
+
|
|
174
|
+
async def close(self) -> None:
|
|
175
|
+
if self._client and not self._client.is_closed:
|
|
176
|
+
await self._client.aclose()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def strip_html_to_text(html: str) -> str:
|
|
180
|
+
"""Strip HTML tags and decode entities to plain text."""
|
|
181
|
+
text = re.sub(r"<script[^>]*>.*?</script>", "", html, flags=re.DOTALL)
|
|
182
|
+
text = re.sub(r"<style[^>]*>.*?</style>", "", text, flags=re.DOTALL)
|
|
183
|
+
text = re.sub(r"<[^>]+>", " ", text)
|
|
184
|
+
text = text.replace("&", "&")
|
|
185
|
+
text = text.replace("<", "<")
|
|
186
|
+
text = text.replace(">", ">")
|
|
187
|
+
text = text.replace(""", '"')
|
|
188
|
+
text = text.replace("'", "'")
|
|
189
|
+
text = text.replace(" ", " ")
|
|
190
|
+
text = re.sub(r"\s+", " ", text).strip()
|
|
191
|
+
return text
|