Rename package from noaa-tides to mcnoaa-tides
Distribution name, import package, entry point script, MCP config, and all internal references updated. Git tracks the directory rename so file history is preserved.
This commit is contained in:
parent
66ec2ba9e7
commit
9f6d7bb4ac
@ -1,8 +1,8 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"noaa-tides": {
|
||||
"mcnoaa-tides": {
|
||||
"command": "uv",
|
||||
"args": ["run", "--directory", "/home/rpm/claude/mat/noaa-tides", "noaa-tides"]
|
||||
"args": ["run", "--directory", "/home/rpm/claude/mat/noaa-tides", "mcnoaa-tides"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
README.md
20
README.md
@ -1,4 +1,4 @@
|
||||
# noaa-tides
|
||||
# mcnoaa-tides
|
||||
|
||||
MCP server for [NOAA CO-OPS Tides and Currents](https://tidesandcurrents.noaa.gov/). Exposes tide predictions, observed water levels, and meteorological data from ~301 U.S. coastal stations as tools, resources, and prompts via [FastMCP 3.0](https://gofastmcp.com/).
|
||||
|
||||
@ -8,16 +8,16 @@ Built for marine planning — fishing trips, boating, crabbing, safety checks.
|
||||
|
||||
```bash
|
||||
# Run directly (no install needed)
|
||||
uvx noaa-tides
|
||||
uvx mcnoaa-tides
|
||||
|
||||
# Or add to Claude Code
|
||||
claude mcp add noaa-tides -- uvx noaa-tides
|
||||
claude mcp add mcnoaa-tides -- uvx mcnoaa-tides
|
||||
|
||||
# With visualization support (charts)
|
||||
uv pip install noaa-tides[viz]
|
||||
uv pip install mcnoaa-tides[viz]
|
||||
|
||||
# Local development
|
||||
uv run noaa-tides
|
||||
uv run mcnoaa-tides
|
||||
```
|
||||
|
||||
## Tools
|
||||
@ -192,7 +192,7 @@ Parameters:
|
||||
- `include_observed` — overlay actual readings (default true)
|
||||
- `format` — `"png"` (inline image) or `"html"` (interactive file)
|
||||
|
||||
Requires `noaa-tides[viz]` — install with `uv pip install noaa-tides[viz]`.
|
||||
Requires `mcnoaa-tides[viz]` — install with `uv pip install mcnoaa-tides[viz]`.
|
||||
|
||||
### `visualize_conditions` — Multi-panel conditions dashboard
|
||||
|
||||
@ -208,7 +208,7 @@ Parameters:
|
||||
- `hours` — data window (default 24)
|
||||
- `format` — `"png"` (inline image) or `"html"` (interactive file)
|
||||
|
||||
Requires `noaa-tides[viz]`.
|
||||
Requires `mcnoaa-tides[viz]`.
|
||||
|
||||
## Resources
|
||||
|
||||
@ -253,7 +253,7 @@ marine_safety_check(station_id="9447130")
|
||||
## Development
|
||||
|
||||
```bash
|
||||
git clone <repo-url> && cd noaa-tides
|
||||
git clone <repo-url> && cd mcnoaa-tides
|
||||
uv sync --dev
|
||||
|
||||
# Run tests (mock client, no network)
|
||||
@ -263,12 +263,12 @@ uv run pytest tests/ -v
|
||||
uv run ruff check src/
|
||||
|
||||
# Start server locally
|
||||
uv run noaa-tides
|
||||
uv run mcnoaa-tides
|
||||
|
||||
# Headless test with Claude
|
||||
claude -p "Search for tide stations in Rhode Island" \
|
||||
--mcp-config .mcp.json \
|
||||
--allowedTools "mcp__noaa-tides__*"
|
||||
--allowedTools "mcp__mcnoaa-tides__*"
|
||||
```
|
||||
|
||||
## Data source
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
[project]
|
||||
name = "noaa-tides"
|
||||
name = "mcnoaa-tides"
|
||||
version = "2026.02.21"
|
||||
description = "FastMCP server for NOAA CO-OPS Tides and Currents API"
|
||||
authors = [{ name = "Ryan Malloy", email = "ryan@supported.systems" }]
|
||||
@ -12,14 +12,14 @@ license = "MIT"
|
||||
viz = ["matplotlib>=3.8", "plotly>=5.18"]
|
||||
|
||||
[project.scripts]
|
||||
noaa-tides = "noaa_tides.server:main"
|
||||
mcnoaa-tides = "mcnoaa_tides.server:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/noaa_tides"]
|
||||
packages = ["src/mcnoaa_tides"]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py312"
|
||||
@ -32,4 +32,4 @@ select = ["E", "F", "I", "W"]
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[dependency-groups]
|
||||
dev = ["pytest>=8", "pytest-asyncio>=0.24", "ruff>=0.9", "noaa-tides[viz]"]
|
||||
dev = ["pytest>=8", "pytest-asyncio>=0.24", "ruff>=0.9", "mcnoaa-tides[viz]"]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
|
||||
try:
|
||||
__version__ = version("noaa-tides")
|
||||
__version__ = version("mcnoaa-tides")
|
||||
except PackageNotFoundError:
|
||||
__version__ = "0.0.0-dev"
|
||||
@ -1,6 +1,6 @@
|
||||
"""Chart rendering for NOAA tide and conditions data.
|
||||
|
||||
Optional dependency — requires noaa-tides[viz] to be installed.
|
||||
Optional dependency — requires mcnoaa-tides[viz] to be installed.
|
||||
"""
|
||||
|
||||
# Marine color palette — shared across all chart renderers
|
||||
@ -21,7 +21,7 @@ def check_deps(format: str) -> None:
|
||||
except ImportError:
|
||||
raise ValueError(
|
||||
"PNG charts require matplotlib. Install with: "
|
||||
"uv pip install noaa-tides[viz]"
|
||||
"uv pip install mcnoaa-tides[viz]"
|
||||
)
|
||||
elif format == "html":
|
||||
try:
|
||||
@ -29,5 +29,5 @@ def check_deps(format: str) -> None:
|
||||
except ImportError:
|
||||
raise ValueError(
|
||||
"HTML charts require plotly. Install with: "
|
||||
"uv pip install noaa-tides[viz]"
|
||||
"uv pip install mcnoaa-tides[viz]"
|
||||
)
|
||||
@ -3,7 +3,7 @@
|
||||
import io
|
||||
from datetime import datetime
|
||||
|
||||
from noaa_tides.charts import BG_COLOR, CORAL, GRID_COLOR, OCEAN_BLUE, SAND, SLATE, TEAL
|
||||
from mcnoaa_tides.charts import BG_COLOR, CORAL, GRID_COLOR, OCEAN_BLUE, SAND, SLATE, TEAL
|
||||
|
||||
|
||||
def _parse_time_series(data: list[dict], value_key: str = "v") -> tuple[list, list]:
|
||||
@ -105,7 +105,7 @@ def render_conditions_png(snapshot: dict, station_name: str = "") -> bytes:
|
||||
if has_predictions:
|
||||
preds = snapshot["predictions"].get("predictions", [])
|
||||
if preds:
|
||||
from noaa_tides.charts.tides import _parse_predictions
|
||||
from mcnoaa_tides.charts.tides import _parse_predictions
|
||||
|
||||
p_times, p_values, markers = _parse_predictions(preds)
|
||||
ax.plot(
|
||||
@ -122,7 +122,7 @@ def render_conditions_png(snapshot: dict, station_name: str = "") -> bytes:
|
||||
if has_water_level:
|
||||
obs_data = snapshot["water_level"].get("data", [])
|
||||
if obs_data:
|
||||
from noaa_tides.charts.tides import _parse_observed
|
||||
from mcnoaa_tides.charts.tides import _parse_observed
|
||||
|
||||
o_times, o_values = _parse_observed(obs_data)
|
||||
if o_times:
|
||||
@ -255,7 +255,7 @@ def render_conditions_html(snapshot: dict, station_name: str = "") -> str:
|
||||
if has_predictions:
|
||||
preds = snapshot["predictions"].get("predictions", [])
|
||||
if preds:
|
||||
from noaa_tides.charts.tides import _parse_predictions
|
||||
from mcnoaa_tides.charts.tides import _parse_predictions
|
||||
|
||||
p_times, p_values, markers = _parse_predictions(preds)
|
||||
fig.add_trace(
|
||||
@ -290,7 +290,7 @@ def render_conditions_html(snapshot: dict, station_name: str = "") -> str:
|
||||
if has_water_level:
|
||||
obs_data = snapshot["water_level"].get("data", [])
|
||||
if obs_data:
|
||||
from noaa_tides.charts.tides import _parse_observed
|
||||
from mcnoaa_tides.charts.tides import _parse_observed
|
||||
|
||||
o_times, o_values = _parse_observed(obs_data)
|
||||
if o_times:
|
||||
@ -3,7 +3,7 @@
|
||||
import io
|
||||
from datetime import datetime
|
||||
|
||||
from noaa_tides.charts import BG_COLOR, CORAL, GRID_COLOR, OCEAN_BLUE, SAND, SLATE, TEAL
|
||||
from mcnoaa_tides.charts import BG_COLOR, CORAL, GRID_COLOR, OCEAN_BLUE, SAND, SLATE, TEAL
|
||||
|
||||
|
||||
def _parse_predictions(predictions: list[dict]) -> tuple[list[datetime], list[float], list[dict]]:
|
||||
@ -7,7 +7,7 @@ import time
|
||||
|
||||
import httpx
|
||||
|
||||
from noaa_tides.models import Station
|
||||
from mcnoaa_tides.models import Station
|
||||
|
||||
DATA_URL = "https://api.tidesandcurrents.noaa.gov/api/prod/datagetter"
|
||||
META_URL = "https://api.tidesandcurrents.noaa.gov/mdapi/prod/webapi"
|
||||
@ -139,7 +139,7 @@ class NOAAClient:
|
||||
"units": units,
|
||||
"time_zone": time_zone,
|
||||
"format": "json",
|
||||
"application": "noaa-tides-mcp",
|
||||
"application": "mcnoaa-tides-mcp",
|
||||
}
|
||||
if begin_date:
|
||||
params["begin_date"] = begin_date
|
||||
@ -4,7 +4,7 @@ import json
|
||||
|
||||
from fastmcp import Context, FastMCP
|
||||
|
||||
from noaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
|
||||
|
||||
def register(mcp: FastMCP) -> None:
|
||||
@ -5,9 +5,9 @@ from contextlib import asynccontextmanager
|
||||
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from noaa_tides import __version__, prompts, resources
|
||||
from noaa_tides.client import NOAAClient
|
||||
from noaa_tides.tools import charts, conditions, meteorological, stations, tides
|
||||
from mcnoaa_tides import __version__, prompts, resources
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.tools import charts, conditions, meteorological, stations, tides
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@ -34,7 +34,7 @@ async def lifespan(server: FastMCP):
|
||||
|
||||
|
||||
mcp = FastMCP(
|
||||
"noaa-tides",
|
||||
"mcnoaa-tides",
|
||||
instructions=(
|
||||
"NOAA Tides & Currents data server. "
|
||||
"Provides tide predictions, observed water levels, and meteorological data "
|
||||
@ -57,5 +57,5 @@ prompts.register(mcp)
|
||||
|
||||
|
||||
def main():
|
||||
print(f"noaa-tides v{__version__}", file=sys.stderr)
|
||||
print(f"mcnoaa-tides v{__version__}", file=sys.stderr)
|
||||
mcp.run()
|
||||
@ -8,8 +8,8 @@ from typing import Literal
|
||||
from fastmcp import Context, FastMCP
|
||||
from fastmcp.utilities.types import Image
|
||||
|
||||
from noaa_tides.charts import check_deps
|
||||
from noaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.charts import check_deps
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
|
||||
|
||||
def register(mcp: FastMCP) -> None:
|
||||
@ -30,7 +30,7 @@ def register(mcp: FastMCP) -> None:
|
||||
PNG format returns an inline image. HTML format saves an interactive
|
||||
chart to artifacts/charts/ and returns the file path.
|
||||
|
||||
Requires noaa-tides[viz] to be installed.
|
||||
Requires mcnoaa-tides[viz] to be installed.
|
||||
"""
|
||||
check_deps(format)
|
||||
noaa: NOAAClient = ctx.lifespan_context["noaa_client"]
|
||||
@ -81,12 +81,12 @@ def register(mcp: FastMCP) -> None:
|
||||
pass
|
||||
|
||||
if format == "png":
|
||||
from noaa_tides.charts.tides import render_tide_chart_png
|
||||
from mcnoaa_tides.charts.tides import render_tide_chart_png
|
||||
|
||||
png_bytes = render_tide_chart_png(predictions, observed, station_name)
|
||||
return Image(data=png_bytes, format="image/png")
|
||||
else:
|
||||
from noaa_tides.charts.tides import render_tide_chart_html
|
||||
from mcnoaa_tides.charts.tides import render_tide_chart_html
|
||||
|
||||
html = render_tide_chart_html(predictions, observed, station_name)
|
||||
path = _save_html(html, station_id, "tides")
|
||||
@ -112,7 +112,7 @@ def register(mcp: FastMCP) -> None:
|
||||
PNG format returns an inline image. HTML format saves an interactive
|
||||
chart to artifacts/charts/ and returns the file path.
|
||||
|
||||
Requires noaa-tides[viz] to be installed.
|
||||
Requires mcnoaa-tides[viz] to be installed.
|
||||
"""
|
||||
check_deps(format)
|
||||
noaa: NOAAClient = ctx.lifespan_context["noaa_client"]
|
||||
@ -159,12 +159,12 @@ def register(mcp: FastMCP) -> None:
|
||||
pass
|
||||
|
||||
if format == "png":
|
||||
from noaa_tides.charts.conditions import render_conditions_png
|
||||
from mcnoaa_tides.charts.conditions import render_conditions_png
|
||||
|
||||
png_bytes = render_conditions_png(snapshot, station_name)
|
||||
return Image(data=png_bytes, format="image/png")
|
||||
else:
|
||||
from noaa_tides.charts.conditions import render_conditions_html
|
||||
from mcnoaa_tides.charts.conditions import render_conditions_html
|
||||
|
||||
html = render_conditions_html(snapshot, station_name)
|
||||
path = _save_html(html, station_id, "conditions")
|
||||
@ -5,7 +5,7 @@ from datetime import datetime, timezone
|
||||
|
||||
from fastmcp import Context, FastMCP
|
||||
|
||||
from noaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
|
||||
|
||||
def register(mcp: FastMCP) -> None:
|
||||
@ -4,7 +4,7 @@ from typing import Literal
|
||||
|
||||
from fastmcp import Context, FastMCP
|
||||
|
||||
from noaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
|
||||
MetProduct = Literal[
|
||||
"air_temperature",
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
from fastmcp import Context, FastMCP
|
||||
|
||||
from noaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
|
||||
|
||||
def register(mcp: FastMCP) -> None:
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
from fastmcp import Context, FastMCP
|
||||
|
||||
from noaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
|
||||
|
||||
def register(mcp: FastMCP) -> None:
|
||||
@ -8,9 +8,9 @@ from fastmcp import Client, FastMCP
|
||||
from fastmcp.client.transports import StreamableHttpTransport
|
||||
from fastmcp.utilities.tests import run_server_async
|
||||
|
||||
from noaa_tides import prompts, resources
|
||||
from noaa_tides.client import NOAAClient
|
||||
from noaa_tides.tools import charts, conditions, meteorological, stations, tides
|
||||
from mcnoaa_tides import prompts, resources
|
||||
from mcnoaa_tides.client import NOAAClient
|
||||
from mcnoaa_tides.tools import charts, conditions, meteorological, stations, tides
|
||||
|
||||
# Realistic station fixtures
|
||||
MOCK_STATIONS_RAW = [
|
||||
@ -98,7 +98,7 @@ MOCK_METADATA = {
|
||||
|
||||
def _build_mock_client() -> NOAAClient:
|
||||
"""Build a NOAAClient with mocked HTTP but real search/nearest logic."""
|
||||
from noaa_tides.models import Station
|
||||
from mcnoaa_tides.models import Station
|
||||
|
||||
client = NOAAClient()
|
||||
client._stations = [Station(**s) for s in MOCK_STATIONS_RAW]
|
||||
@ -135,7 +135,7 @@ async def _test_lifespan(server: FastMCP):
|
||||
|
||||
|
||||
def _build_test_server() -> FastMCP:
|
||||
mcp = FastMCP("noaa-tides-test", lifespan=_test_lifespan)
|
||||
mcp = FastMCP("mcnoaa-tides-test", lifespan=_test_lifespan)
|
||||
stations.register(mcp)
|
||||
tides.register(mcp)
|
||||
meteorological.register(mcp)
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
import pytest
|
||||
from fastmcp import Client
|
||||
|
||||
from noaa_tides.charts.conditions import render_conditions_html, render_conditions_png
|
||||
from noaa_tides.charts.tides import (
|
||||
from mcnoaa_tides.charts.conditions import render_conditions_html, render_conditions_png
|
||||
from mcnoaa_tides.charts.tides import (
|
||||
_parse_observed,
|
||||
_parse_predictions,
|
||||
render_tide_chart_html,
|
||||
|
||||
76
uv.lock
generated
76
uv.lock
generated
@ -767,6 +767,44 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcnoaa-tides"
|
||||
version = "2026.2.21"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
viz = [
|
||||
{ name = "matplotlib" },
|
||||
{ name = "plotly" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "mcnoaa-tides", extra = ["viz"] },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "fastmcp", specifier = ">=3.0.1" },
|
||||
{ name = "matplotlib", marker = "extra == 'viz'", specifier = ">=3.8" },
|
||||
{ name = "plotly", marker = "extra == 'viz'", specifier = ">=5.18" },
|
||||
]
|
||||
provides-extras = ["viz"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "mcnoaa-tides", extras = ["viz"] },
|
||||
{ name = "pytest", specifier = ">=8" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.24" },
|
||||
{ name = "ruff", specifier = ">=0.9" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mcp"
|
||||
version = "1.26.0"
|
||||
@ -819,44 +857,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/03/cc/7cb74758e6df95e0c4e1253f203b6dd7f348bf2f29cf89e9210a2416d535/narwhals-2.16.0-py3-none-any.whl", hash = "sha256:846f1fd7093ac69d63526e50732033e86c30ea0026a44d9b23991010c7d1485d", size = 443951, upload-time = "2026-02-02T10:30:58.635Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "noaa-tides"
|
||||
version = "2026.2.21"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
viz = [
|
||||
{ name = "matplotlib" },
|
||||
{ name = "plotly" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "noaa-tides", extra = ["viz"] },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "fastmcp", specifier = ">=3.0.1" },
|
||||
{ name = "matplotlib", marker = "extra == 'viz'", specifier = ">=3.8" },
|
||||
{ name = "plotly", marker = "extra == 'viz'", specifier = ">=5.18" },
|
||||
]
|
||||
provides-extras = ["viz"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "noaa-tides", extras = ["viz"] },
|
||||
{ name = "pytest", specifier = ">=8" },
|
||||
{ name = "pytest-asyncio", specifier = ">=0.24" },
|
||||
{ name = "ruff", specifier = ">=0.9" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.4.2"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user