sodir-mcp
A read-only MCP server for the public Sodir (Norwegian Offshore Directorate) FactPages. It answers two questions with sourced, unit-labelled data:
| Tool | What it returns |
|---|---|
wellbore_facts |
Official facts of one wellbore: status, purpose, content, operator, field, spud (entered) and completion dates, total depth (MD), Sodir final vertical depth (TVD), kelly bushing elevation, water depth, drilling facility, surface location. |
field_production_monthly |
Sodir's monthly saleable production of a whole field (oil, gas, NGL, condensate, oil equivalents, produced water) with totals over a window. |
The package contains no Sodir data. You fetch the public tables once into a local cache, and the tools read that cache (read-only). Nothing is sent anywhere else.
Data licence: the Sodir data is published under the Norwegian Licence for Open Government Data (NLOD) 2.0. Every tool response carries the NLOD attribution; keep it if you pass the data on. See NOTICE-DATA.md. The code of this package is MIT-licensed.
Install and first run
uvx sodir-mcp refresh # downloads about 8 MB of CSV from factpages.sodir.no into the cache
uvx sodir-mcp status # what the cache holds, and when it was fetched
uvx sodir-mcp # serve over stdio (same as `sodir-mcp serve`)
(uvx runs the published package. Before it is published, install from a built wheel:
pip install dist/sodir_mcp-0.1.0-py3-none-any.whl.)
Claude Desktop (claude_desktop_config.json):
{"mcpServers": {"sodir": {"command": "uvx", "args": ["sodir-mcp"]}}}
Until refresh has run, each tool answers data_not_loaded with the hint run: sodir-mcp refresh.
Set SODIR_MCP_AUTO_REFRESH=1 to let the first tool call download the data (off by default, so
nothing downloads without being asked).
Commands
| Command | Does |
|---|---|
sodir-mcp refresh [--force] [--from-dir DIR] |
Downloads field, wellbore_development_all, wellbore_exploration_all and field_production_monthly, checks that each is CSV with the expected columns, normalises, and replaces the cache atomically. A failed refresh keeps the previous cache. At most once per 24 hours unless --force. --from-dir reads the four <table>.csv files from a folder you downloaded yourself (offline path; only this command reads files). |
sodir-mcp status |
Shows the cache path, the tables, fetch time and SHA-256. |
sodir-mcp serve [--http --host H --port P] |
MCP over stdio (default) or streamable HTTP. |
Environment: SODIR_MCP_CACHE (cache folder; default is the platform cache folder, for example
~/Library/Caches/sodir-mcp on macOS or ~/.cache/sodir-mcp on Linux), SODIR_MCP_AUTO_REFRESH=1,
SODIR_MCP_BACKEND=postgres (see below). The only host the package contacts is
factpages.sodir.no; redirects to other hosts are refused. A cache older than 90 days adds a note
to every answer.
Example
wellbore_facts(wellbore="F-12") (a Volve alias; any exact Sodir name such as 15/9-F-12 works,
for other fields only exact Sodir names). Trimmed output with values from the fixture used in the
tests (hand-made, not a Sodir extract):
{"wellbore": "15/9-F-12", "npd_wellbore_id": 5599, "category": "development",
"status": "P&A", "purpose": "PRODUCTION", "spud_date": "2007-06-14",
"completion_date": "2007-08-27", "total_depth_md_m": 3520.0,
"final_vertical_depth_tvd_m": 3107.0, "kb_elevation_m": 54.0,
"drilling_facility": {"name": "MÆRSK INSPIRER", "type": "JACK-UP 3 LEGS", "fixed_or_move": "FIXED"},
"data_as_of": {"table": "wellbore_development_all", "exported_at_utc": "…", "sha256_16": "…"},
"source": {"type": "sodir", "server": "sodir-mcp", "tool": "wellbore_facts",
"query_id": "sodir_wellbore_v1", "params": {"wellbore": "15/9-F-12"}, "rows": 1, "…": "…"}}
field_production_monthly(field="VOLVE", start="2014-01", end="2014-12") returns rows[{month, oil_msm3, gas_bsm3, ngl_msm3, condensate_msm3, oe_msm3, water_msm3}] as JSON numbers, units
(million Sm3, billion Sm3), totals summed in decimal, data_coverage and notes.
What the answers will and will not say
- Values are exactly as Sodir publishes them; nothing is converted, completed or corrected.
- The completion date is Sodir's end of drilling for a development wellbore and rig release for an
exploration wellbore drilled from a moveable facility; never the production start-up date. For a
sidetrack the entered date is the first drilling after kick-off, not a spud (
date_notes). final_vertical_depth_tvd_mnull means "not published", never zero.- The surface location is the wellhead, not the bottom hole.
- Facility names are quoted verbatim and may be cut at 16 characters by Sodir.
- Field volumes are whole-field saleable volumes; do not compare them with per-wellbore allocated volumes without stating both bases.
- Errors are structured:
{"error", "detail", "hint"}withinvalid_input,unknown_wellbore,unknown_field,out_of_coverage,data_not_loaded,backend_unavailable,timeout,internal_error. A name that is not an exact Sodir name or alias is never replaced by a similar one.
Safety
Read-only tools; the cache is opened with mode=ro. No tool takes a path, a URL, SQL or a free-form
query. Names are validated (letters, digits, space and / . _ # : -, 64 characters). Output is
capped at 500 rows and 20,000 characters. No credentials are involved.
Postgres backend (optional, for the Volve Well Ops Copilot stack)
pip install "sodir-mcp[postgres]" and SODIR_MCP_BACKEND=postgres read the stack's sodir_*
tables as the read-only role (POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB,
POSTGRES_READONLY_USER, POSTGRES_READONLY_PASSWORD). Same tools, same output.
Development
cd mcp_servers/sodir
uv run --group dev pytest # package tests (fixtures are hand-made CSVs)
uv build && uvx twine check dist/* # wheel and sdist; nothing is uploaded
From the repository root, make test and make ci-full run the package tests too (the root
pyproject.toml adds mcp_servers/sodir/tests to testpaths and mcp_servers/sodir/src to
pythonpath). The repository's tests/mcp_servers/test_sodir_package.py fails if the copied
helpers drift from mcp_servers/common.py or the output leaves contracts/mcp-tools.md.
Metadata
Release files for sodir-mcp 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sodir_mcp-0.1.0.tar.gz | 35.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sodir_mcp-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.2 kB
Release files / sodir_mcp-0.1.0.tar.gz
| Download URL | sodir_mcp-0.1.0.tar.gz |
|---|---|
| Size | 35.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a4d305a6d4904e77884db94a17647a78821acb285eac406747a68740e0b385a3
|
|
BLAKE2b-256 checksum How to use checksums |
8a5888c5eb2e8f460b4f3db30d6c1ca3941a6a4790a3bf8c442dbb377f7f91d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.15.0
|
Release files / sodir_mcp-0.1.0-py3-none-any.whl
| Download URL | sodir_mcp-0.1.0-py3-none-any.whl |
|---|---|
| Size | 33.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90ed1aa017cdfa210ea308a436dae989abe9c401fe1e7785bf75fd4cdce3485e
|
|
BLAKE2b-256 checksum How to use checksums |
b5ebd96b1bbe55b364ea923c62e820757a87a582836348b0a4f900cd70a443e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.15.0
|