Ocean MCP
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Ocean MCPFind daily sea-surface temperature around Marseille for summer 2025 and calculate monthly averages."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Ocean MCP
An MCP server that lets an LLM work with Copernicus Marine ocean data through semantic, ocean-data-shaped tools — not a 1:1 wrapper around the Copernicus Marine Toolbox Python API.
"Find daily sea-surface temperature around Marseille for summer 2025 and calculate the monthly averages."
Why a semantic layer, not a thin wrapper
The Copernicus Marine Toolbox is a general-purpose data-access library: describe(), subset(), open_dataset(), read_dataframe(). Exposing those 1:1 as MCP tools would hand the LLM low-level plumbing (raw dataset IDs, service names, coordinate-selection strategies) and force it to reconstruct scientific judgment the tools should already encode — e.g. that a request needs the nearest grid cell, that a 2D dataset has no depth axis, or that a full catalogue crawl takes ~7 minutes and can't run inside a single tool call.
Instead, this server sits a domain layer between the LLM and the Toolbox:
MCP Layer (mcp_tools/) — thin: parse input, call one service, shape output
|
v
Ocean Service Layer (services/) — all business logic
|
+--> Dataset Discovery (services/catalogue.py)
+--> Metadata (services/metadata.py)
+--> Validation (services/validation.py)
+--> Data Access (services/extraction.py)
|
v
Copernicus Marine Toolbox (copernicus/client.py — the only module that imports it)mcp_tools/* never talks to copernicusmarine directly and contains no business logic — it validates input via Pydantic, calls one service function, and returns one schema. All science and correctness logic (which service to prefer, whether a variable exists, whether a date falls in range) lives in services/*, which is fully testable without network access via a fake CopernicusClient.
Related MCP server: Copernicus Earth Observation MCP Server
What was verified before building this (not assumed)
describe()without aproduct_id/dataset_idscope crawls the entire ~1,260-dataset catalogue and takes several minutes.contains=[...]does not speed this up — it still walks the whole catalogue and filters after. This is whysearch_ocean_datasetsnever callsdescribe()live: it searches a local JSON index built offline byscripts/refresh_catalogue_cache.py.Datasets often expose multiple zarr services with identical variables/coordinates but different chunking —
arco-geo-series(optimized for spatial slabs) vs.arco-time-series(optimized for point/time-series access).extract_point_timeseriesexplicitly prefersarco-time-seriesrather than trusting auto-selection, and reports which service it used.Time coordinate values are not uniformly typed across the catalogue: gridded/model datasets use epoch milliseconds, but in-situ observation datasets use ISO 8601 strings directly (
coordinate_unit == "ISO8601").time_utils.coerce_time_value_to_isohandles both.read_dataframe()returns time/latitude/longitude as aMultiIndex, not columns.services/extraction.pynormalizes this before use.copernicusmarinereadsCOPERNICUSMARINE_SERVICE_USERNAME/PASSWORDinto module-level constants at import time, not lazily per call. This means.envmust be loaded before anything importscopernicusmarine— see the ordering inserver.pyandtests/conftest.py.2D (surface-only) datasets have no depth coordinate at all, which is how
validate_data_requestdistinguishes "depth not applicable to this dataset" from "depth out of range". 3D (depth-resolved) datasets, conversely, silently return every depth level if none is requested —extract_point_timeseries/extract_area_statisticsnow require an explicitdepthwheneverhas_depthis true, rather than averaging across levels no one asked for.A depth coordinate's
minimum_value/maximum_valueare sometimes bothNoneeven though the axis has real bounds — some ocean model products report depth as a discrete list of levels (coordinate.values) instead of a continuous range.coordinate_utils.coordinate_min_maxfalls back tomin(values)/max(values).subset()/open_dataset()needcoordinates_selection_method="nearest"explicitly (matching whatread_dataframe()already used) — with the default"inside", a depth/bbox request that doesn't land exactly on a grid point can match nothing and silently return all-NaNdata instead of an error.xr.Dataset.resample(time=freq).map(fn)does not reduce the time dimension for you — each group passed tofnstill has its own multi-step "time" axis inside it.extract_area_statisticsinitially reduced onlylatitude/longitudeper group, so "monthly" aggregation silently produced one row per input day instead of one per month.services/area_statistics.pynow also reduces overtimeinside each resample group.ResponseSubset(fromsubset(), verified viadry_run=True) fields:file_path,output_directory,filename,file_size/data_transfer_size(MB),variables,coordinates_extent(a list ofGeographicalExtent/TimeExtentobjects withminimum/maximum/coordinate_id),status,message,file_status,file_names.open_dataset()'s returnedxr.Datasetuses plain dims{time, latitude, longitude}with noMultiIndexsurprise (unlikeread_dataframe()) — but at least one real product decodes itstimecoordinate to a validdatetime64dtype with semantically wrong values (clustered near the 1970 epoch). A dtype check alone doesn't catch this;extract_area_statisticsalso sanity-checks the decoded range against the requested date range before aggregating.
Tools
Tool | Purpose |
| Rank candidate datasets from the local catalogue cache against a free-text query, optional variables, region, and date range. |
| Normalized metadata for one dataset: variables (units, standard names), spatial/temporal coverage, depth availability, provider, and which service to prefer for point vs. area extraction. Prefers a live scoped lookup (seconds), falls back to the cache. |
| Recommends the single most appropriate dataset for a scientific request — re-verifies coverage against live metadata and prefers gap-filled L4 analysis products over raw L3/L3S swath products, rather than trusting a variable-name match alone. Reports concrete reasoning, limitations, and up to two alternatives with why they weren't picked. |
| Checks a proposed extraction before running it — dataset/variable existence (with closest-match suggestions), coordinate/bbox validity, date coverage, depth applicability — and returns |
| Validates, then extracts a time series at the nearest grid cell to a point. Reports requested vs. actual coordinates, the distance between them, units, and missing-value counts — nothing is silent. |
| Spatial mean/min/max/std/percentile over a bounding box, optionally aggregated over time (daily/monthly/yearly). Mean/std are weighted by |
| Semantic wrapper over |
| Pure computation (mean/min/max/std/percentile/anomaly/trend/correlation) over values already returned by a prior tool call — no re-fetching, no server-side result store. |
Known limitation: free-text relevance ranking
search_ocean_datasets/recommend_dataset score candidates with keyword/phrase matching over title, keywords, and description — no domain ontology. On a real query for "chlorophyll concentration," it recommended a dinoflagellate-biomass model product over the dataset literally named bgc-chl, because the two tied on score and the tie-break fell on catalogue order. The tie itself is defensible (both are real, related biogeochemistry variables), but the ranking is naive; a synonym/variable-alias table would be the natural next improvement.
Setup
Requires Python ≥3.11 and uv.
uv syncCredentials
Copy .env.example to .env and fill in your real Copernicus Marine credentials.
Credentials are read only via COPERNICUSMARINE_SERVICE_USERNAME/COPERNICUSMARINE_SERVICE_PASSWORD, by the Toolbox itself. This project never reads, logs, or returns their values — config.py only checks they're present, and errors surfaced to the LLM (errors.py → fastmcp.exceptions.ToolError) never include secret values or raw stack traces.
Build the catalogue cache
search_ocean_datasets reads a local index rather than crawling the catalogue live. Build it once (takes several minutes):
uv run python scripts/refresh_catalogue_cache.pyRe-run periodically to refresh (get_dataset_metadata's response reports catalogue_snapshot_date when it falls back to the cache, so staleness is always visible).
Run the server
uv run python -m ocean_mcp.serverClaude Code / MCP client configuration
Add to your MCP client config (e.g. .mcp.json):
{
"mcpServers": {
"ocean-mcp": {
"command": "uv",
"args": ["run", "python", "-m", "ocean_mcp.server"],
"cwd": "/absolute/path/to/mcp-toolbox",
"env": {
"COPERNICUSMARINE_SERVICE_USERNAME": "your-username",
"COPERNICUSMARINE_SERVICE_PASSWORD": "your-password"
}
}
}
}(Credentials can instead be left out of the config and picked up from .env/the environment where the server process runs — whichever keeps them furthest from source control in your setup.)
Example queries
"Find daily sea surface temperature datasets covering the Mediterranean."
"Get metadata for cmems_obs-sst_med_phy_my_l3s_P1D-m — what variables and depth range does it have?"
"Recommend a dataset for daily sea surface temperature around Marseille in summer 2025."
"Validate a request for thetao at 43.2965, 5.3698 between June and September 2025."
"Give me daily SST at 43.2965, 5.3698 for the first week of June 2025."
"Calculate the monthly average sea surface temperature over the Gulf of Lion for Q1 2025."
"Subset SST over the Gulf of Lion for June 1–3, 2025 to a NetCDF file."
"What's the trend in those monthly SST values?"
Development
uv run pytest tests/unit tests/integration # no network required
uv run ruff check .
uv run mypy srcLive tests hit the real Copernicus Marine service and require real credentials plus an explicit opt-in:
RUN_LIVE_COPERNICUS_TESTS=1 uv run pytest tests/live -m liveTest layout
tests/unit/— services and schemas against a fakeCopernicusClient(tests/fakes.py, built from live-verified model shapes), no network.tests/integration/— fullmcp_toolscall paths viafastmcp.Clientin-memory, checking schema shape and error translation (ToolError).tests/live/— realdescribe()/read_dataframe()/open_dataset()/subset()calls, skipped unless opted in.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceAccess oceanographic and environmental data from 63+ ERDDAP servers worldwide through natural language queries. Search datasets, retrieve metadata, and download scientific data for climate research, marine biology, and coastal management.18
- AlicenseBqualityDmaintenanceProvides tools to search, download, and manage satellite imagery from all Copernicus Sentinel missions via the Copernicus Data Space ecosystem. It enables advanced geospatial queries, temporal coverage analysis, and automated data management for Earth observation tasks.133LGPL 3.0

Rasdaman MCP Serverofficial
AlicenseAqualityAmaintenanceEnables natural language interaction with rasdaman multidimensional databases by translating tool calls into WCS/WCPS queries. It allows users to list coverages, retrieve metadata, and execute complex queries on datacubes through an LLM.67MIT- FlicenseCqualityDmaintenanceEnables LLMs to interact with ERDDAP search, metadata, and tabledap services for oceanographic data.51
Related MCP Connectors
Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.
Bounded tools for rendering, extraction, RAG, enrichment, local discovery and review analysis.
Interact with climate metrics via Riskthinking.AI's CDT Express API in supported AI chat experiences
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ishimwe5555/mcp-toolbox'
If you have feedback or need assistance with the MCP directory API, please join our Discord server