The required check on an MCP server repository
This page is for the owner of a remote MCP server whose tools a ChatGPT Business, Enterprise, or Edu admin has already approved. The same repository check is what a Claude organisation would point at a default config. This page did not measure a Claude organisation. Measured 2026-09-30.
1. The hole
Help Center article Developer mode and MCP apps in ChatGPT, read 2026-09-30. Curl to help.openai.com that day returned HTTP 403. The words below are the page reader's text of the FAQ answer to "Do MCP app changes auto-update in my workspace?":
No. After an admin first approves an MCP app for the workspace, ChatGPT uses a "frozen" snapshot of its available tools and inputs. Changes made later by the app's developer are not applied until an admin reviews and publishes an update.
If the live app no longer matches the frozen snapshot, tool calls can error.
https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt
The same article's answer on updating after publish, same read:
Business admins/owners cannot currently update apps after publishing; recreate and republish to update tools or metadata. Enterprise/Edu admins/owners can enable or disable app/connector actions after publishing.
The publish section, same read, on Enterprise and Edu action control after publish:
Updates to the MCP server are not automatically enabled - you can click the Refresh button to obtain the latest set of actions, or updates to existing actions. New actions are disabled by default, and changes to existing actions are shown as a diff.
The frozen sentence says "available tools and inputs". The Business sentence is the one that says "tools or metadata". The article does not mention a pull request or a required status check.
On the repository, that review is no merge check. Enterprise and Edu do show a diff of action changes after Refresh. That diff is in Workspace settings. It is not a check on the commit that changed the tool file. Business recreate-and-republish does not say it shows a diff. Neither paragraph stops a merge.
The personal app on ChatGPT Plus is a different page. It reports and does not issue receipts: the ChatGPT app page. Dots was not run: dots and the gate.
2. The recipe
Install the GitHub App on the repository that holds the tool file: github.com/apps/coderifts.
.coderifts.yml names paths. parseConfig, read from the app tree at 8c847f7 (src/config-parser.js, not in that tree's dirty set), accepts schema as a list of strings. Every entry must be a string. The function does not return a type key. An object entry is a parse error. One named file, in the shape the parser accepts:
schema:
- mcp.json
The type mcp_manifest is assigned by the contract-gate Action, not by that file. coderifts/contract-gate at 06b90dc, src/artifacts.js, classifies a changed path when the filename matches (^|/)(mcp[^/]*\.json|tools?-catalog\.json|mcp-manifest\.json). mcp.json, mcp-tool-manifest.json, tools-catalog.json, tool-catalog.json, and mcp-manifest.json match. A tools file under another name does not. The Action derives artifacts from the diff. It does not read schema from .coderifts.yml. The schema list is what the App webhook fetches and diffs. Listing one path does not stop the Action from classifying a second matching file in the same diff.
The customer workflow is examples/contract-gate.yml at 06b90dc. The two contexts, verbatim, from that file and from the App:
- uses: coderifts/contract-gate@v0
name: "CodeRifts / contract-gate" # the required context, verbatim
# The App posts the other context:
# CodeRifts / issuer
@v0 is the example's ref. It is a moving tag. Copying the example as written does not pin a commit. The workflow file on coderifts/demo pull request 4, head df3de0d3ac4bdd1da82ed6449c7780b27ac13c7d, pins coderifts/contract-gate@f01c6f155f258ee9156cb2b6ef378478c6bce26e and names the aggregator job CodeRifts / contract-gate.
The ruleset, measured the same day on coderifts/demo ruleset 22074842 (coderifts-enforcement, enforcement active): bypass_actors is empty. strict_required_status_checks_policy is true. The required checks are CodeRifts / issuer bound to integration id 2860592 and CodeRifts / contract-gate bound to integration id 15368. Classic branch protection on main, read the same day, lists the same two contexts and the same app ids, and its strict is false. The published steps for a customer ruleset are in ENFORCEMENT.md at the same commit. This page does not apply them.
3. A pull request that changes a tool schema or a description
coderifts/demo contains agent-api-demo/mcp-tool-manifest.json. The classifier above matches that filename. .coderifts.yml on that repository lists one path, api/openapi.yaml. It does not list the manifest. Pull request 4 changes api/openapi.yaml only. Title: feat!: breaking changes v1.5.0 — remove phone field + narrow order status enum. State OPEN. mergeStateStatus BLOCKED. There is no pull request on this repository whose diff is that manifest. This page does not open one.
On head df3de0d3ac4bdd1da82ed6449c7780b27ac13c7d, both required contexts concluded failure. CodeRifts / issuer is app id 2860592. CodeRifts / contract-gate is app id 15368. The issuer output.title is "6 breaking API changes detected". The issuer output.summary begins with the five lines in the next section. The contract-gate check-run's output.title and output.summary were null. This page does not invent a log line for it. The rollup also showed contract-gate (Action) failed and canary skipped.
The issuer summary also says it is not claiming enforcement: it could not read branch protection (missing_permission). The ruleset read above is a separate API call, and it does list those two contexts as required. The open pull request, with mergeStateStatus BLOCKED, is the measurement that the change did not merge. It is an OpenAPI change. It is not an mcp_manifest change.
4. For the admin who re-publishes your app
The five lines of the issuer check summary on that head, with the receipt digest in the third line. On this run the digest is n/a. The place is "issued in this run — not in the commit".
STOP · merge · n/a · n/a
not_verified_locally
n/a · issued in this run — not in the commit
npx @coderifts/receipt-verifier
the approver is not in the signature · https://coderifts.com/docs/proof-boundaries/
The first line is the execution action, the operation, a short target, and the expiry. The third line is the receipt digest, then where the receipt sits. When a digest is present, that field is the identifier of the change set the grant was bound to. It is the thing you can hold next to the frozen snapshot. It is not a rendering of the snapshot, and it is not the git diff. The git diff of the tool file is on the pull request. The Workspace settings diff, on Enterprise and Edu after Refresh, is the article's diff. This summary is neither of those.
What this summary shows: those five lines, and behind them a details block (on this run: 6 breaking, 1 safe, 0 unclassified, risk score 84/100, 2 blocking policy violations, and the enforcement-not-verified paragraph). What it does not show: the approver, the frozen tool list, or a digest for this run.
5. Two boundaries
the receipt does not name the approver — the GitHub review does
you would be the first foreign repository with this check required; here is the demo as the only measured stop
The stop measured on 2026-09-30 is pull request 4, ruleset 22074842, both contexts failed, merge state blocked. This round did not enumerate other GitHub App installations.
6. When a check is a lock
a check is a lock only when it is required on the default branch, bound to the right app, bypass closed, SHA pinned — without all four it is a comment
On the demo ruleset the first three hold: required on main, bound to 15368 and 2860592, bypass empty. The pull request's workflow file is SHA pinned. The published example's @v0 is not that pin. Classic protection on the same branch has strict: false. The issuer check on this run does not claim the lock: it could not read protection.
7. Price
From the pricing page, unchanged: Every beta organization receives Team access at $0. Free authorization requires a provider-verifiable public repository. This page adds no price.
What this page does not prove
It does not prove an mcp_manifest diff has failed a required check. The manifest file is in the demo. No pull request changes it.
It does not prove a foreign repository has either context required. The sentence in section 5 is the measurement.
It does not prove the receipt names a person. Line five says the approver is not in the signature.
It does not prove the issuer check verified the ruleset. The summary says enforcement status not verified.
It does not prove @v0 is a commit. The example ref and the pull request's pin are different strings.
It does not prove a personal ChatGPT app can issue a receipt. Analyze needs no key. Authorize needs a key. That limit is on the ChatGPT app page.
It does not prove a shell command, a raw credential, or a tool call outside the guarded table is stopped. The boundary on this page is the merge of the repository that holds the tool file.
It does not prove the Enterprise Workspace diff is this check. That diff is OpenAI's, after Refresh. This check is GitHub's, on the pull request.