Repository navigation
feat: list_public_tokens_tool for pages built outside the chat - #304
Conversation
Lists the account's public (pk.) tokens with their URL restrictions and recommends one: a token restricted to site_url when there is one, otherwise the default with advice to use a URL-restricted token for a published site. Never returns secret or temporary tokens and can't create tokens. Server instructions point page building at it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| /** MAPBOX_PUBLIC_TOKEN when it's set, otherwise the error. */ | ||
| private envFallbackOr(message: string): CallToolResult { | ||
| const envToken = process.env.MAPBOX_PUBLIC_TOKEN; | ||
| if (envToken?.startsWith('pk.')) { | ||
| return this.result({ | ||
| tokens: [], | ||
| recommended: { | ||
| token: envToken, | ||
| reason: `The public token this server is configured with (MAPBOX_PUBLIC_TOKEN). ${message}` | ||
| }, | ||
| guidance: GUIDANCE | ||
| }); | ||
| } | ||
| return { content: [{ type: 'text', text: message }], isError: true }; | ||
| } |
There was a problem hiding this comment.
Is there any concern here on the fallback? For instance if I'm hosting this and my env has a MAPBOX_PUBLIC_TOKEN present in the environment - what happens? How do we ensure it is using the users token, not the servers?
There was a problem hiding this comment.
Good catch, that was a real leak. On a hosted server, a caller whose token couldn't list tokens would have gotten the operator's MAPBOX_PUBLIC_TOKEN. Fixed in 6564c75: the fallback now only kicks in when the request is running on the server's own MAPBOX_ACCESS_TOKEN (a local install). A hosted caller with their own bearer token just gets the error. I didn't match on the username since that claim in the token isn't verified. Added a test that a hosted caller never sees the env token.
| }; | ||
| } | ||
| } | ||
| const fallback = tokens.find((t) => t.default) ?? tokens[0]; |
There was a problem hiding this comment.
If no default is found, could this result in a token restricted to other urls? would a check to make sure there are not restricted urls help?
There was a problem hiding this comment.
Yep, it could have. Fixed in 6564c75: the fallback only picks from tokens with no URL restrictions, preferring the default, then any unrestricted one. If every public token is restricted to some other site, nothing gets recommended and the text says so. Tests cover both cases.
Only fall back to MAPBOX_PUBLIC_TOKEN when the call runs on the server's own MAPBOX_ACCESS_TOKEN, and never recommend a token restricted to other URLs. From review on #304. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part of #281 (option 1, widened slightly)
Why
An agent building a site or app with a Mapbox map (ChatGPT/Codex, Claude Code, artifacts) had to ask the user for a public token.
render_map_toolonly helps inside the chat. This gives the agent the token, and steers it toward a URL-restricted one for anything published.What
list_public_tokens_toollists the account's public (pk.) tokens: name, whether it's the default, URL restrictions and scopes. It also returns a recommended token and guidance.site_url(where the page will run) and it recommends a token restricted to that URL when the account has one. Otherwise it recommends the default token and says a published site should use a URL-restricted one, with the link to create it. Anyone who copies an unrestricted pk token can use it, and usage bills to the account.usage=pkand drops anything else that comes back, so secret and temporary tokens never reach the model. A test feeds it sk and tk entries to check.tokens:write. Hosted already removes DevKit'screate_token_toolon purpose, and people who want to mint restricted tokens can use DevKit.tokens:read, which the in-chat map already uses (resolveMapboxPublicToken) to put the default pk token into every map page.pk.access token it returns that token without calling the API; when listing fails andMAPBOX_PUBLIC_TOKENis set, it returns that. A 401/403 says the token needstokens:read.site_urlis best effort (scheme-insensitive prefix match,*.domainwildcards); Mapbox makes the final call when the page loads.This doesn't generate map code: the agent still writes the GL JS (options 3 and 4 in #281 can come back later if that turns out poorly).
Testing
usage=pk), listing and the recommended token with and withoutsite_url, the fallback reason when nothing matches, sk/tk entries never passed through, the object response form, the pk access token path (no API call), thetokens:readhint on 403, theMAPBOX_PUBLIC_TOKENfallback, an account with no public tokens, and 11urlMatchescases (includingexample.com.evil.orgnot matchingexample.com). Plus a server-instructions test.tokens:readnote.site_url: https://example.com: listed the account's 3 public tokens (no secret tokens), none URL-restricted, so it recommended the default public token and said to create a restricted one for a published site.🤖 Generated with Claude Code