Skip to content

fix: canonical marine-fuels path and stdio-only registry record (#146 items 3-4) - #171

Merged
karlwaldman merged 1 commit into
mainfrom
fix/146-remaining
Oct 15, 2026
Merged

karlwaldman merged 1 commit into
mainfrom
fix/146-remaining

Conversation

@karlwaldman

Copy link
Copy Markdown
Member

Closes the remaining items of #146. Items 1-2 are in #168; this PR touches none of its files except README.md (a different section, no textual overlap).

Item 4: deprecated marine-fuels alias

opa_get_marine_fuels (src/index.ts) and energy_marine_fuels (src/energyPlugin.ts) now call GET /v1/prices/marine-fuels/latest.

Evidence Result
oilpriceapi-api config/routes.rb (origin/main) both paths route to prices#marine_fuels_latest
config/openapi_route_policy.yml /v1/marine-fuels/latest is a deprecated compatibility alias
swagger/v1/swagger.yaml documents /v1/prices/marine-fuels/latest
Live, test key, 2026-10-15: {}, ?port=SGSIN, ?fuel_type=VLSFO both paths 200, diff of the jq-sorted bodies is empty (17,705 / 2,327 / 131 bytes)

The capability ledger drops the via alias and the /v1/marine-fuels compatibility prefix. npm run check:capability-ledger: OK, 37 exposed, 36 tools, 49 REST paths.

Item 3: server.json remotes

Not added, on purpose. The registry schema (2025-12-11, ServerDetail.remotes) describes a remote as the same server as the package. The live endpoint is a different server:

  • POST https://mcp.oilpriceapi.com/mcp initialize returns serverInfo {"name":"OilPriceAPI","version":"0.1.1"}. tools/list returns 9 energy_* tools, not the 36 opa_* tools in npm 3.4.2. Checked 2026-10-15.
  • The protected-resource metadata gives scope energy:read (OAuth), matching chore(deploy): prepare durable remote MCP 0.1.2 rollout #151's record of DO app oilpriceapi-openai-mcp.
  • Live registry readback for 3.4.2 has remotes: null. All 20 published versions have none.

The README gains a ## Transport section saying this server is stdio-only and the hosted endpoint is a separate energy_* server. directorySource.test.ts pins three facts: no remotes, stdio-only packages, and that README text.

Tests

  • Red first: the updated path assertions and the new tests failed on the old code (16 failures).
  • npm test: 547 passed, 1 failed. The failure is energyPlugin.test.ts > cancels an upstream that stalls after opening its response body (5 s timeout). It fails the same way on unmodified origin/main (2 of 5 local runs) and does not touch marine code. It is a pre-existing local flake, filed separately.
  • npm run build, npm run verify:release-metadata, npm run check:capability-ledger: pass. tsc --noEmit (pai-check): pass.

Nothing is released, published or deployed by this PR. energy_marine_fuels reaches the hosted endpoint only after a separate image deploy.

🤖 Generated with Claude Code

…items 3-4)

- opa_get_marine_fuels and energy_marine_fuels now call the documented
  GET /v1/prices/marine-fuels/latest instead of the deprecated
  /v1/marine-fuels/latest alias. Both routes hit prices#marine_fuels_latest
  and returned byte-identical bodies live on 2026-10-15.
- Capability ledger drops the `via` alias and the compatibility prefix.
- server.json stays without `remotes`: mcp.oilpriceapi.com/mcp serves a
  different 9-tool energy_* server (v0.1.1). README now records that this
  server is stdio-only, and a test pins both facts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d91f604b-cdba-45e5-b8f8-f570115d4e68
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@karlwaldman

Copy link
Copy Markdown
Member Author

Adversarial review: APPROVE

Head c48c73940281f9fe7b0aff0dcc8f9857749bc27d. I tried to refute both items and could not.

CI on this SHA. All required checks pass: unit+build, Packed runtime Node 18/20/22/24, product-facts smoke, capability ledger. Live API smoke skips by design on PRs. GitHub reports MERGEABLE/CLEAN.

Merge with current main. The branch is one commit behind #168, which touches README.md and src/index.ts. I merged origin/main into a local worktree with no conflicts. On the merged tree, build, check:capability-ledger (36 tools, 49 REST paths), check:readme-tools (36 tools) and verify:release-metadata all pass. vitest: 550 passed, 1 failed. The failure is the known energyPlugin ... stalls flake (#172). It also fails on #173, which does not touch energyPlugin.

