Repository navigation
fix(helius-mcp): scope results handles to the caller and label getAccountPlan as JWT - #209
Merged
Merged
Conversation
nathanliow
approved these changes
Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces two follow-ups from the #181 review, both on the hosted surface. Nothing hosted is deployed yet, so neither was exploitable. We had to fix both before the hosted work relies on these paths
1. Result handles are scoped per caller
dispatch.tsstored and looked up everyexpandResulthandle under one process-wide key fromgetRouterContext(). On a hosted server, caller B could expand caller A's handle. Dispatch would re-run A's stored recipe (A's addresses and filters) under B's credentials.RequestContext.sessionKeyexisted for this, and its doc comment said bridging it toRouterContextwas deferredThe change:
getRouterContext(extra?)now derives the result owner from the request:sessionKeysessionId: a hash of the two, so two connections on one API key don't read or evict each other's handles. The feat(helius-mcp): Add an Optional Per-Request Context #182 review flagged that they shared one bucket capped at 10extra: storing a handle, expanding one, and the new handle issued when expanding withcontinuation: "next". The compiler caught that third path oncebuildStoredResultrequired an owner.RESULT_NOT_FOUNDwithout calling the handlerrequest-context.tsnow describes the live behavior2.
getAccountPlanis labelledjwtIt inherited
authRequirement: 'none'from the account-group default, but it reads plan, credits and project from the dashboard session, the same path asgetAccountStatus. Without a session it can only report "plan unknown". The runtime was already safe (getJwt(ctx)returns nothing for a request context), but the catalog contradicted it, andgetHostedActions()is built from the catalogIt's now labelled
jwtexplicitly and keeps its scalar response settings. The hosted surface drops from 78 to 77 actions. The hosted-eligibility snapshot is updated deliberately, and a new test pins the labelI checked every
heliusAccountaction's label against what its handler reads.getStarted,getHeliusPlanInfoandcompareHeliusPlansalso read the session but are labellednone. All three are already excluded by hand and work without a session by reporting less, so I left them as isTests
tests/result-ownership.test.ts(8 tests) drives the real store-and-expand round trip throughdispatchRoutedToolandexpandStoredResult, with only the action handler mocked. It covers:Negative control: against
main'scontext.tsanddispatch.ts, the five isolation tests fail. The three that pass cover the stdio behavior this PR keepsVerification
pnpm buildandpnpm validatepass (77 hosted-eligible)compile-skills.ts --checkin sync