Skip to content

Pyth template completion, MCP token-bloat fix, honest 404s for /v1 - #1

Open
92Infinitus92 wants to merge 10 commits into
developfrom
feat/pyth-price-feed-fields
Open

Pyth template completion, MCP token-bloat fix, honest 404s for /v1#1
92Infinitus92 wants to merge 10 commits into
developfrom
feat/pyth-price-feed-fields

Conversation

@92Infinitus92

@92Infinitus92 92Infinitus92 commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Four changes, one commit each:

  • Pyth template: 2 → 9 fields. conf, exponent, publish times, EMA values
    and posted_slot are now overridable, so scenarios can express staleness and
    confidence cases. Unit-tested against a real PriceUpdateV2 account.
  • MCP: 85x smaller template payloads. get_override_templates no longer
    inlines hundreds of constant options (1.86 MB → 22 KB; it overflowed LLM
    context and TPM limits). The new search_constant_options tool resolves
    values on demand. First tests in the mcp crate. The /v1/scenarios/templates
    HTTP endpoint (studio dropdowns) intentionally keeps full data.
  • Unknown /v1/ paths return JSON 404* instead of the SPA's index.html with
    HTTP 200, which masked client/API mismatches as JSON parse errors. Route
    registration is shared between server and test via configure_api.
  • surfnet_timeTravel docs: absoluteTimestamp is milliseconds and jumps are
    forward-only — neither was documented.

Testing: cli 19/19, mcp 5/5, core pyth 3/3; fmt and clippy clean; live
e2e against a running surfnet.

Open questions

  • Search page size is 20 (MAX_SEARCH_RESULTS): keep, change, or make it a
    tool parameter?
  • Add a real GET /v1/scenarios/{id}? It grows the public API and old embedded
    UIs must not depend on it.
  • Small cleanup PR for three pre-existing nits (legacy error convention in
    older MCP tools, dead Arc app_data in cli/http, HEAD 404s on GET routes)?

Greptile Summary