Item 4: the route switch.

  • On oilpriceapi-api origin/main, both /v1/marine-fuels/latest and /v1/prices/marine-fuels/latest route to prices#marine_fuels_latest (config/routes.rb:516 and :618).
  • The Rack::Attack marine throttle at config/initializers/rack_attack.rb:515 matches both paths, so the switch changes no rate limiting. No entitlement or admission rule keys on either path.
  • Live with the test key, 2026-10-15: both paths return 200 with jq-sorted bodies identical for {} (11,158 B), ?port=SGSIN (1,464 B) and ?fuel_type=VLSFO (85 B).
  • A read-only live smoke on the merged tree passed for opa_get_marine_fuels on /v1/prices/marine-fuels/latest: "HTTP 200 success-envelope; formatter fields present".

Item 3: stdio only, no remotes.

  • POST https://mcp.oilpriceapi.com/mcp initialize returns serverInfo OilPriceAPI 0.1.1.
  • tools/list returns 9 energy_* tools.
  • server.json has remotes: null and one npm/stdio package.
  • The README text is accurate.

Non-blocking notes

  • README "is not this server": the hosted server is build/remote.js (Dockerfile.remote), built from this repository. It is a different server and tool set, so the wording holds. A reader may still not expect the source to live here.
  • Backend user_quarterly_stats_service.rb:134 labels only the alias path "Marine Fuels". Calls on the canonical path will show as "Prices Marine Fuels Latest" in quarterly stats. This is cosmetic and is backend work.
  • Separate from this PR: in the same read-only live smoke, opa_get_futures_curve returned HTTP 522.

