Skip to content

feat(tools): expose the 14 operations ledger v8 deferred — history, tools, drilling, analytics (#195) - #200

Merged
karlwaldman merged 4 commits into
mainfrom
feat/mcp-deferred-ops-1110
Nov 10, 2026
Merged

karlwaldman merged 4 commits into
mainfrom
feat/mcp-deferred-ops-1110

Conversation

@karlwaldman

Copy link
Copy Markdown
Member

Summary

One PR for all 14 operations that ledger v8 (#193) deferred. It supersedes #196, #197, #198 and #199. Each operation was verified live on 2026-11-10 with the production smoke key (Scale) before its tool was built. Ledger 8 -> 9 is one bump with one change entry, and all 14 rows are flipped to exposed, leaving 47 deferred rows. There are four commits, one per family, and each is green on its own.

Commit Family Tools Operations
1 market history opa_get_continuous_futures_history (new), opa_get_futures_history + contract=gasoil, opa_get_natural_gas_storage (new), opa_get_macro_releases (new) continuous brent/wti history, ICE gasoil history, natural-gas storage, macro releases
2 tools opa_calculate_fuel_surcharge, opa_test_price_alert, opa_get_service_status fuel-surcharge calculate, POST alert test, /v1/status
3 drilling opa_list_well_permits, opa_preview_well_permits /v1/ei/well-permits, /preview
4 analytics opa_get_price_statistics, opa_get_price_trend, opa_get_price_correlation, opa_get_spread_statistics the four /v1/analytics operations

Gating, as stated in each description. The public-claims smoke bans plan names, so gates are written as entitlement gates:

  • Futures entitlement (403): continuous history, gasoil. The continuous tool also states any start-date cap the API applied.
  • Plan-gated (403): natural-gas storage, the four analytics tools. Analytics never renders the 403 sample_data.
  • Well Permits subscription (403): permit list.
  • Any key: macro releases, surcharge calculation, permit preview (including Free), alert test (metered).
  • Keyless: service status.

Behaviour notes:

  • Continuous bars are described as revisable public snapshots, not settlements (metadata.official_settlement=false live).
  • Macro releases report "nothing scheduled" only when meta.calendar.status is ok.
  • opa_get_service_status treats a 503 as health unknown and does not retry it.
  • opa_test_price_alert is read scope, automation category, and accepts numeric ids only (live id 134).
  • Analytics does not render the body's tier field: it reads "free" for Scale (oilpriceapi-api#10761, confirmed-live).
  • Natural-gas storage may be one week behind: oilpriceapi-api#10762 (unverified).

Counts: 40 -> 52 tools, read scope 36 -> 48, core profile + opa_get_service_status. Updated in toolRegistry.test, index.test, package-smoke, docker-smoke and the matrix smoke.

Verification (local, on main 124c1de, head of this branch)

  • npm test: 742 passed, 1 skipped.
  • npm run build; check:readme-tools: OK, 52 tools.
  • check:capability-ledger against the live OpenAPI: OK v9, 136 operations, 65 exposed, 71 not exposed.
  • smoke:public-claims, smoke:live-contract-matrix, smoke:package, smoke:docker: pass.
  • test:live with the smoke key and SMOKE_WRITE_CONTRACTS=1: 51 passed, 1 non-network, 0 failed, 0 cannot-check. This includes the alert dry run on the synthetic alert.
  • Not verified live: the 403 paths. Both test keys are on the same Scale account, so those are mocked against the documented bodies.

No npm or registry publish. Auto-merge cannot arm until #194 is fixed. Tracking: #195.

🤖 Generated with Claude Code

https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8

karlwaldman and others added 4 commits November 10, 2026 13:22
…rage, macro calendar (#195)

- opa_get_continuous_futures_history: GET /v1/futures/continuous/{brent,wti}/historical.
  States the bars are revisable public snapshots, not settlements, and
  any entitlement start-date cap the API applied.
- opa_get_futures_history gains contract=gasoil: GET /v1/futures/ice-gasoil/historical (USD per tonne).
- opa_get_natural_gas_storage: GET /v1/natural-gas/storage (plan-gated, 403;
  weeks bounded 1-52 by the tool).
- opa_get_macro_releases: GET /v1/macro/releases with the calendar coverage caveat.

Ledger 8 -> 9 (this family's five rows), catalog entries, live-smoke shapes,
README rows, tool counts 43/39. Verified live 2026-11-10 with the Scale smoke key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
…status (#195)

- opa_calculate_fuel_surcharge: GET /v1/fuel-surcharge/calculate (percent or
  per_mile, seven EIA regions, 1-52 weeks; checks per-method inputs first;
  states a stale index).
- opa_test_price_alert: POST /v1/alerts/{alert_id}/test. Read scope,
  automation category; dry run that sends nothing, described as metered.
  Covered in the live price-alert lifecycle on the synthetic alert.
- opa_get_service_status: GET /v1/status. Keyless, core category,
  entitlement none; a 503 is reported as health unknown, not retried.

Ledger v9 rows for this family (same v9 change entry), catalog entries,
README rows, tool counts 46/42.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
- opa_list_well_permits: GET /v1/ei/well-permits (states, permit-date range,
  page, per_page 1-100; Well Permits subscription, 403 otherwise).
- opa_preview_well_permits: GET /v1/ei/well-permits/preview (one state,
  1-90 days, at most 25 rows; any key including Free; renders as_of, data
  age, stale flag and per-state date coverage).

Ledger v9 rows for this family (same v9 change entry), catalog entries,
README rows, tool counts 48/44.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
…n, spread (#195)

- opa_get_price_statistics: GET /v1/analytics/statistics.
- opa_get_price_trend: GET /v1/analytics/trend (analysis, sma, ema, rsi, levels).
- opa_get_price_correlation: GET /v1/analytics/correlation (analysis,
  rolling, matrix of 2-10 codes).
- opa_get_spread_statistics: GET /v1/analytics/spread (list, current, historical).

Plan gate stated as an entitlement gate (HTTP 403); the 403 sample_data is
never rendered; the body's `tier` field is not rendered (reads "free" for
Gen-2 paid plans, oilpriceapi-api#10761). Ledger v9 rows for this family
(same v9 change entry; analytics family exposed), catalog entries, live-smoke
shape, README rows, tool counts 52/48.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3sTPV81755NVtvVwb9Cd8
@karlwaldman karlwaldman added the risk:elevated Elevated: auto-merges after an adversarial review APPROVE label Nov 10, 2026
@coderabbitai

coderabbitai Bot commented Nov 10, 2026

Copy link
Copy Markdown

Important

  • Trigger review

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

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b31a1804-11e1-4ca3-a4d3-6848e439390e
  • 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

@karlwaldman

Copy link
Copy Markdown
Member Author

Adversarial review: PR #200 at 3fb1bd9b7843d07a978b5d3120c32513e6d169f7

Verdict: APPROVE. I tried to refute each claim and could not. Everything was run in a fresh worktree at the head SHA.

Evidence

Check Result
npm ci && npm run build pass. Manifest lists 52 tools.
npm test 40 files, 742 passed, 1 skipped
npm run check:capability-ledger OK v9: 136 published ops, 65 exposed, 52 tools, 72 REST paths
npm run smoke:public-claims passed across 86 surfaces
npm run check:readme-tools all 52 tools documented
npm run test:live (smoke key, OILPRICEAPI_LIVE_REQUIRED=1 SMOKE_WRITE_CONTRACTS=1) PASSED 51, failed 0, cannot-check 0

Note that test:live without SMOKE_WRITE_CONTRACTS=1 fails by design. The runner refuses to skip stateful tools, and CI sets that flag in live-tests.yml. On this PR the CI "Live API smoke test" job was skipped, so the local run above is the live evidence.

1. Endpoints and inputs

  • Every new tool calls the path and method that https://api.oilpriceapi.com/openapi.json publishes. Query parameter names match the spec: from/to/interval, historical/weeks, days/importance/source, the full base_price/method/increment/percent_per_increment/rounding/mpg/region/weeks set, states/start_date/end_date/page/per_page, state/days, code/period/type/window, code1/code2/codes, spread/period/type and include=commodities.

  • The zod bounds match the spec bounds: per_page 1-100, days 1-90, weeks 1-52, analytics period 1-365, and the enums. Handlers reject an invalid calendar date, a reversed range, a future to and an unknown state before any request is sent. The alert id must match ^\d+$.

  • No new tool writes data. opa_test_price_alert sends POST /v1/alerts/:id/test. I read V1::PriceAlertsController#test on oilpriceapi-api origin/main, and it is a pure read:

    • it looks the alert up with current_user.price_alerts.find;
    • alert.evaluate is a comparison with no side effects, and in_cooldown? is a time check;
    • it never calls evaluate_and_record!, never calls a mailer and never fires a webhook.

    It cannot notify anyone, the caller included. The read-only annotation is accurate.

  • Gasoil: /v1/futures/ice-gasoil/historical is in the spec. live-contract-catalog covers Brent only, so I ran the real handler against production. contract=gasoil for 2026-10-01 to 10-10 returned 15 contract months and 105 rows in USD per tonne, with settlement values.

2. Descriptions

  • I found no invented numbers. Every limit quoted (400 rows, 25 permits, 90 days, 52 weeks, 730 days, 15-minute status staleness) matches the spec or the handler code.
  • "realtime" appears only as the name of the API's own status tier.
  • Plan gating is stated generically: "plan-gated: the API answers HTTP 403" for analytics and gas storage. Well Permits is described as "an add-on, or included in some plans".
  • I checked the specific gating claims against the backend:
    • Fuel surcharge calculate has no feature gate. The controller comment says so, and the route is absent from determine_required_feature.
    • Macro releases has no gate.
    • The permit preview runs before_action :ensure_well_permits_access, except: :preview, so it is open to free keys.

3. Existing tools

  • I split src/index.ts per registerTool on main and on head. All 40 tools are still present.
  • The only registration-body diff is the gasoil text in opa_get_futures_history: the description, the enum and the describe string. Every other difference is code that moved position. tsc compiles and no symbol was dropped.
  • Ledger diff: version 8 to 9. The only changes are status, tools and reason edits for the 14 operations. No operation was added or removed.

4. Errors

  • Live with a bogus key: analytics and the permit list return a clear HTTP 401 message, and the key is not echoed. opa_test_price_alert with an unknown id returns "No alert found with id ... on this account".
  • 403, 404 and 429 go through the shared describeUnavailable, describeRequestFailure and alertHttpError helpers. They classify a 429 by the API's signal and only suggest an upgrade for a durable quota.

Non-blocking findings

  1. The new unit tests cover 403 and 404 but not 401 or 429 for the new tools. Those paths depend on the shared helpers' existing tests.
  2. The live contract catalog does not exercise contract=gasoil. Adding one gasoil entry would keep that path covered after this PR.
  3. "The API applies no plan gate to this endpoint" (surcharge calculate, macro releases) is true today. It is a specific claim that will go stale silently if the API adds a gate. Wording like "available on every plan today" would date it.
  4. The count is 12 new tools plus the gasoil extension. That is 14 ledger operations, not 14 tools.

@karlwaldman
karlwaldman merged commit cba0efb into main Nov 10, 2026
9 checks passed
@karlwaldman
karlwaldman deleted the feat/mcp-deferred-ops-1110 branch November 10, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:elevated Elevated: auto-merges after an adversarial review APPROVE

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant