Repository navigation
the app directory needs a privacy policy and terms, and both were 404 - #2759
Merged
Merged
Conversation
Listing the MCP server at /api/mcp/mcp in the ChatGPT app directory requires four reachable URLs in the submission form: website, support, privacy policy and terms of service. The first two exist. The other two answered 404 on production, so the submission could not be filled in at all. The privacy policy is read off the code rather than drafted from a template, because overstating what we collect is as wrong as understating it. Every claim traces to a line: autocapture is off and person profiles are disabled in posthog-provider.tsx and nothing ever calls identify; analytics are proxied through our own origin so the browser never contacts the analytics host; the server-side id in analytics-server.ts is a daily hash of the user agent with no IP stored; rate-limit buckets live in an in-memory Map that never reaches disk. If any of those change this page changes in the same commit. The terms carry two clauses that are not boilerplate. "Not financial advice, we never execute transactions, never send us a key" is what the three negative test cases the review requires will point at. The conflict-of-interest disclosure repeats what publisher.json and /partners already say, and all three must stay identical. The domain challenge route is the third required piece: verification fetches a token as plain text from /.well-known/openai-apps-challenge and the body must be the token alone. The token stays in an env var rather than the repo, because committing it would let anyone prove ownership of this domain for their own submission; until it is set the route 404s, which is the honest state. force-dynamic so rotating it needs no rebuild. Both pages are in the sitemap and linked from the footer: neither is reachable from the rail, and a reviewer follows those URLs.
…ogue
Three things, all found by calling the live endpoint rather than by reading
the code.
The values were wrong. Latency benches declaring unit "s" store milliseconds
by the fmtUnit convention, and this endpoint published the stored number
beside the declared unit: {"value":443.494,"unit":"s"} for a head lag the
site and /api/stat both render as 0.44 s. An agent citing value and unit
together was off by a thousand, under provider names, on three benches
including number 001. valueInDeclaredUnit already existed for exactly this
reason, its docstring names this same bench, and /api/stat already called it;
the MCP server never did. Every value crossing the boundary now goes through
it, and rankings carry both the stored `ms` and a `value` in the declared
unit so either read is right.
list_benchmarks returned the catalogue. All 229 live benches in one response,
142,555 characters, about 35,638 tokens, for a question as broad as "what do
you measure". A model handed that spends its context on JSON instead of on
the answer. It is now compact, capped and filterable: 25 rows of nine short
fields, about 2,400 tokens, 94% less, and it says how many it held back.
The tools were named for the REST surface they mirrored, not for the
questions people ask, and a client routes on the description text. Nobody
thinks "list benchmarks"; they ask which Solana RPC is fastest, or whether
Alchemy beats QuickNode. So: search_benchmarks takes their words and ranks
the catalogue against them, including by the provider names a bench measures,
so "is Helius faster than Triton" finds the Solana bench without anyone
knowing its slug. compare_providers takes the names they said and returns a
provider it does not measure as `missing` rather than as a loser, because
those are different answers. recommend_provider takes the goal they stated
and returns the caveat with it, since "read p99 not p50 for a bot" is usually
worth more than the ranking.
query_prom is gone. Behind its allowlist it was safe enough for a developer
tool, but no end user asks for a PromQL passthrough, it was attack surface
with no reader, and every extra tool dilutes the routing the useful ones
depend on. Its allowlist and validator went with it.
Shaping lives in src/lib/mcp-tools.ts so it can be tested without a server:
13 tests cover the unit conversion in both directions, provider-name search,
the cap, the longest-phrase use-case match, and the missing-vs-loser rule.
Read-only annotations and structuredContent on every tool.
…reqs # Conflicts: # src/components/site-footer.tsx
Found by auditing the diff rather than by a test. I converted every value on the tool path and left the one on the resource path, which serves the same benchmark to the same clients under the same field names: an agent pinning openchainbench://benchmark/aggregator-head-lag still read 443.494 next to unit "s". Both its value and its leader now go through valueInDeclaredUnit, and its rankings carry a declared-unit `value` beside the stored `ms` like the tool response does. Nine conversions in the file now, one for every raw read.
This was referenced Oct 1, 2026
Flotapponnier
added a commit
that referenced
this pull request
Oct 1, 2026
Listing the MCP server in the ChatGPT app directory requires four reachable URLs, and two of them answered 404 on production: /privacy and /terms. They shipped to dev in #2759 and were waiting on a release that has twenty-odd unrelated commits in it. A reviewer clicking straight through to an error page is a rejection, so they come across on their own. The privacy policy is read off the code rather than drafted from a template, because overstating what we collect is as wrong as understating it. Every claim traces to a line: autocapture off and person profiles disabled in posthog-provider.tsx with nothing ever calling identify; analytics proxied through our own origin so the browser never reaches the analytics host; the server-side id a daily hash of the user agent with no IP stored; rate-limit buckets in an in-memory Map that never reaches disk. The terms carry two clauses that are load-bearing rather than boilerplate. "Not financial advice, we never execute transactions, never send us a key" is what the three negative test cases the review requires point at, and the MCP server was measured against those cases today. The conflict-of-interest disclosure repeats what publisher.json and /partners already say; all three must keep saying the same thing. The domain challenge route comes with them: verification fetches a token as plain text from /.well-known/openai-apps-challenge and the body must be the token alone. The token stays in an env var rather than the repo, because committing it would let anyone prove ownership of this domain for their own submission. Until it is set the route 404s, which is the honest state, so shipping it now costs nothing. Both pages are in the sitemap and linked from the footer; nothing in the rail or the body reaches them otherwise.
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.
Listing the MCP server at
/api/mcp/mcpin the ChatGPT app directory requires four reachable URLs in the submission form: website, support, privacy policy and terms of service. The first two exist. The other two answered 404 on production, so the form could not be filled in at all.This ships the three missing prerequisites.
/privacy
Read off the code rather than drafted from a template, because overstating what we collect is as wrong as understating it. Every claim traces to a line:
posthog-provider.tsx:57-64—autocapture: false,person_profiles: "identified_only", and nothing in the codebase ever callsidentifyapi_host: "/ingest"— analytics are proxied through our own origin, the browser never contacts the analytics host directlyanalytics-server.ts:72— the server-side id is sha256(user-agent + UTC day),$process_person_profile: false, no IP storedrate-limit.ts:21— buckets live in an in-memory Map, never written to disk, lost on deployIf any of those change, this page changes in the same commit.
/terms
Two clauses are load-bearing rather than boilerplate:
publisher.jsonand on/partners. All three must keep saying the same thing./.well-known/openai-apps-challenge
Verification fetches a token as plain text and the body must be the token alone — no JSON wrapper, no HTML error page. The token stays in
OPENAI_APPS_CHALLENGE(Vercel prod env) rather than the repo: committing it would let anyone prove ownership of this domain for their own submission. Until it is set the route 404s, which is the honest state.force-dynamicso rotating it takes effect without a rebuild.Notes
Both pages are in the sitemap and linked from the footer; neither is reachable from the rail, so without that they would be orphans a reviewer could not follow.
Verified:
tsc --noEmitclean,pnpm validatepasses, and the/partnersand/methodologypages the new copy links to both exist.