@karlwaldman karlwaldman added risk:reviewed Elevated, adversarial review approved this head SHA and removed risk:elevated Elevated: auto-merges after an adversarial review APPROVE labels Oct 15, 2026
@karlwaldman
karlwaldman merged commit 01cd664 into main Oct 15, 2026
9 checks passed
@karlwaldman
karlwaldman deleted the fix/146-remaining branch October 15, 2026 17:45
karlwaldman added a commit that referenced this pull request Oct 15, 2026
Publishes to npm and the MCP registry:
- opa_get_spread exposes pair, crack type/crude, margin index and daily
  history; type=basis no longer always fails (#169, #170)
- opa_get_history_range: date-range price history for up to 5 codes (#123, #177)
- canonical marine-fuels path and stdio-only registry record (#146, #171)
- honest 404 wording and README tool drift check (#146, #168)
- smoke cleanup of synthetic records (#126, #173)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
karlwaldman added a commit that referenced this pull request Oct 17, 2026
* chore(deps): bump actions/upload-artifact from 7.0.1 to 7.0.2

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@043fb46...cf430e0)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump actions/setup-node from 7.0.0 to 7.1.0 (#160)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@8207627...949feb2)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(ci): reconcile capability ledger drift and patch proxy-addr (#159) (#167)

* fix(ci): reconcile capability ledger drift and patch proxy-addr (#159)

- Ledger v3: record GET /v1/context/market/news and GET /v1/futures/oman
  as explicitly deferred; market-brief family is now partial.
- Refresh the route-policy snapshot to oilpriceapi-api 66f46ed0d (only
  change since 531e29899 is the retired congressional-trades reason,
  which no ledger family uses).
- npm audit fix: proxy-addr 2.0.7 -> 2.0.8 (GHSA-jqcg-44mw-7w3h).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

* fix(ci): add ledger row for GET /v1/meta/methodology (#159)

The live OpenAPI contract started publishing GET /v1/meta/methodology on
2026-10-14, so check:capability-ledger reported drift against main.
Record it as not_exposed/deferred in the commodities family.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

* fix(ledger): record methodology and api-key rename decisions in v3 changes (#159)

Adversarial review BLOCK on 929f194: the v3 changes entry omitted the
GET /v1/meta/methodology decision. The ledger contract requires every
decision change in changes. While re-checking, the live contract also
started publishing PATCH /v1/api-keys/{id} (rename key), which made
check:capability-ledger drift again; record it as unsupported like the
other api-keys operations. Add a test that every v3 decision is named in
the v3 summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* chore(deps): bump express from 5.2.1 to 5.3.0 (#163)

Bumps [express](https://github.com/expressjs/express) from 5.2.1 to 5.3.0.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](expressjs/express@v5.2.1...v5.3.0)

---
updated-dependencies:
- dependency-name: express
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @modelcontextprotocol/sdk from 1.31.0 to 1.32.1 (#164)

Bumps [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) from 1.31.0 to 1.32.1.
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](modelcontextprotocol/typescript-sdk@1.31.0...1.32.1)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.32.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump actions/download-artifact from 8.0.1 to 8.0.2 (#161)

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@3e5f45b...9000827)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: README tool drift check and honest 404 wording (#146) (#168)

- scripts/check-readme-tools.mjs fails CI when README tool tables and
  build/capabilities.json differ; wired into Unit tests + build.
- README documents the 4 missing tools (36/36).
- describeRequestFailure: a "No route matches" 404 is reported as a
  server request-path defect with the issues link; a routed 404 keeps
  the API's own message; an empty 404 no longer claims the dataset is
  unpopulated.


Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: canonical marine-fuels path and stdio-only registry record (#146 items 3-4) (#171)

- opa_get_marine_fuels and energy_marine_fuels now call the documented
  GET /v1/prices/marine-fuels/latest instead of the deprecated
  /v1/marine-fuels/latest alias. Both routes hit prices#marine_fuels_latest
  and returned byte-identical bodies live on 2026-10-15.
- Capability ledger drops the `via` alias and the compatibility prefix.
- server.json stays without `remotes`: mcp.oilpriceapi.com/mcp serves a
  different 9-tool energy_* server (v0.1.1). README now records that this
  server is stdio-only, and a test pins both facts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(smoke): clean up synthetic records by name and sweep past leaks (#126) (#173)

The live smoke deleted its synthetic alert/watch only `if (id)`. On
2026-09-04 the id read missed the response envelope, cleanup was skipped,
and watch b84b24a0 has run hourly on the shared test key since.

- Cleanup runs whenever the create was sent. Without an id it deletes by
  the run's unique `mcp-live-contract-<ms>` name, then verifies absence.
- Each lifecycle first sweeps `mcp-live-contract-<digits>` records older
  than one hour. Undated, customer-named or look-alike records are left alone.
- Logic lives in scripts/synthetic-cleanup.mjs with unit tests.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat: opa_get_history_range — date-range price history (#123) (#177)

* feat: opa_get_history_range for date-range price history (#123)

Adds opa_get_history_range over GET /v1/prices/historical: 1-5 commodities,
start_date/end_date (YYYY-MM-DD), hourly/daily/weekly/monthly, page and
per_page (max 500). Rows render per code, oldest first, at the API's own
precision, with currency/unit and the API's instrument disclosures.

- Pagination totals come from X-Total / X-Total-Pages (requestApi now
  returns success headers); a page that is not the last says so explicitly.
- A start date later than requested (plan history-depth clamp) is reported.
- A missing-code claim for a code that has rows on the page is dropped
  (api#8496, confirmed live 2026-10-15).
- extractErrorDetail reads the JSend fail envelope so 400s carry the
  API's message instead of a bare HTTP 400.
- Ledger v4: GET /v1/prices/historical exposed; GET /v1/prices recorded as
  alias. Live-contract catalog entry, README row, inventory counts 33/37.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: history-depth clamp, past-last-page and page order in opa_get_history_range (#123)

Addresses the adversarial review BLOCK on #177:
- D1: when the plan's history-depth clamp moves the applied start past the
  requested range and zero rows come back, say the plan window starts at the
  applied date and point to opa_get_plans, instead of calling it missing data.
- D2: a page beyond X-Total-Pages is reported as past the last page.
- D3: description, output and keyless teaser state pages run newest first.
- D4: ledger reason for GET /v1/prices alias no longer claims identical rows
  (default source, spot_price type scope and BYOS scoping differ).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): expose pair, crack type/crude, margin index and history (#169) (#170)

* fix(spread): expose pair, crack type/crude, margin index and history (#169)

opa_get_spread called /v1/spreads/${type} with no query string, so
type=basis always returned 400 MISSING_PARAMETER and crack/margin were
stuck on their backend defaults.

- pair (basis): WAHA_HH|BRENT_WTI|BRENT_DUBAI|JKM_HH|TTF_HH|BRENT_OMAN;
  basis without a pair now lists every pair via /v1/spreads/basis/all
- crack_type (3-2-1|jet|diesel|gasoline) and crude (4 codes) for crack
- index (usgc|singapore|nwe) for margin
- start_date/end_date route to /{type}/historical
- parameters that do not apply to the type, malformed/reversed dates and
  basis history without a pair are refused before any request
- capability ledger: basis/all and the three /historical ops now exposed

Fixes #169

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): classify local refusals as invalid_argument; fix teaser text (#169)

Reword the three spreadEndpoint refusals so telemetry classifyToolMiss
maps them to invalid_argument instead of unknown, and assert it in the
spread tests. The keyless teaser no longer says blending/transport
margins; it says composite refinery margins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(ledger): v5 records #169 spread exposure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* release: v3.5.0 (#179)

Publishes to npm and the MCP registry:
- opa_get_spread exposes pair, crack type/crude, margin index and daily
  history; type=basis no longer always fails (#169, #170)
- opa_get_history_range: date-range price history for up to 5 codes (#123, #177)
- canonical marine-fuels path and stdio-only registry record (#146, #171)
- honest 404 wording and README tool drift check (#146, #168)
- smoke cleanup of synthetic records (#126, #173)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: karlwaldman <karl.waldman@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
karlwaldman added a commit that referenced this pull request Oct 17, 2026
* chore(deps): bump node from `50c8e8c` to `ebfe2f9`

Bumps node from `50c8e8c` to `ebfe2f9`.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 24-alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): bump actions/setup-node from 7.0.0 to 7.1.0 (#160)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@8207627...949feb2)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(ci): reconcile capability ledger drift and patch proxy-addr (#159) (#167)

* fix(ci): reconcile capability ledger drift and patch proxy-addr (#159)

- Ledger v3: record GET /v1/context/market/news and GET /v1/futures/oman
  as explicitly deferred; market-brief family is now partial.
- Refresh the route-policy snapshot to oilpriceapi-api 66f46ed0d (only
  change since 531e29899 is the retired congressional-trades reason,
  which no ledger family uses).
- npm audit fix: proxy-addr 2.0.7 -> 2.0.8 (GHSA-jqcg-44mw-7w3h).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

* fix(ci): add ledger row for GET /v1/meta/methodology (#159)

The live OpenAPI contract started publishing GET /v1/meta/methodology on
2026-10-14, so check:capability-ledger reported drift against main.
Record it as not_exposed/deferred in the commodities family.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

* fix(ledger): record methodology and api-key rename decisions in v3 changes (#159)

Adversarial review BLOCK on 929f194: the v3 changes entry omitted the
GET /v1/meta/methodology decision. The ledger contract requires every
decision change in changes. While re-checking, the live contract also
started publishing PATCH /v1/api-keys/{id} (rename key), which made
check:capability-ledger drift again; record it as unsupported like the
other api-keys operations. Add a test that every v3 decision is named in
the v3 summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* chore(deps): bump express from 5.2.1 to 5.3.0 (#163)

Bumps [express](https://github.com/expressjs/express) from 5.2.1 to 5.3.0.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](expressjs/express@v5.2.1...v5.3.0)

---
updated-dependencies:
- dependency-name: express
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @modelcontextprotocol/sdk from 1.31.0 to 1.32.1 (#164)

Bumps [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) from 1.31.0 to 1.32.1.
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](modelcontextprotocol/typescript-sdk@1.31.0...1.32.1)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.32.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump actions/download-artifact from 8.0.1 to 8.0.2 (#161)

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@3e5f45b...9000827)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: README tool drift check and honest 404 wording (#146) (#168)

- scripts/check-readme-tools.mjs fails CI when README tool tables and
  build/capabilities.json differ; wired into Unit tests + build.
- README documents the 4 missing tools (36/36).
- describeRequestFailure: a "No route matches" 404 is reported as a
  server request-path defect with the issues link; a routed 404 keeps
  the API's own message; an empty 404 no longer claims the dataset is
  unpopulated.


Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: canonical marine-fuels path and stdio-only registry record (#146 items 3-4) (#171)

- opa_get_marine_fuels and energy_marine_fuels now call the documented
  GET /v1/prices/marine-fuels/latest instead of the deprecated
  /v1/marine-fuels/latest alias. Both routes hit prices#marine_fuels_latest
  and returned byte-identical bodies live on 2026-10-15.
- Capability ledger drops the `via` alias and the compatibility prefix.
- server.json stays without `remotes`: mcp.oilpriceapi.com/mcp serves a
  different 9-tool energy_* server (v0.1.1). README now records that this
  server is stdio-only, and a test pins both facts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(smoke): clean up synthetic records by name and sweep past leaks (#126) (#173)

The live smoke deleted its synthetic alert/watch only `if (id)`. On
2026-09-04 the id read missed the response envelope, cleanup was skipped,
and watch b84b24a0 has run hourly on the shared test key since.

- Cleanup runs whenever the create was sent. Without an id it deletes by
  the run's unique `mcp-live-contract-<ms>` name, then verifies absence.
- Each lifecycle first sweeps `mcp-live-contract-<digits>` records older
  than one hour. Undated, customer-named or look-alike records are left alone.
- Logic lives in scripts/synthetic-cleanup.mjs with unit tests.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat: opa_get_history_range — date-range price history (#123) (#177)

* feat: opa_get_history_range for date-range price history (#123)

Adds opa_get_history_range over GET /v1/prices/historical: 1-5 commodities,
start_date/end_date (YYYY-MM-DD), hourly/daily/weekly/monthly, page and
per_page (max 500). Rows render per code, oldest first, at the API's own
precision, with currency/unit and the API's instrument disclosures.

- Pagination totals come from X-Total / X-Total-Pages (requestApi now
  returns success headers); a page that is not the last says so explicitly.
- A start date later than requested (plan history-depth clamp) is reported.
- A missing-code claim for a code that has rows on the page is dropped
  (api#8496, confirmed live 2026-10-15).
- extractErrorDetail reads the JSend fail envelope so 400s carry the
  API's message instead of a bare HTTP 400.
- Ledger v4: GET /v1/prices/historical exposed; GET /v1/prices recorded as
  alias. Live-contract catalog entry, README row, inventory counts 33/37.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: history-depth clamp, past-last-page and page order in opa_get_history_range (#123)

Addresses the adversarial review BLOCK on #177:
- D1: when the plan's history-depth clamp moves the applied start past the
  requested range and zero rows come back, say the plan window starts at the
  applied date and point to opa_get_plans, instead of calling it missing data.
- D2: a page beyond X-Total-Pages is reported as past the last page.
- D3: description, output and keyless teaser state pages run newest first.
- D4: ledger reason for GET /v1/prices alias no longer claims identical rows
  (default source, spot_price type scope and BYOS scoping differ).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): expose pair, crack type/crude, margin index and history (#169) (#170)

* fix(spread): expose pair, crack type/crude, margin index and history (#169)

opa_get_spread called /v1/spreads/${type} with no query string, so
type=basis always returned 400 MISSING_PARAMETER and crack/margin were
stuck on their backend defaults.

- pair (basis): WAHA_HH|BRENT_WTI|BRENT_DUBAI|JKM_HH|TTF_HH|BRENT_OMAN;
  basis without a pair now lists every pair via /v1/spreads/basis/all
- crack_type (3-2-1|jet|diesel|gasoline) and crude (4 codes) for crack
- index (usgc|singapore|nwe) for margin
- start_date/end_date route to /{type}/historical
- parameters that do not apply to the type, malformed/reversed dates and
  basis history without a pair are refused before any request
- capability ledger: basis/all and the three /historical ops now exposed

Fixes #169

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): classify local refusals as invalid_argument; fix teaser text (#169)

Reword the three spreadEndpoint refusals so telemetry classifyToolMiss
maps them to invalid_argument instead of unknown, and assert it in the
spread tests. The keyless teaser no longer says blending/transport
margins; it says composite refinery margins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(ledger): v5 records #169 spread exposure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* release: v3.5.0 (#179)

Publishes to npm and the MCP registry:
- opa_get_spread exposes pair, crack type/crude, margin index and daily
  history; type=basis no longer always fails (#169, #170)
- opa_get_history_range: date-range price history for up to 5 codes (#123, #177)
- canonical marine-fuels path and stdio-only registry record (#146, #171)
- honest 404 wording and README tool drift check (#146, #168)
- smoke cleanup of synthetic records (#126, #173)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: karlwaldman <karl.waldman@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
karlwaldman added a commit that referenced this pull request Oct 17, 2026
* fix(demo): tag keyless /v1/demo requests with client=mcp

The API's demo telemetry attributes keyless traffic by the `client` query
parameter; MCP sent only X-Api-Client headers, so every MCP demo request was
logged as client=unknown (OilpriceAPI/oilpriceapi-api#9388).

Add withDemoClient() and apply it at all three real /v1/demo request sites:
fetchDemoPrices (stdio server), the energy plugin facade (remote), and the
doctor demo check. Existing query params and an explicit client are kept.
No iid is sent: the server persists no anonymous install id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGRpvj87b8YNgH7PC8ivEE

* chore(deps): bump actions/setup-node from 7.0.0 to 7.1.0 (#160)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 7.0.0 to 7.1.0.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](actions/setup-node@8207627...949feb2)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(ci): reconcile capability ledger drift and patch proxy-addr (#159) (#167)

* fix(ci): reconcile capability ledger drift and patch proxy-addr (#159)

- Ledger v3: record GET /v1/context/market/news and GET /v1/futures/oman
  as explicitly deferred; market-brief family is now partial.
- Refresh the route-policy snapshot to oilpriceapi-api 66f46ed0d (only
  change since 531e29899 is the retired congressional-trades reason,
  which no ledger family uses).
- npm audit fix: proxy-addr 2.0.7 -> 2.0.8 (GHSA-jqcg-44mw-7w3h).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

* fix(ci): add ledger row for GET /v1/meta/methodology (#159)

The live OpenAPI contract started publishing GET /v1/meta/methodology on
2026-10-14, so check:capability-ledger reported drift against main.
Record it as not_exposed/deferred in the commodities family.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

* fix(ledger): record methodology and api-key rename decisions in v3 changes (#159)

Adversarial review BLOCK on 929f194: the v3 changes entry omitted the
GET /v1/meta/methodology decision. The ledger contract requires every
decision change in changes. While re-checking, the live contract also
started publishing PATCH /v1/api-keys/{id} (rename key), which made
check:capability-ledger drift again; record it as unsupported like the
other api-keys operations. Add a test that every v3 decision is named in
the v3 summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* chore(deps): bump express from 5.2.1 to 5.3.0 (#163)

Bumps [express](https://github.com/expressjs/express) from 5.2.1 to 5.3.0.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](expressjs/express@v5.2.1...v5.3.0)

---
updated-dependencies:
- dependency-name: express
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump @modelcontextprotocol/sdk from 1.31.0 to 1.32.1 (#164)

Bumps [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) from 1.31.0 to 1.32.1.
- [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases)
- [Commits](modelcontextprotocol/typescript-sdk@1.31.0...1.32.1)

---
updated-dependencies:
- dependency-name: "@modelcontextprotocol/sdk"
  dependency-version: 1.32.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump actions/download-artifact from 8.0.1 to 8.0.2 (#161)

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](actions/download-artifact@3e5f45b...9000827)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: README tool drift check and honest 404 wording (#146) (#168)

- scripts/check-readme-tools.mjs fails CI when README tool tables and
  build/capabilities.json differ; wired into Unit tests + build.
- README documents the 4 missing tools (36/36).
- describeRequestFailure: a "No route matches" 404 is reported as a
  server request-path defect with the issues link; a routed 404 keeps
  the API's own message; an empty 404 no longer claims the dataset is
  unpopulated.


Claude-Session: https://claude.ai/code/session_01QQ7W3CkRKEnBpU5KRQUfUu

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix: canonical marine-fuels path and stdio-only registry record (#146 items 3-4) (#171)

- opa_get_marine_fuels and energy_marine_fuels now call the documented
  GET /v1/prices/marine-fuels/latest instead of the deprecated
  /v1/marine-fuels/latest alias. Both routes hit prices#marine_fuels_latest
  and returned byte-identical bodies live on 2026-10-15.
- Capability ledger drops the `via` alias and the compatibility prefix.
- server.json stays without `remotes`: mcp.oilpriceapi.com/mcp serves a
  different 9-tool energy_* server (v0.1.1). README now records that this
  server is stdio-only, and a test pins both facts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(smoke): clean up synthetic records by name and sweep past leaks (#126) (#173)

The live smoke deleted its synthetic alert/watch only `if (id)`. On
2026-09-04 the id read missed the response envelope, cleanup was skipped,
and watch b84b24a0 has run hourly on the shared test key since.

- Cleanup runs whenever the create was sent. Without an id it deletes by
  the run's unique `mcp-live-contract-<ms>` name, then verifies absence.
- Each lifecycle first sweeps `mcp-live-contract-<digits>` records older
  than one hour. Undated, customer-named or look-alike records are left alone.
- Logic lives in scripts/synthetic-cleanup.mjs with unit tests.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat: opa_get_history_range — date-range price history (#123) (#177)

* feat: opa_get_history_range for date-range price history (#123)

Adds opa_get_history_range over GET /v1/prices/historical: 1-5 commodities,
start_date/end_date (YYYY-MM-DD), hourly/daily/weekly/monthly, page and
per_page (max 500). Rows render per code, oldest first, at the API's own
precision, with currency/unit and the API's instrument disclosures.

- Pagination totals come from X-Total / X-Total-Pages (requestApi now
  returns success headers); a page that is not the last says so explicitly.
- A start date later than requested (plan history-depth clamp) is reported.
- A missing-code claim for a code that has rows on the page is dropped
  (api#8496, confirmed live 2026-10-15).
- extractErrorDetail reads the JSend fail envelope so 400s carry the
  API's message instead of a bare HTTP 400.
- Ledger v4: GET /v1/prices/historical exposed; GET /v1/prices recorded as
  alias. Live-contract catalog entry, README row, inventory counts 33/37.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: history-depth clamp, past-last-page and page order in opa_get_history_range (#123)

Addresses the adversarial review BLOCK on #177:
- D1: when the plan's history-depth clamp moves the applied start past the
  requested range and zero rows come back, say the plan window starts at the
  applied date and point to opa_get_plans, instead of calling it missing data.
- D2: a page beyond X-Total-Pages is reported as past the last page.
- D3: description, output and keyless teaser state pages run newest first.
- D4: ledger reason for GET /v1/prices alias no longer claims identical rows
  (default source, spot_price type scope and BYOS scoping differ).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): expose pair, crack type/crude, margin index and history (#169) (#170)

* fix(spread): expose pair, crack type/crude, margin index and history (#169)

opa_get_spread called /v1/spreads/${type} with no query string, so
type=basis always returned 400 MISSING_PARAMETER and crack/margin were
stuck on their backend defaults.

- pair (basis): WAHA_HH|BRENT_WTI|BRENT_DUBAI|JKM_HH|TTF_HH|BRENT_OMAN;
  basis without a pair now lists every pair via /v1/spreads/basis/all
- crack_type (3-2-1|jet|diesel|gasoline) and crude (4 codes) for crack
- index (usgc|singapore|nwe) for margin
- start_date/end_date route to /{type}/historical
- parameters that do not apply to the type, malformed/reversed dates and
  basis history without a pair are refused before any request
- capability ledger: basis/all and the three /historical ops now exposed

Fixes #169

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): classify local refusals as invalid_argument; fix teaser text (#169)

Reword the three spreadEndpoint refusals so telemetry classifyToolMiss
maps them to invalid_argument instead of unknown, and assert it in the
spread tests. The keyless teaser no longer says blending/transport
margins; it says composite refinery margins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(ledger): v5 records #169 spread exposure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* release: v3.5.0 (#179)

Publishes to npm and the MCP registry:
- opa_get_spread exposes pair, crack type/crude, margin index and daily
  history; type=basis no longer always fails (#169, #170)
- opa_get_history_range: date-range price history for up to 5 codes (#123, #177)
- canonical marine-fuels path and stdio-only registry record (#146, #171)
- honest 404 wording and README tool drift check (#146, #168)
- smoke cleanup of synthetic records (#126, #173)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
karlwaldman added a commit that referenced this pull request Oct 17, 2026
* feat: opa_get_futures_history for futures settlement history (#124)

Adds opa_get_futures_history over GET /v1/futures/{brent,wti,natural-gas}/historical:
instrument enum mapped to literal paths, from/to (YYYY-MM-DD, at most 730
days, no future dates), optional contract_month (YYYY-MM), daily/weekly/
monthly bars. All bounds are checked before any network call.

- One OHLC table per contract month at the API's own precision; settlement
  column only where the API reports one; daily close is labelled as the
  last captured price. Quote basis from the instrument specification.
- The endpoint has no pagination, so rendering is capped at 400 rows with
  an explicit partial-answer notice naming what was left out (#113).
- The HTTP 200 {error, available_dates} empty window is reported as a data
  fact with the API's available range.
- Ledger v4: the three canonical history rows are exposed (selection 2);
  spreads stay deferred with an updated reason. Live-contract catalog entry
  with a new futures-history shape, field-checked through its handler args.
  README row and inventory counts 33/37.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: opa_get_history_range — date-range price history (#123) (#177)

* feat: opa_get_history_range for date-range price history (#123)

Adds opa_get_history_range over GET /v1/prices/historical: 1-5 commodities,
start_date/end_date (YYYY-MM-DD), hourly/daily/weekly/monthly, page and
per_page (max 500). Rows render per code, oldest first, at the API's own
precision, with currency/unit and the API's instrument disclosures.

- Pagination totals come from X-Total / X-Total-Pages (requestApi now
  returns success headers); a page that is not the last says so explicitly.
- A start date later than requested (plan history-depth clamp) is reported.
- A missing-code claim for a code that has rows on the page is dropped
  (api#8496, confirmed live 2026-10-15).
- extractErrorDetail reads the JSend fail envelope so 400s carry the
  API's message instead of a bare HTTP 400.
- Ledger v4: GET /v1/prices/historical exposed; GET /v1/prices recorded as
  alias. Live-contract catalog entry, README row, inventory counts 33/37.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: history-depth clamp, past-last-page and page order in opa_get_history_range (#123)

Addresses the adversarial review BLOCK on #177:
- D1: when the plan's history-depth clamp moves the applied start past the
  requested range and zero rows come back, say the plan window starts at the
  applied date and point to opa_get_plans, instead of calling it missing data.
- D2: a page beyond X-Total-Pages is reported as past the last page.
- D3: description, output and keyless teaser state pages run newest first.
- D4: ledger reason for GET /v1/prices alias no longer claims identical rows
  (default source, spot_price type scope and BYOS scoping differ).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): expose pair, crack type/crude, margin index and history (#169) (#170)

* fix(spread): expose pair, crack type/crude, margin index and history (#169)

opa_get_spread called /v1/spreads/${type} with no query string, so
type=basis always returned 400 MISSING_PARAMETER and crack/margin were
stuck on their backend defaults.

- pair (basis): WAHA_HH|BRENT_WTI|BRENT_DUBAI|JKM_HH|TTF_HH|BRENT_OMAN;
  basis without a pair now lists every pair via /v1/spreads/basis/all
- crack_type (3-2-1|jet|diesel|gasoline) and crude (4 codes) for crack
- index (usgc|singapore|nwe) for margin
- start_date/end_date route to /{type}/historical
- parameters that do not apply to the type, malformed/reversed dates and
  basis history without a pair are refused before any request
- capability ledger: basis/all and the three /historical ops now exposed

Fixes #169

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(spread): classify local refusals as invalid_argument; fix teaser text (#169)

Reword the three spreadEndpoint refusals so telemetry classifyToolMiss
maps them to invalid_argument instead of unknown, and assert it in the
spread tests. The keyless teaser no longer says blending/transport
margins; it says composite refinery margins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* chore(ledger): v5 records #169 spread exposure

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* release: v3.5.0 (#179)

Publishes to npm and the MCP registry:
- opa_get_spread exposes pair, crack type/crude, margin index and daily
  history; type=basis no longer always fails (#169, #170)
- opa_get_history_range: date-range price history for up to 5 codes (#123, #177)
- canonical marine-fuels path and stdio-only registry record (#146, #171)
- honest 404 wording and README tool drift check (#146, #168)
- smoke cleanup of synthetic records (#126, #173)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* test: reconcile complete tool inventories after history additions

* test: align container inventory with both history tools

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:reviewed Elevated, adversarial review approved this head SHA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant