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.
@@ -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_*
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ startCommand:
2
+ type: stdio
3
+ configSchema:
4
+ type: object
5
+ required: []
6
+ properties: {}
7
+ commandFunction: |-
8
+ (config) => ({ command: 'uvx', args: ['schism-mcp'] })
File without changes
@@ -0,0 +1,5 @@
1
+ """Allow running with python -m schism_mcp."""
2
+
3
+ from .server import main
4
+
5
+ main()
@@ -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("&amp;", "&")
185
+ text = text.replace("&lt;", "<")
186
+ text = text.replace("&gt;", ">")
187
+ text = text.replace("&quot;", '"')
188
+ text = text.replace("&#039;", "'")
189
+ text = text.replace("&nbsp;", " ")
190
+ text = re.sub(r"\s+", " ", text).strip()
191
+ return text