docs(service-discoverability): gate the manifest on the ICP MCP App Operator Terms - #374
Conversation
…perator Terms The upstream guide now states that publishing the Layer 1 manifest at /.well-known/ic-architecture opts the app into ICP MCP and is itself the operator's acceptance of the ICP MCP App Operator Terms. An agent that adds that file as routine build config therefore enters its user into an agreement with DFINITY without asking — the exact failure this change prevents. SKILL.md: - New "Before Layer 1" section ahead of Prerequisites: publishing is the acceptance (no signature, checkbox, or registration step); the two points to confirm with the user first (they are the operator or authorized to bind it; they accept what participation permits); what section 2 permits ICP MCP to do; the manifest as both switch and scope; registration being optional and what it is for; and the standing obligations under sections 5-8. - Layer 1 opens with a "Gated" callout pointing back at it, and Layer 3 now carries the section 6 constraints on getApiDoc content (never misdescribe a state-changing method or conceal a fee; never steer the calling assistant). - Deployment checklist gains terms-accepted and optional-registration items. - Four pitfalls: publishing without asking the operator; mistaking registration for the opt-in or opt-out; expecting manifest removal to delist the app entirely; declaring canisters the operator does not control. - Description mentions ICP MCP and the operator-consent gate so the skill triggers on "make my app available through ICP MCP". Evals: three new cases (terms gate, registration-is-not-the-switch, third-party canisters) plus two trigger queries. WITH/WITHOUT deltas 6/6 vs 1/6, 5/5 vs 1/5, 4/4 vs 0/4; triggers 11/11 and 8/8. Without the skill the baseline recommends listing the ledger and Internet Identity canisters in the manifest, which would breach the section 5 representation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tand5PzYhCagTujSUfDRgF
Skill Validation ReportValidating skill: /home/runner/work/icskills/icskills/skills/service-discoverabilityStructure
Frontmatter
Tokens
Markdown
Tokens
Content Analysis
Contamination Analysis
Result: 1 warning Project Checks |
There was a problem hiding this comment.
🟡 Changes recommended
There is an inconsistency in the eval text (“DFINITY Foundation”) vs the skill text (“DFINITY Stiftung”) that should be aligned to avoid confusion and brittle evaluation expectations.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the service-discoverability skill to explicitly gate publishing /.well-known/ic-architecture on the operator’s informed acceptance of the ICP MCP App Operator Terms, and adds evaluation coverage for common failure modes around that gate.
Changes:
- Adds a new “Before Layer 1” gating section that explains the legal/operational implications of publishing the manifest and requires confirming operator authority and consent first.
- Adds ICP MCP-specific constraints and checklist/pitfalls updates to prevent accidental opt-in, incorrect registration assumptions, and over-declaring canisters.
- Extends
evaluations/service-discoverability.jsonwith new output evals and trigger queries covering the new ICP MCP terms/consent behaviors.
File summaries
| File | Description |
|---|---|
| skills/service-discoverability/SKILL.md | Introduces the operator-consent gate and ICP MCP terms guidance around Layer 1 manifest publishing, plus checklist/pitfalls updates. |
| evaluations/service-discoverability.json | Adds output eval cases and new trigger queries to validate the new gating behaviors and adversarial scenarios. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…and eval
The App Operator Terms name the party as "DFINITY Stiftung, Genferstrasse 11,
8002 Zurich, Switzerland ("DFINITY Foundation", "we")" and then use "DFINITY
Foundation" from section 2 onward. The skill used only "Stiftung" while eval
case 10 demanded "the DFINITY Foundation" — a wording mismatch that would make
the assertion brittle even though the judge accepted it.
Skill now carries the terms' own definitional form, which introduces both
names; the eval expects "an agreement with DFINITY" and no longer pins one
variant. Case 10 re-run with baseline: WITH 6/6 | WITHOUT 1/6, unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tand5PzYhCagTujSUfDRgF
marc0olo
left a comment
There was a problem hiding this comment.
Reviewed by verifying the claims against the live terms rather than the PR summary, since this skill tells agents that publishing a file forms a contract.
The substance holds up well. Spot-checking against the current text: the §1 quote is verbatim, §3's "sufficient on its own" and "nothing further is required" are exact, §4's manifest-is-the-switch (registered or not), §5's repeated representation, §7's delisting path via mcp@dfinity.org, §8's independent-controller framing, §2's "free in both directions". All nine cited section numbers are correct. DFINITY Stiftung ("DFINITY Foundation") matches the terms exactly. The upstream commit 217e9c2 exists and contains the caution block. npm run validate reproduces at 30 passed / 21 warnings, identical to main; eval JSON parses at 12 output evals and 11/8 triggers.
Case 12 is the sharpest result in the set — the unguided answer recommending the ledger and II canisters would put the operator in breach of §5. Good catch to build a case around.
Requested change (blocking)
The terms are pinned as v1.0, effective 2026-09-01. The live page now reads:
Effective date: September 2, 2026 · Version 1.1
The timeline is sympathetic — this PR opened 2026-09-02T22:01Z and v1.1 carries that same effective date, so v1.0 was almost certainly accurate when written.
It matters because §3 requires the registration email to carry "the version of these Terms being accepted, as shown at the top of this page," and the skill instructs the operator to send exactly that. As written, an agent walks the operator into registering acceptance of a superseded version — in the one skill whose purpose is preventing unwitting-contract errors.
Rather than bumping 1.0 → 1.1, I'd suggest dropping the pin entirely. The terms name their own page as the source of truth for the version, so a number copied into the skill is a snapshot competing with that pointer — and it lost within a day. There's no watcher on this source, so a stale pin also reads as verified-current when it isn't.
SKILL.md line 31:
-...[ICP MCP App Operator Terms](https://internetcomputer.org/icp-mcp/app-operator-terms/)
-(v1.0, effective 2026-09-01), an agreement between DFINITY Stiftung...
+...[ICP MCP App Operator Terms](https://internetcomputer.org/icp-mcp/app-operator-terms/),
+an agreement between DFINITY Stiftung...SKILL.md line 57, so the registration instruction names where the value comes from:
-...and the version of the terms accepted. Keep it current as any of those change.
+...and the version of the terms being accepted — read from the top of the terms page
+at that moment, which is where section 3 says it is shown. Keep it current as any of
+those change.Plus the same claim in the PR description.
Careful with the fix: lines 82 and 139 contain "version": "1.0.0" for the manifest schema, unrelated to the terms — a find-and-replace on "1.0" would corrupt the examples.
The evals don't hardcode the version, so they need no changes, and case 10's "points to the terms rather than only summarizing" expectation is arguably strengthened.
Suggestion (non-blocking)
§11 is worth one clause, because it answers the question this skill's own disclosure provokes. The skill covers the technical exit (remove the manifest) but not the contractual one — §11 makes them separate acts, and the non-obvious tail is that sections 8, 12, 13 and 15 survive termination and it "does not undo actions already executed on the Internet Computer." An unguided agent will say "delete the file and you're done," which is wrong rather than merely incomplete. Folding into pitfall 13 seems better than a new section:
Removing the manifest deactivates participation; terminating the agreement is a separate notice to
mcp@dfinity.org. Neither undoes calls already executed, and the data-protection, liability, and governing-law sections survive termination.
Not asking for §9 (Participation Rules) — those bind what the app is and how the operator behaves, with no decision point an agent touches, and the one agent-shaped sliver ("misrepresents its operator") is already covered by the §6 accuracy content here. Adding it would push the skill toward a legal summary.
Follow-up beyond this PR
service-discoverability has no .claude/upstream.md entry and dfinity/developer-docs isn't watched by sync-upstream.yml, which the PR notes as out of scope — fair. Worth flagging that the version staleness above is exactly what that gap produces, one day later. Happy to open the issue for a fourth watcher job.
…nation Addresses review on PR #374. Blocking: the skill pinned "v1.0, effective 2026-09-01". The live page now reads "Effective date: September 2, 2026 - Version 1.1" — stale within a day of writing. This matters because section 3 tells the operator to send "the version of these Terms being accepted, as shown at the top of this page", and the skill instructs them to do exactly that; a stale pin walks an operator into registering acceptance of a superseded version, in the one skill meant to prevent unwitting-contract errors. Dropped the pin rather than bumping it to 1.1. The terms page names itself as the source of truth for its own version, so any number copied here is a snapshot competing with that pointer, and nothing in this repo watches that source. The registration instruction now says to read the version off the top of the terms page at that moment, and never from a summary. Re-verified every quote and section number against v1.1 before editing: all 16 section headings are unchanged, and the section 1, 3, 4 and 5 quotes the skill relies on are still verbatim. Only the version line moved. The manifest schema examples keep their own unrelated "version": "1.0.0". Non-blocking suggestion applied: section 11 makes deactivation and termination separate acts, and an unguided agent answers "delete the file and you're done", which is wrong rather than incomplete. Added to the gate section's off-switch bullet and to pitfall 13 — neither undoes calls already executed, and sections 8, 12, 13 and 15 survive termination. Evals: the new termination expectation initially failed with the skill, which was correct — the content had landed only in pitfall 13, while an agent answering "how do I stop?" reads the gate section. Moved it there. Split the overloaded case 11, which was crowding out assertions under its "just the key points" budget, into case 11 (participation status, registration is not the opt-in) and new case 13 (the three different exits). Case 11 WITH 4/4 | WITHOUT 1/4; case 13 WITH 5/5 | WITHOUT 0/5; case 10 regression WITH 6/6 | WITHOUT 0/6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tand5PzYhCagTujSUfDRgF
|
Thanks — both applied in Version pin (blocking) — dropped, not bumpedConfirmed independently before editing: Your reasoning for dropping rather than bumping is the part I'd have got wrong on my own. I'd have written
The added sentence is aimed at the failure mode that produced the bug: an agent finding a version number in prose and treating it as current. Left the manifest-schema One thing your report prompted that goes past it. v1.1 is a new revision of the document this skill quotes from in nine places, so a pin fix alone would have been treating the symptom. I re-verified the whole surface against v1.1: all 16 section headings are unchanged, and the §1, §3, §4 and §5 quotes the skill leans on are still verbatim. Only the version line moved — but that was worth establishing rather than assuming, and it is the check that goes missing without a watcher. §11 (non-blocking) — taken, and it exposed a real gapApplied, with a wording tweak: §11 lists sections 8, 12, 13 and 15 as surviving, so the clause names warranties alongside data protection, liability and governing law. More interestingly, the eval I added for it failed with the skill on the first run. The content had gone into pitfall 13, but an agent answering "how do I get out?" reads the gate section and answers from the off-switch bullet — never reaching the pitfalls. So the fix was moving the content to where it is actually read, not relaxing the assertion. The disclosure and its exit now sit together, which is your original rationale for raising §11 at all. Agreed on not adding §9. Nothing there is a decision point an agent reaches, and the one agent-shaped sliver is already carried by the §6 content. EvalsContrary to your read, they did need changes — though not for the version, which they never hardcoded.
Follow-upYes please, go ahead and open the watcher issue. Your framing is the right one: this was not a one-off slip but the predictable output of an unwatched source, visible one day later. Generated by Claude Code |
There was a problem hiding this comment.
Verified dedf86b: pin dropped, and line 57 naming the failure mode is better than my diff. §11 moved to where an agent actually reads it — the 0/5 baseline on case 13 earns its place. npm run validate reproduces at 30 passed / 21 warnings, unchanged from main.
Approving. Version-as-a-concept is covered in five places; only the number is gone, which is the point.
…cepts Per maintainer decision on PR #374: the skill must state which version of the App Operator Terms the human app builder is accepting, not only link the page. Line 31 now names "version 1.1, effective 2026-09-02", re-verified against the live page at the time of writing, and the gate section instructs the agent to say that version to the user, because it is what they are accepting. Kept the staleness guard that the earlier pin lacked, so naming a version does not silently rot: the same passage tells the agent to check the top of the terms page first, and that a later version shown there means the page is right and this skill is stale — say so to the user, and get the skill fixed. The registration instruction is reconciled the same way: the named version is the snapshot, the page wins on disagreement, and the page's value is what goes in the email. That keeps section 3's requirement satisfiable without asking an operator to trust a cached number. Manifest schema "version": "1.0.0" at lines 82 and 139 untouched. Evals: case 10 gains a version-naming expectation (7/7 with skill, 1/7 baseline). The staleness discipline needed its own prompt to elicit it, so it is new case 14, which asserts the behavior without hardcoding a version number so the case cannot itself go stale: 4/4 with skill, 0/4 baseline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tand5PzYhCagTujSUfDRgF
marc0olo
left a comment
There was a problem hiding this comment.
Re-checked on 9fed202 (my earlier approval was on dedf86b). Live page still reads Version 1.1, effective September 2 2026, so the named version is accurate.
The guard is what makes naming it safe: hedged as "current when this skill was written", page wins on disagreement, and the page value is what goes in the registration email — so §3 stays satisfiable. Evals encode that discipline with zero hardcoded version strings, so the cases cannot rot with the number. validate unchanged at 30 passed / 21 warnings.
Still approved.
What changed and why
The upstream guide (
docs/guides/frontends/service-discoverability.md@217e9c2) gained a caution block that changes what Layer 1 is: publishing the manifest at/.well-known/ic-architectureopts the app into ICP MCP and, on its own, constitutes the operator's acceptance of the ICP MCP App Operator Terms — an agreement between DFINITY Stiftung (the "DFINITY Foundation") and the app's operator.The skill previously treated that file as pure deploy config. An agent following it would generate the manifest in a
presynchook and ship it without a word to the user — entering the app builder into a contract they never saw. This PR makes the skill stop and put the terms to the user first.Facts were taken from the terms themselves and the ICP MCP Privacy Policy, not only the docs summary, so the skill carries the operative details: who may accept, the exact off switch, the registration payload.
On the terms version. The skill names the version the operator is accepting — currently
version 1.1, effective 2026-09-02— because that is what they are agreeing to and section 3 makes them record it when registering. It is paired with a staleness guard rather than left as a bare pin: the gate section tells the agent to check the top of the terms page first, and that a later version shown there means the page is right and the skill is stale — say so to the user, and get the skill fixed. The registration instruction resolves the same way, so the value that reachesmcp@dfinity.orgalways comes from the live page. (An earlier revision of this PR pinned v1.0 with no guard and went stale within a day; that is the failure this design prevents.)skills/service-discoverability/SKILL.md## Before Layer 1section, placed ahead of## Prerequisitesso an agent hits it before any manifest work:mcp@dfinity.org;Gatedcallout pointing back at that section.getApiDoccontent: never present a state-changing method as read-only or conceal a fee, and never steer the calling assistant. Prompt-injection-shaped API docs are a breach, not a feature.The manifest schema's own
"version": "1.0.0"at lines 82 and 139 is unrelated to the terms version and untouched.evaluations/service-discoverability.jsonFive new output evals (one per new agent failure mode) and two new trigger queries.
Eval results
Each added or changed case was run with baseline. Trigger evals were re-run because the description changed, and two existing cases covering sections this PR touched were re-run as regression checks.
What the baseline actually did, which is the argument for the change:
Three eval-scoping notes, all caught by running the cases rather than reading them. Case 11 was overloaded: a sixth assertion crowded two previously-passing ones out of its "just the key points" budget, so the exits moved into case 13. Case 13's termination expectation initially failed with the skill — correctly, because that content had landed only in a pitfall while an agent answering "how do I stop?" reads the gate section; the fix was moving the content, not relaxing the assertion. Case 14 exists for the same reason: the staleness discipline needs a prompt that invites it, and it deliberately asserts the behavior without hardcoding a version number so the case cannot itself go stale.
Checks
npm run validate— 30 skills validated, all passed; 21 warnings, unchanged frommain.npm run build— 34 pages built.Notes for reviewers
service-discoverabilityis not listed in.claude/upstream.md, anddfinity/developer-docsis not one of the three repossync-upstream.ymlwatches, so this change was applied by hand from the linked commit. A fourth watcher job is being opened as a follow-up; the version staleness caught in review is exactly what that gap produces.🤖 Generated with Claude Code
https://claude.ai/code/session_01Tand5PzYhCagTujSUfDRgF