This PR bundles four focused changes: expanding the Pyth PriceUpdateV2 override template from 2 to 9 fields, shrinking MCP template payloads 85× by summarising constant options and adding an on-demand search_constant_options tool, returning proper JSON 404s for unknown /v1/* paths, and correcting surfnet_timeTravel docs (milliseconds, forward-only).

  • Pyth template gains conf, exponent, publish_time, prev_publish_time, ema_price, ema_conf, and posted_slot; a detailed LLM-context block guides staleness-scenario construction. All 8 fields are covered by a new round-trip test.
  • MCP payload reduction extracts compact_template_json (shared between the get_override_templates tool and the MCP resource handler), introduces search_constant_options for on-demand option lookup, and adds 5 unit tests. The HTTP /v1/scenarios/templates endpoint is intentionally left with full data.
  • JSON 404s are implemented by registering a web::scope("/v1").default_service(…) catch-all inside a shared configure_api helper; the test covers GET, POST, and a known-good path.

Confidence Score: 5/5

  • This PR is safe to merge. All four change areas are well-tested, the routing change is validated end-to-end, and the new MCP tool has no side-effects on existing behaviour.
  • Every changed code path has direct test coverage: the Pyth byte-layout is pinned by a round-trip test, the 404 routing is exercised for GET, POST, and a known-good path, the MCP payload reduction is covered by five unit tests including a schema-shape assertion, and the U16BeRef string-parsing is guarded by three property tests. The docs and YAML changes carry no runtime risk.
  • No files require special attention.

Important Files Changed

Filename Overview
crates/mcp/src/surfpool/mod.rs Adds compact_template_json helper (eliminates the two duplicate inline-mapping blocks), new search_constant_options tool with error handling for unknown template/constant, and 5 tests covering schema shape, case-insensitive search, truncation, and error paths. Logic is sound; metadata is included verbatim in results (flagged in a previous comment).
crates/cli/src/http/mod.rs Extracts configure_api so the route graph is shared between the live server and tests. Adds the /v1 scope default-service 404 handler and a test that validates GET, POST, and a known-good route (200). Routing order is correct: specific resources registered before the scope win in actix-web's trie matcher.
crates/core/src/scenarios/protocols/pyth/v2/overrides.yaml Adds 7 new overridable properties and a comprehensive LLM-context block documenting scaling conventions, forward-only time semantics, and a step-by-step staleness-scenario recipe. Documentation is accurate and well-structured.
crates/core/src/surfnet/locker.rs Extracts the Pyth fixture bytes into pyth_price_update_v2_fixture(), refactors the import block, and adds test_get_forged_account_data_overrides_all_pyth_price_feed_fields that round-trips all 8 template fields through the forge path and decodes the result. Tests are thorough and pin against the real byte layout.
crates/types/src/scenarios.rs Extends PdaSeed::U16BeRef to accept decimal strings (needed when MCP tools pass config indices as JSON strings). Three new tests cover the happy path, rejection of out-of-range/non-numeric strings, and rejection of non-integer JSON types.

Sequence Diagram

sequenceDiagram
    participant LLM as LLM Client
    participant MCP as MCP Server
    participant Registry as TemplateRegistry

    LLM->>MCP: get_override_templates()
    MCP->>Registry: registry.all()
    Registry-->>MCP: Vec of OverrideTemplate
    Note over MCP: compact_template_json - constants summarised as optionsCount, options array omitted
    MCP-->>LLM: templates with id, properties, constants summary, llmContext

    LLM->>MCP: search_constant_options(templateId, query)
    MCP->>Registry: find template by id
    Registry-->>MCP: OverrideTemplate
    Note over MCP: iterate constants sorted, case-insensitive match, cap at MAX_SEARCH_RESULTS=20
    MCP-->>LLM: results with value field, totalMatches, truncated

    LLM->>MCP: create_scenario(templateId, values with resolved feed_id)
    MCP->>MCP: POST surfnet_registerScenario
    MCP-->>LLM: success with studioUrl
Loading

Reviews (8): Last reviewed commit: "docs: make the cheatcode catalog match w..." | Re-trigger Greptile

The pyth-price-feed-v2 template exposed only feed_id and price, so scenarios
could not express staleness or confidence-band cases that lending/PMM logic
actually reads. Adds the remaining PriceFeedMessage fields (conf, exponent,
publish_time, prev_publish_time, ema_price, ema_conf) and posted_slot as
template properties, and extends llm_context with field semantics, a staleness
recipe, and the Surfnet clock rules (timeTravel takes milliseconds, the
simulated clock only moves forward).

Covered by a forging unit test against a real 134-byte PriceUpdateV2 account:
all eight writable fields change to values that differ from the fixture's,
bytes before price are asserted untouched, trailing padding is preserved.
absoluteTimestamp is consumed in milliseconds while the Clock sysvar exposes
seconds, and jumps are forward-only; the doc comment said neither, which cost
a debugging session to rediscover empirically.
get_override_templates inlined every constant option (hundreds of price feeds,
duplicated across templates), producing a ~1.86 MB payload that overflowed LLM
context windows and TPM limits. Constants are now summarized as
{label, description, optionsCount} — 85x smaller measured — and the new
search_constant_options tool resolves concrete values via case-insensitive
search with stable sorted paging (MAX_SEARCH_RESULTS) and self-correcting
errors for unknown template ids or constants. The str:///override_templates
resource shares the same serialization through compact_template_json, so both
MCP surfaces stay compact.

Adds the first tests of the mcp crate: tool error paths, case-insensitive
matching, truncation reporting, and a guarantee that no template leaks inlined
options.
…llback

The studio SPA catch-all answered any unknown GET — including API misses like
GET /v1/scenarios/{id}, which has never existed — with index.html and HTTP 200.
Clients then failed deep in JSON parsing ("Unexpected token '<'") instead of
seeing a clean 404, which is how a tag-wiping frontend bug shipped unnoticed.
Unknown /v1/* paths now get {"error":"not found"} with 404; page routes and
static assets keep the SPA fallback.

Route registration moved into configure_api, shared between the server and the
new HTTP test, so the load-bearing registration order (real endpoints before
the /v1 catch-all scope) is asserted on the exact code production runs.
@92Infinitus92 92Infinitus92 self-assigned this Aug 4, 2026
@failfmi failfmi closed this Aug 4, 2026
@failfmi failfmi reopened this Aug 4, 2026
@failfmi

failfmi commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@greptileai

Comment thread crates/cli/src/http/mod.rs
Review feedback: the guard's default_service is method-agnostic, but the test
only exercised GET. A POST to an unknown /v1 path now asserts the JSON 404 too.
@92Infinitus92
92Infinitus92 marked this pull request as ready for review August 5, 2026 06:46
Comment thread crates/mcp/src/surfpool/mod.rs
Comment thread crates/mcp/src/surfpool/mod.rs
Constant options carry their value as a string, so Raydium amm_config_index
never derived its PDA and the override was skipped with only a log line.
The search tool asked for template_id while its payload returns templateId. A
test covers both the schema a model reads and the deserializer behind it.
@failfmi
failfmi self-requested a review August 6, 2026 10:03
failfmi
failfmi previously approved these changes Aug 6, 2026
Six documented fields were snake_case while the wire is camelCase. fetch_before_use
was the harmful one: it is serde(default), so a client following the docs got HTTP
200 and an override stored with fetchBeforeUse false — no fresh fetch, and a skip
when the account was not already in the SVM.
@92Infinitus92

Copy link
Copy Markdown
Collaborator Author

Re-signed the branch — every commit is GPG-verified now. Nothing else changed: the tree hash is identical to the previous tip, git diff between old and new is empty, and the merge with develop kept its original second parent.

The hashes referenced in the threads above moved: b8aad6807642b0, 838cad6f2f0a31, 77db914e052847. The stacked branch was rebuilt on the new base, so c5350e54d648c1.

The registerScenario schema omitted the required tags and typed account as a
string, so a request following it failed deserialization. The clock cheatcodes
were missing from the catalog the MCP tool points models at, and the EpochInfo
examples showed snake_case. The Pyth staleness workflow now says how to read the
simulated clock, since pauseClock returns no timestamp.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants