Skip to content

RFC-014 draft: tool presence, toolsets and surfaces - #34

Merged
antra-tess merged 16 commits into
mainfrom
rfc-014-surfaces
Oct 25, 2026
Merged

antra-tess merged 16 commits into
mainfrom
rfc-014-surfaces

Conversation

@antra-tess

Copy link
Copy Markdown
Contributor

Adds RFC-014, Tool Presence, Toolsets and Surfaces. It lets servers say which tools belong in a model's stable tool list and which come and go with state, and leaves it to hosts to present them without rebuilding the prompt cache.

The problem

  • A model's tool list renders first and is cached; any change rebuilds the whole cache.
  • Servers either list everything all the time (world servers with ~50 tools, hosts with hundreds) or narrow the list by state through tools/list_changed, paying a full rebuild per mode change.

What it adds

  • Per-tool hints in _meta:

    • mcpl/presence — static (default) | dynamic
    • mcpl/visible — available right now
    • mcpl/toolset — which toolset the tool belongs to

    tools/list stays the full truth.

  • Toolsets. They are declared in the manifest, can be opened as named instances, and are handled with toolsets/open|close|list (Host → Server). The server is session-aware; toolsets belong to the principal; toolsets/list is the truth on reconnect, including toolsets that ended while away.

  • Hint changes {rev, tools, toolsets, suggest}. They travel in call results (a call reports its own consequences), on the push event that explains them, or standalone as tools/hints, in rev order.

  • Host presentation (guidance):

    • static tools stay in the header;
    • dynamic tools are announced in the conversation and called through host meta-tools, or projected where the model API can change tools append-only;
    • reminders are re-sent when announcements leave the context;
    • hosts may make toolsets of their own for servers that declare nothing.
  • Optional on both sides. Servers that send no hints, and hosts that don't advertise toolHints, see today's behaviour.

History. Revision 1, the first commit here, had servers implement four tools and a sub-protocol; that design needed per-service adapters. Revision 2 moves presentation to the host. Later commits fold in findings from three implementations. Semantic usage hints were considered and rejected: servers can't know how an agent will use their tools.

Implementations

  • mcpl-cc-bridge (host)
  • games-mcpl (native server)

Both PRs are linked below.

🤖 Generated with Claude Code

antra-tess and others added 8 commits October 24, 2026 17:06
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Presentation moves to the host. Servers keep their full tool list and hint
per tool: static or dynamic, visible now, toolset. Toolsets open and close per
session (agent, host policy, server suggestion), optionally as named instances.
Hint changes ride in call results, on push events, or standalone, ordered by a
per-connection rev. The four tools become the host's meta-tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e on open as content

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… content, unique names, compat tools)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…list is truth on reconnect, ended toolsets, principal-wide state, refused pushes, rev on tools/list, retitling, tailoring)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nstance required on instance toolsets; hints vs grant

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…from toolsets/list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antra-tess

Copy link
Copy Markdown
Contributor Author

Related: RFC-015 #33 · host implementation anima-research/mcpl-cc-bridge#10 (stacked on #9) · native server anima-research/games-mcpl#1

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review

Recommendation: fix, then merge. 1 high severity finding should be fixed first.

Reviewed commit d770560 with claude-opus-5-5, by reading the code. It did not run the code or look at the checks.

Does this change belong in the project?

It fits the project. This draft RFC belongs in the project. It proposes an optional, backward-compatible MCPL extension for tool hints and toolsets, and it follows the format and placement of the repository's other draft RFCs.

1 reason with sources
  • It serves MCPL's stated purpose: a backward-compatible MCP extension that makes servers active participants. Servers tell hosts which tools are static and which are dynamic, and which are visible right now, using push events and manifest declarations. The RFC is optional on both sides: hosts and servers that don't advertise toolHints behave as they do today. That matches the README's description of MCPL as a 'backward-compatible extension'. (README.md (intro, Features))

Findings

9 findings of medium or high severity, see the inline comments:

7 more findings, less severe
  • Low severity, documentation, RFC-014-surfaces.md:198-209. Rationale 'makes a host's re-open after a reconnect safe' contradicts the rule that hosts do not re-open after reconnect. Line 199 justifies adoption with "a host's re-open after a reconnect". Lines 205-209 then say that on reconnect the host takes toolsets/list as the truth and "does not re-open on its own". The first sentence reads as left over from an earlier design and gives implementers the wrong reconciliation strategy. Re-opening after reconnect would also restart toolsets that ended while the host was away.
  • Low severity, documentation, RFC-014-surfaces.md:176. default is 'not allowed with instances' but is a required field in ToolsetDecl and appears in the instances example. The field table says default is "Not allowed with instances". The schema at line 499 makes default: "open" | "closed" required, and the manifest example at line 167 sets "default": "closed" together with "instances": true. The text probably means that "open" is not allowed with instances. As written, a strict validator built from the prose rejects the RFC's own example.
  • Low severity, documentation, RFC-014-surfaces.md:431. Refers to RFC-015, which does not exist in the repository. Lines 13-14 and line 431 rely on RFC-015 for how a surface host obtains the agent's credentials. No RFC-015 file exists in the repository. The surface-host design in RFC §8.6 therefore depends on a credential mechanism that is unspecified. Mark the dependency as pending, or describe it here.
  • Low severity, documentation, RFC-014-surfaces.md:486-493. Schema omits the hints carried in the toolsets/close result. Section 5.2 (line 211) says the close result "carries the hint changes it caused, like an open". The schema lists "mcpl/hints" only for the tools/call and toolsets/open results, and gives toolsets/close as → {content?}. Schema-driven implementations will not parse hints from close results.
  • Low severity, documentation, RFC-014-surfaces.md:7-14. Header says the RFC only 'uses' SPEC sections, but it amends §9.2, §16.1 and Appendix B. The RFC adds hints, toolset and instance to the push/event params (§9.2), reserves a new surface: tag namespace (§16.1 reserves only chat: and mcpl:), adds new methods and _meta keys, and changes the schema. The header lists none of these as amendments. Other RFCs list them, for example RFC-013 at lines 9-13 ("Amends SPEC §9.3 … Appendix B"). Integrators reading the header will miss the SPEC sections that need editing.
  • Low severity, documentation, RFC-014-surfaces.md:118. Conflict between an explicit presence: "static" and mcpl/toolset is unresolved. mcpl/toolset "implies presence: \"dynamic\"", but the RFC does not say what a host does with a tool that declares both mcpl/presence: "static" and a toolset. The same question arises when a hint change moves a toolset tool to presence: "static". Hosts may put such a tool in the header or treat it as dynamic, and so present it differently. State which hint wins, or make the combination invalid.
  • Low severity, test coverage, RFC-014-surfaces.md:507-521. No vectors for ended records, multi-connection propagation or static/dynamic moves. Several normative behaviours have no conformance vector. One is the ended record, reported once per connection and kept for a bounded time (RFC §5.3). Others are open, close and visibility changes made on one connection reaching another as hint changes (RFC §5.6), presence moves (RFC §4.2), and close results carrying hints. These vectors need to be frozen "before acceptance" as well, otherwise implementations can diverge on them unnoticed.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +310 to +317
A server **MUST** send hint changes on a connection in `rev` order — whichever of the three
ways each travels — so that a lower `rev` never arrives after a higher one (allocate the `rev`
and write the message in one step). The host applies changes in `rev` order and ignores any
with a `rev` it has already passed; a host that sees a gap or a reversal anyway re-reads the
state with `toolsets/list`.
A host applies the `hints` of a push only if it accepts the push; if it refuses one (or the
server gets no answer), the server re-sends the same changes as `tools/hints` with the same
`rev`. A host applies each `rev` at most once.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High severity. Re-sending a refused push's hints as tools/hints contradicts the rev-ordering rule, so the change is dropped.

The server MUST send hint changes in rev order so that "a lower rev never arrives after a higher one", and the host "ignores any with a rev it has already passed". But when a host refuses a push carrying hints, or the push gets no answer, the server must re-send "the same changes as tools/hints with the same rev". That re-send comes after the push response, and by then later revs (in tool results or other pushes) may already have been sent and applied. A conforming host then discards the re-send as stale, so the visibility change is lost. Or the host treats the refused push as a gap and falls back to toolsets/list every time. The rule needs a defined resolution: allocate a new rev for the re-send, send no further hints until the push is answered, or have the host hold back later revs.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +319 to +320
`definition` lets a dynamic tool's options change without a `tools/list` round trip. A static
tool's definition changes only through `tools/list_changed`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. definition hint lets a server change a tool's definition, including RFC-008 class, without the re-list hosts rely on for policy.

definition?: Tool in a hint change replaces a dynamic tool's whole definition "without a tools/list round trip", and line 322 says hint changes need no capability. RFC-008 (lines 100-102) has class changes announced through tools/list_changed, after which the host recomputes the effective class on re-list. A definition carried in _meta can include a new mcpl/class, a new description or new annotations. That bypasses the host's re-list and class-policy path, and the RFC's security section does not mention it. The RFC should restrict which fields definition may change (for example the inputSchema only), or require hosts to re-run their tool policy on it.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +364 to +367
- Latest-value state (progress, pose, health) uses RFC-006 plain coalescing with a key per
instance. Activity uses RFC-006 deferred coalescing where the host supports it, so a burst
arrives as one message describing the present. A visibility change is never carried only by
a replaceable event.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Applying hints on coalesced or deferred pushes is undefined.

Activity is told to use RFC-006 deferred coalescing, and plain coalescing for latest-value state. Hints, however, are applied "only if [the host] accepts the push" (line 315). In RFC-006 a deferred notice joins a pending batch that is not visible to the model, and a plain occurrence can be replaced. The RFC does not say whether hints on a joined or replaced notice are applied on acceptance or when rendered, or whether they are lost with the replaced occurrence. "A visibility change is never carried only by a replaceable event" implies a second carrier. But a second carrier would need either the same rev, which the host then ignores, or a new rev, which contradicts "the same changes". Implementations will disagree on whether such visibility changes take effect.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +130 to +135
- In `tools/list`, `mcpl/visible` on a toolset's tool means "visible in at least one open
instance"; per-instance visibility travels in hint changes (RFC §6). The list carries the
declared definitions, and its result carries `_meta["mcpl/rev"]`: the hint revision it
reflects, so a host applies only later changes on top of it. That revision covers what the
list states — definitions and coarse visibility. **A host's per-instance baseline comes from
`toolsets/list`**, whose hints state every open instance's visibility in full.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Gap recovery through toolsets/list cannot restore updated definitions or static/dynamic moves.

A host that sees a rev gap must re-read state with toolsets/list (line 313). But tools/list carries only "the declared definitions", while a dynamic tool's updated definition (new enum options) and presence moves travel only in hint changes. toolsets/list is described as returning visibility per instance. It does not cover undeclared toolsets, which the server does not track, or moves of tools outside any toolset. After a lost tool result, which can happen through cancellation or timeout, the host can therefore not recover the current options of a dynamic tool or a presence move. The baseline that toolsets/list returns should be defined to include current definitions and presence.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +194 to +199
- `instance` is a name the host proposes (the agent's or its own). The server MAY return a
different one if it must; the returned name is the instance's id for the session. For a
toolset without `instances`, `instance` is absent.
- Opening what is already open — a toolset without instances, or an instance by a name that is
open — **adopts** it: the result describes the existing one and changes nothing. This makes a
host's re-open after a reconnect safe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Server renaming an instance breaks the 'adopt' idempotency for retried opens.

The server MAY return a different instance name from the one the host proposed. Re-opening is safe only by adoption, and adoption keys on "an instance by a name that is open". If an open's response is lost (timeout, disconnect) after the server renamed the instance, the host retries with the name it proposed. The server then finds no open instance by that name and opens a second game. Opening has side effects (line 208), so a retry is not idempotent exactly in the case adoption is meant to cover. Renaming should be forbidden, or the server should treat a re-proposed name as an alias.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +127 to +129
- `tools/list` **MUST** list every tool, visible or not, so that a host that ignores hints sees
today's behaviour. Calling an invisible tool — or a tool of a toolset that is not open —
returns an ordinary tool error that says why.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Calls to tools of a toolset that is not open must fail, but the server cannot know for undeclared toolsets.

Section 4.1 says calling "a tool of a toolset that is not open — returns an ordinary tool error". Section 5.4 says that for undeclared toolsets the host opens and closes the group alone, sends no toolsets/open or toolsets/close, and "the server does not track it". A server therefore cannot enforce or report "not open" for undeclared toolsets, and the rule cannot be met as written. Conformance vector 10 and these two sections should say which side gates calls for undeclared groups.

Comment thread RFC-014-surfaces.md
Comment on lines +507 to +521
## 13. Conformance vectors (to freeze before acceptance)

1. A server with hints lists every tool in `tools/list`, visible or not.
2. A call that makes tools visible carries `mcpl/hints` in its result; no push follows for it.
3. A push explaining a visibility change carries it in `hints`; the host applies it once.
4. Out-of-order `rev`: the stale change is ignored.
5. `toolsets/open` on a toolset with `instances` returns an instance name; calls carry it.
6. A call to an instance-bound tool with two instances open and no `mcpl/instance` fails,
naming them.
7. A `definition` change to a dynamic tool reaches the host without `tools/list_changed`.
8. A server whose host did not advertise `toolHints` sends no `tools/hints`.
9. After a reconnect, `toolsets/list` reports an instance the server kept; the host adopts
it rather than opening a second.
10. A tool tagged with an undeclared toolset: the host never sends `toolsets/open` for it,
and the tool is visible only while the host has the group open.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity, test coverage. No conformance vector for the refused-push re-send or gap recovery.

The rev rules are the most error-prone part of the protocol: a refused or unanswered push re-sent as tools/hints with the same rev, at-most-once application, and a gap or reversal triggering toolsets/list. Yet the vectors cover only "out-of-order rev: stale change ignored" (vector 4). Add vectors for a refused push whose hints arrive later as tools/hints, and for a host detecting a gap and recovering its baseline. These would also surface the ordering conflict in RFC §6.1.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +197 to +199
- Opening what is already open — a toolset without instances, or an instance by a name that is
open — **adopts** it: the result describes the existing one and changes nothing. This makes a
host's re-open after a reconnect safe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Adopting an existing instance silently ignores differing options.

An open for a name that is already open "adopts it: the result describes the existing one and changes nothing", even when the options differ (for example size 19 requested, 9×9 open). Toolsets belong to the principal and are shared across connections (RFC §5.6), so a second connection or agent that proposes a common name such as "game" gets an unrelated existing game with no signal of the mismatch. The RFC should require the server to report the mismatch, or reject the open when its options conflict with the existing instance.

Comment thread RFC-014-surfaces.md Outdated

### 5.1 Declaration

In the manifest (§17), so changes are announced by `mcpl/manifestChanged`:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Toolset declarations fall in the manifest's capabilities domain, which conflicts with the RFC's 'no authority' stance.

The RFC puts toolHints, toolsets and surface in experimental.mcpl, so that changes are announced by mcpl/manifestChanged. Under SPEC §17.1, every member other than version, revision and featureSets belongs to the capabilities domain. Per §17.5 and §17.6, adding one becomes capability-expansion-pending awaiting a policy decision, and removing one becomes capability-revoked. Adding a toolset would then produce grant and receipt work that the RFC ("adds no uses path and changes no grant") says does not exist. The RFC should state how hosts classify these members in the diff and the receipt. The surface-changed impact name also clashes with the RFC's new meaning of "surface".

antra-tess and others added 2 commits October 25, 2026 10:25
…nts on coalesced pushes, definition limits, recovery, no renaming, adopted, undeclared gating, manifest domain, schema, vectors)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review

Recommendation: merge and fast-follow. 11 medium severity findings can be fixed after the merge.

Reviewed commit 10513c8 with claude-opus-5-5, by reading the code. It did not run the code or look at the checks.

Does this change belong in the project?

It fits the project. This is a draft RFC that extends how MCPL servers and hosts handle tools. It follows the repo's existing RFC process and format, and it adds no new authority, so it fits.

6 reasons with sources
  • Proposing protocol extensions as RFC files at the repo root is how the project works. The README says SPEC.md is the authoritative text and that RFC files keep the proposal and review history. RFC-001 through RFC-013 sit at the root, and some, such as RFC-007, are still drafts. This PR adds one more draft file and does not touch SPEC.md. (README.md (Status))
  • The RFC extends MCPL's stated goal of making servers active participants in the inference lifecycle. Hint changes ride on push events, call results and a new notification. They build on features MCPL already has: push events, manifest changes and event tags. The RFC also stays backward-compatible, which the README's opening line requires: hosts that don't advertise toolHints see today's behaviour. (README.md (intro, Features))
  • It follows the format and conventions of its sibling RFCs. Like RFC-007, it has a Status/Targets/Depends-on header, revision notes, SPEC amendment lists, conformance vectors listed before acceptance, and open questions. It builds on RFC-006 and RFC-008 rather than redefining them. (RFC-007-tool-lifecycle.md (header))
  • It leaves authority with the existing capability grant system. The draft says RFC-002 / SPEC §5.4 remains the only source of authority and that hints confer nothing. So it does not compete with the existing grant model. (README.md (Capability Grants); RFC-014 header)
  • For the maintainers: the surface-host part (§8.6) relies on an RFC-015 that is not in this repository. No RFC-015 file exists in the repo. The dependency is limited to guidance on surface hosts outside the main host, so the rest of the RFC stands on its own. (repository file list)
  • Acceptance will later need executable vectors, as the README already requires for other drafts. The draft lists 20 vectors 'to freeze before acceptance', which is consistent with the project's practice. As a draft, it does not have to meet that bar yet. (README.md (Status, RFC-005 note))

Findings

10 findings of medium or high severity, see the inline comments:

6 more findings, less severe or past the limit on comments
  • Medium severity, RFC-014-surfaces.md:290-294. One host's policy close affects every connection of the principal. Toolsets belong to the principal (§5.6), and the host may close a toolset by its own policy, for example when it has been idle for many turns (line 224). If two connections (for example two sessions of the same agent) share a principal, one host closing an instance it considers idle closes it for the other connection as well, possibly forfeiting a live game (see the close example on line 210). The RFC should limit host-policy closes to toolsets that host opened, or warn about the cross-connection effect.
  • Low severity, documentation, RFC-014-surfaces.md:151-154. "Every toolset is implicitly open" conflicts with instance toolsets always being closed. Line 151 says that for a host without toolHints every toolset is implicitly open. Line 195 and the schema at line 536 say a toolset with instances is always "closed" and has no implicit instance. The text does not say what implicit openness means for an instances toolset: which instance name, or whether there is one at all. The "may instead offer plain tools" escape covers only part of the case.
  • Low severity, documentation, RFC-014-surfaces.md:248-254. Garbled sentence in the toolsets/list description. Line 250 reads "…differs from the listed one — and — so a host can tell…". A dash pair and "and" are left over from an edit, so the sentence that defines what toolsets/list returns is hard to parse. This is normative content (full visibility, current definitions, ended) and should read cleanly.
  • Low severity, documentation, RFC-014-surfaces.md:11-14. Amendment list leaves out the §5.1/§5.2 capability advertisement. The RFC adds a toolHints advertisement for both server and host (§6.3) and a surface manifest member. These amend SPEC §5.1 (Server Capabilities) and §5.2 (Host Support), the sections RFC-007 lists for the same kind of change. The "Amends" list names §4, §9.2, §16.1, §17.1, Appendix B and the Changelog but not §5.1-5.2. It also leaves out the new _meta on tools/list and tools/call. Revision 3 says the amendment list is complete.
  • Low severity, test coverage, RFC-014-surfaces.md:132. No conformance vector for ignoring presence: "static" on a toolset tool. Revision 3 added the rule that an explicit presence: "static" on a tool with mcpl/toolset is ignored and the tool is treated as dynamic. None of vectors 1-20 covers it. Add a vector in which a tool with both mcpl/toolset and mcpl/presence: "static" stays out of the header.
  • Low severity, test coverage, RFC-014-surfaces.md:195. No vector for default: "open" toolsets. A default: "open" toolset is open from the start of a session and appears in toolsets/list without any toolsets/open. No conformance vector checks this. Add one, including the host adopting such a toolset rather than opening it.

Comment thread RFC-014-surfaces.md Outdated

```ts
interface HintChanges {
rev: number; // per connection, strictly increasing

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. rev only has to increase, so a host cannot tell a gap.

HintChanges.rev is defined as "per connection, strictly increasing" (line 304). The recovery rule (lines 346-349) then tells a host to re-read both lists when it "sees a gap". With revs that only have to increase, a jump from 5 to 9 is legal and indistinguishable from lost changes. One host may treat every skip as a gap and re-read all the time, while another never detects real loss. The text should require consecutive revs (+1 per change object on a connection), or drop gap detection as a trigger.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +341 to +344
A host applies the `hints` of a push only if it accepts the push. If it refuses one, or the
server gets no answer, the server **restates** the current state of the instances that push
concerned under a **new** `rev`, standalone — never the old `rev`, which later changes may
already have passed. A host applies each `rev` at most once.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. A refused push leaves a rev hole that looks like a gap.

When a host refuses a push carrying hints at rev N, it does not apply N. The server restates the state later under a new rev M > N. The text never says whether the refused N counts as "passed". If it does not, the host sees a gap at N as soon as N+1 arrives and starts a full tools/list + toolsets/list recovery on every refused push. If it does, the ordering rule at lines 337-339 needs to say so. Until the restatement arrives, any change with a rev between N and M is also applied on top of a state that is missing N's changes.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +335 to +341
A server **MUST** send hint changes on a connection in `rev` order — whichever of the three
ways each travels — so that a lower `rev` never arrives after a higher one (allocate the `rev`
and write the message in one step). The host applies changes in `rev` order and ignores any
with a `rev` it has already passed. Entries state values (`visible: true`, a definition), not
differences, so applying an entry again is harmless.

A host applies the `hints` of a push only if it accepts the push. If it refuses one, or the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Accept-then-apply on pushes conflicts with ignoring revs already passed.

The server only guarantees the order in which hint-carrying messages are written. However, the host applies a push's hints only once it decides to accept the push, and that decision can be asynchronous because push/event is a request. In the meantime a tool result carrying rev N+1 can arrive and be applied. When the push with rev N is then accepted, the rule "ignores any with a rev it has already passed" discards it. Because the push was accepted, the server never restates it, so the visibility change is lost silently. The rule needs to define ordering in terms of arrival rather than acceptance, or require the host to apply hints in arrival order whatever it decides about the event.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +346 to +349
**Recovering.** A host that sees a gap or a reversal, or lost a result carrying hints (a
timeout, a cancelled call), re-reads `tools/list` (definitions and presence) and
`toolsets/list` (open instances, their visibility and their current definitions, RFC §5.3) and
takes them as its new baseline.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Recovery from two separate reads has no single consistent baseline rev.

Recovery re-reads tools/list (which carries _meta["mcpl/rev"]) and toolsets/list (which carries mcpl/hints.rev) as two independent requests. Changes can land between the two reads, so the revs differ. The RFC does not say which rev becomes the baseline, or how to merge definitions taken at rev A with per-instance visibility taken at rev B. Hint changes with revs between A and B can then be dropped or applied twice against the wrong base.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +213 to +220
- `instance` is a name the host proposes (the agent's or its own), or omits to let the server
choose one. A proposed name **MUST** be used as given, or the open refused (for example an
invalid name); the server **MUST NOT** substitute another, so that a retried open finds what
the first one opened. For a toolset without `instances`, `instance` is absent.
- Opening what is already open — a toolset without instances, or an instance by a name that is
open — **adopts** it: the result describes the existing one, changes nothing, and carries
`adopted: true`. If the request's `options` were not applied, the result **MUST** say so in
its `content`. This makes a retried open safe; it is not a reconnect strategy (below).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. A retried open without an instance name is not safe.

The RFC says adopting makes "a retried open safe" (line 220), and that a proposed name must be kept "so that a retried open finds what the first one opened". It also lets the host omit instance so the server picks a name. If such an open times out and is retried, nothing identifies the first instance, so the server creates a second one (for example a second game of Go). Either require hosts to propose a name whenever they might retry, or add an idempotency key.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +251 to +254
host can tell "never had one" from "it ended while you were away" — toolsets that closed
recently while no connection of the principal could be told. Each connection hears each such
record once, on its first `toolsets/list` after the end; servers keep the records for a
bounded time (the reference SDK: an hour):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. ended records repeat on every reconnect for up to an hour.

ended records are delivered "once per connection" and kept for a bounded time (an hour in the reference SDK). A reconnect is a new connection, and hosts must call toolsets/list after every reconnect and tell the agent about anything ended. So a host on a flaky link tells the agent "your game ended" again on each reconnect during that hour. The record should be retired once any connection of the principal has been told, or deliveries should be tracked per principal or host session instead of per connection. Vector 13 makes the per-connection behaviour normative.

Comment thread RFC-014-surfaces.md
Comment on lines +305 to +311
tools?: Array<{
name: string;
instance?: string; // required for a toolset with instances: visibility is per instance
presence?: "static" | "dynamic";
visible?: boolean;
definition?: Tool; // dynamic tools only: a new definition (new enum options)
}>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Per-instance definition contradicts "instances share definitions".

Lines 155-156 and 276 state that instances share their toolset's definitions and that a name identifies one definition. Yet a hint tools entry can carry both instance and definition (for example new enum options such as the legal moves in one particular game). §5.3 also restates "the current definition of every visible dynamic tool" without saying per instance. With two Go games open, it is undefined whether a definition sent with instance: "vs-ada" applies only to that instance or replaces the shared definition. Hosts will diverge, and act may validate arguments against the wrong game's options.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +351 to +355
`definition` lets a dynamic tool's options change without a `tools/list` round trip. It may
change the description, `inputSchema` and `annotations` only: its `name` and its RFC-008 class
(`_meta["mcpl/class"]`) are those of the listed definition, and a host **MUST** ignore any
other value there. A host applies its tool policy to a definition received this way exactly as
to a listed one. A static tool's definition changes only through `tools/list_changed`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity, documentation. Unclear which fields a hint definition may change.

definition is typed as a full Tool, which includes _meta with mcpl/presence, mcpl/toolset and mcpl/visible. The text says it changes only description, inputSchema and annotations, but the MUST covers only name and mcpl/class ("any other value there"). It does not say whether a host must ignore a changed mcpl/toolset or mcpl/presence inside a definition, so a server could move a tool between toolsets or out of the stable list by that route. The MUST should cover every field outside the three that may change. Vector 20 tests only mcpl/class.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +382 to +385
or the `hints` push member to a host that did not advertise `toolHints`. It **MAY** tailor
everything else to the host as well — omit the toolset declarations, hint `_meta` and any
hints-only tools — so that a host without `toolHints` sees exactly what it saw before this
RFC.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity, documentation. Letting servers omit "hints-only tools" contradicts "tools/list MUST list every tool".

§4.1 (line 141) and design principle 1 say tools/list MUST list every tool so that nothing is hidden. Vector 1 tests this. §6.3 lets a server omit "any hints-only tools" for a host without toolHints, and §11 adds extra tools (toolset_open/toolset_close) for such hosts. So the tool list differs by host capability, and the MUST is not true as written. The rule should be scoped or the exception stated.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +504 to +507
- A server with toolsets **MAY** offer a host that did not advertise `toolHints` two plain
static tools that open and close toolsets (the reference SDK names them `toolset_open` and
`toolset_close`), so it remains usable there: the toolset's tools are in the list, and a
call without `mcpl/instance` goes to the only open instance.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Hosts without toolHints cannot address one of several open instances.

For hosts without toolHints, the fallback offers toolset_open/toolset_close, and a call without mcpl/instance "goes to the only open instance". Nothing stops such a host from opening two instances through toolset_open. A plain host does not set mcpl/instance, so every call after that fails with the multiple-instances error from §5.5, and those games can no longer be played. The fallback should limit such hosts to one instance, or name another way to choose one (for example an argument).

antra-tess and others added 2 commits October 25, 2026 10:42
…st statements

From review of revision 3:
- Revs are consecutive per connection, so a skipped number means a lost message.
- Hosts apply hint changes on arrival, in arrival order, whatever they decide about the
  message; a refused push is not restated and leaves no hole; the accept decision can't
  reorder anything.
- tools/list and toolsets/list are statements as of their place in the order; recovery needs
  no common baseline. toolsets/list states per-instance visibility in full.
- ended records carry an id, are reported until they expire, and are told once per id.
- A hint definition with an instance is that instance's own; hosts take only description,
  schema and annotations from it.
- An open without a name is not idempotent; policy closes are limited.
- The list-every-tool rule applies to hosts with toolHints; hosts without can choose among
  instances; implicit open excludes instance toolsets.
- Amendment list names §5.1/§5.2 and the MCP _meta members; vectors 21-26.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review

Recommendation: fix, then merge. 1 high severity finding should be fixed first.

Reviewed commit d8ba2a0 with claude-opus-5-5, by reading the code. It did not run the code or look at the checks.

Does this change belong in the project?

It fits the project. This draft RFC fits. It proposes optional, backward-compatible tool hints and toolsets for MCPL, which is the project's own kind of work, and it uses the same RFC-file format and amendment style as the other drafts at the repo root.

6 reasons with sources
  • A draft RFC filed at the root is how this project proposes protocol changes. The README says SPEC.md is the integrated text and that RFC files keep proposal and review history. Drafts RFC-006, -007, -008 and -013 already sit at the root as RFC-NNN-*.md. This PR adds RFC-014 the same way and does not touch SPEC.md. (README.md (Status))
  • Hosts and servers working together on how tools reach the model during inference is within MCPL's stated purpose. MCPL is described as an extension that makes servers active participants in the inference lifecycle, with host-controlled treatment of what servers send. The RFC adds server hints, host-controlled presentation and push-event routing. All of these build on existing features: push events, event tags, manifest changes and coalescing. (README.md (intro, Features))
  • It keeps to the project's rule that authority lives only in the grant. The RFC adds no uses path and changes no grant. It says hints confer nothing, which matches the SPEC's position that the grant, not announcements, is the protection. (SPEC.md §17.9; RFC-002 / §5.4)
  • It follows the pattern of the related tool drafts: optional, and backward-compatible with plain MCP. The RFC builds on RFC-008's mcpl/ _meta prefix and on RFC-006's coalescing, and it is advertisement-gated (toolHints). Hosts and servers that don't advertise it keep today's behaviour. (RFC-007-tool-lifecycle.md, RFC-008-tool-classes.md)
  • A maintainer should check one place where it departs from §17: it declares a new manifest domain with no change receipt. §17.5 requires one receipt for each manifest change, and §17.6 already maps tool changes to surface-changed. The RFC's amendment list names §17.1 but not §17.5–17.7. The departure is real, but it is the kind of thing review of a draft RFC settles; it is not a reason to reject the proposal. (SPEC.md §17.5 step 4, §17.6, §17.7)
  • §8.6 (surface hosts) depends on RFC-015, which is not in the repo, so that part brings in a concept still to be proposed. The RFC says it depends on nothing for authority, but surface hosts get their credentials through RFC-015. That RFC is mentioned in the PR but does not exist yet. Maintainers may want to treat §8.6 as provisional until RFC-015 is proposed. (README.md (RFC table); repository file list)

Findings

10 findings of medium or high severity, see the inline comments:

2 more findings, less severe
  • Low severity, documentation, RFC-014-surfaces.md:537-542. Security section skips other server-authored text that reaches the model. §10 deals with dynamic tool definitions entering the conversation. It does not cover other server-authored strings the host is told to show the agent: suggest[].reason, ended[].reason, toolset titles or retitles, and the content of open and close results. These are also injection surfaces and should carry the same attribution and trust-tier guidance.
  • Low severity, test coverage, RFC-014-surfaces.md:596-639. Conformance vectors do not cover the rev placement of toolsets/ results or hints on cancelled results.* The vectors test consecutive revs across results, pushes and tools/hints (vector 4). None checks the rev carried by toolsets/open, toolsets/close or toolsets/list results, or that hints on a cancelled or timed-out call result are applied (§6.1). These are the edge cases revision 4's rev rules are meant to settle, so they should be frozen as vectors.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +355 to +366
It travels three ways, sharing one `rev` counter:

1. **In a tool result**, `_meta["mcpl/hints"]` — for what that call caused.
2. **On a push event**, params member `hints` — for a change the event explains ("White
played E5", and `play` becomes visible).
3. **Standalone**, as `tools/hints` (Server → Host, Notification) — for a change with nothing
to say.

**Order.** A server **MUST** number the messages carrying hint changes on a connection
consecutively — the first is `rev` 1, each next one more — whichever of the three ways each
travels, and **MUST** write them in that order (allocate the `rev` and write the message in one
step).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High severity. Rev numbering covers three channels, but hints also travel in toolsets/open, close and list results.

§6.1 says hint changes travel "three ways" (tool result, push, tools/hints), and those three share the consecutive rev counter. But §5.2 (lines 255-258), §5.3 (line 269) and the schema (line 573) also put _meta["mcpl/hints"] in toolsets/open, toolsets/close and toolsets/list results. The RFC never says whether those results use up a new rev or, like tools/list, repeat the last one sent. The §5.3 example shows rev: 41 on a list without settling it. If a server repeats the last rev, a host following the "one more than the last" rule sees a non-consecutive rev and starts recovery, which issues another toolsets/list and can loop. If a server allocates a new rev and the host assumes it does not, the next push looks like a skip. The consecutive rev rule is the main thing revision 4 sets out to fix, so this gap undermines it.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +191 to +195
In the manifest (§17), so changes are announced by `mcpl/manifestChanged`. `toolHints`,
`toolsets` and `surface` form their own manifest domain, **`toolsets`** (amending §17.1's list
of domains). A change in it carries no grant consequence and no receipt work: a host re-reads
the declarations and refreshes its catalog, as it does for a `tools/list` change. (The
server-level `surface` member is unrelated to §17.6's `surface-changed` impact name.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. "No receipt work" for the new toolsets domain contradicts SPEC §17.5 and §17.7.

SPEC §17.5 step 4 says a host MUST emit one receipt per manifest change. §17.7 says a host SHOULD fold tool-list changes into the resident-facing changelog, and §17.6 already has a surface-changed impact for tool changes. RFC §5.1 says a change in the new toolsets domain carries "no receipt work", but its amendment list does not touch §17.5 or §17.6. Moving toolHints out of the capabilities domain (§17.1 places every member other than version, revision and featureSets there) also means withdrawing toolHints is no longer treated as a capability removal. The RFC should either amend those sections explicitly or map toolset changes to an existing impact such as surface-changed.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +191 to +194
In the manifest (§17), so changes are announced by `mcpl/manifestChanged`. `toolHints`,
`toolsets` and `surface` form their own manifest domain, **`toolsets`** (amending §17.1's list
of domains). A change in it carries no grant consequence and no receipt work: a host re-reads
the declarations and refreshes its catalog, as it does for a `tools/list` change. (The

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. No rule for open instances when a manifest change removes or alters their toolset declaration.

Toolset declarations can change through mcpl/manifestChanged, and the host "re-reads the declarations and refreshes its catalog". The RFC does not say what happens to instances already open when their toolset disappears from the manifest, or loses instances: true. The same gap applies when a declared toolset becomes undeclared: under §5.4 the host then becomes the one gating calls, and its open or closed state for the group is undefined. Hosts and servers will disagree on whether those instances are closed and whether the agent should be told.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +368 to +373
**Applying.** A host applies a message's hint changes **when it reads the message, in the order
messages arrive**, whatever it then does with the message: it applies the `hints` of a push it
refuses or has not yet decided on, and the `_meta["mcpl/hints"]` of a result it no longer waits
for (a cancelled or timed-out call). Accepting an event is a decision about the event, not about
the server's state; a server never restates anything because a push was refused. Entries state
values (`visible: true`, a definition), not differences.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Hints in the result of a cancelled call can be lost, and pushing them instead is forbidden.

The host is told to apply _meta["mcpl/hints"] from "a result it no longer waits for (a cancelled ... call)". Under MCP cancellation (notifications/cancelled), the receiver SHOULD NOT send a response to a cancelled request, so a conforming server may never send that result. Meanwhile §6.2 says the server MUST NOT push a call's own consequences. Visibility changes caused by a cancelled call then reach the host either never (if no rev was allocated, leaving its state silently stale) or only after a later rev shows a gap. The RFC should require those consequences to go out as tools/hints when no result will be sent.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +363 to +373
**Order.** A server **MUST** number the messages carrying hint changes on a connection
consecutively — the first is `rev` 1, each next one more — whichever of the three ways each
travels, and **MUST** write them in that order (allocate the `rev` and write the message in one
step).

**Applying.** A host applies a message's hint changes **when it reads the message, in the order
messages arrive**, whatever it then does with the message: it applies the `hints` of a push it
refuses or has not yet decided on, and the `_meta["mcpl/hints"]` of a result it no longer waits
for (a cancelled or timed-out call). Accepting an event is a decision about the event, not about
the server's state; a server never restates anything because a push was refused. Entries state
values (`visible: true`, a definition), not differences.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Retried push/event with hints conflicts with the rev and arrival-order rules.

SPEC §9.4 lets servers retry push/event with a stable eventId, and tells hosts to deduplicate by eventId. The RFC does not say what happens to hints on a retried push. If the retry carries the original rev, the host sees a non-consecutive rev. Applying "on arrival, in arrival order" would then re-apply stale values over newer state. If the host drops the retry as a duplicate, the rev check and the arrival-order rule disagree. If the retry gets a new rev, the stale values are stated again as current. A rule is needed, for example that a push carrying hints is never retried, or that a duplicate's hints are ignored.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +272 to +275
What the principal has open on the server, and the hint state of those instances as of the
result's `rev`, in full: every tool visible in each open instance — **a tool not stated visible
is invisible** — and every current definition that differs from the declared one. A host
replaces its per-instance state with it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. No recovery path for changed definitions of dynamic tools outside declared toolsets.

Recovery and reconnect (§6.1 lines 379-384, §5.3) depend on toolsets/list, which restates current definitions only for open instances of declared toolsets. tools/list carries only declared definitions. A dynamic tool with no toolset, or in an undeclared toolset (§5.4), can still get a definition in hints, for example new enum options. After a rev gap or a reconnect, nothing restates that definition, so the host keeps checking arguments and announcing the tool against a stale or declared schema until the next change. §5.3 also tells hosts to send toolsets/list only to servers that declared toolsets, which leaves hint-only servers with no recovery source at all.

Comment thread RFC-014-surfaces.md
Comment on lines +277 to +281
It also carries `ended`, so a host can tell "never had one" from "it ended while you were
away": toolsets that closed while no connection of the principal could receive an event about
it. Each record has an `id`, unique for the principal and never reused. A server reports a record
on every `toolsets/list` until it expires (a bounded time; the reference SDK: an hour); a host
tells its agent about each `id` once, however often it reconnects in that time:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. ended misses toolsets that ended while another connection of the principal was online.

ended records only toolsets that closed "while no connection of the principal could receive an event about it". Toolsets belong to the principal (§5.6), so take host B reconnecting after a game ended while host A, the same principal, was connected. B gets no ended record, and under §5.2 it quietly treats the instance as closed. B's agent is never told the game ended, which is the "ended while you were away" case this mechanism exists to cover. The phrase "could receive an event" is also undefined for a connection that has not granted pushEvents.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +408 to +410
- **Something else's consequences are pushed**, with the explaining event when there is one.
"Something else" includes another principal acting in a shared instance — an opponent's move
reaches you as a push carrying your visibility change.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Required push for hint changes has no fallback when pushEvents is not granted.

§6.2 says changes caused by something else "are pushed" with the explaining event, and line 399 says a host that has not granted pushEvents still tracks visibility. Without that grant the server cannot send push/event (SPEC §5.4 grants), and the RFC never tells the server to fall back to tools/hints. A server following §6.2 literally will either send a push that is not allowed or drop the change, and the host's visibility state goes stale.

Comment thread RFC-014-surfaces.md
Comment on lines +510 to +515
### 8.6 Surface hosts outside the main host

A host that cannot present dynamic tools (a plain MCP client) can gain the capability from a
**surface host**: an MCPL server that is itself a host for the servers behind it, exposes the
meta-tools as its own static tools, and renders dynamic tools as events. It connects to those
servers as the agent, so it obtains the agent's credentials from its own host through RFC-015.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity, documentation. Surface hosts depend on an RFC-015 that is not in the repo and get no security analysis.

§8.6 has a surface host, a third-party MCPL server, obtain the agent's credentials "from its own host through RFC-015", and §15 names RFC-015's access/token. RFC-015 is not in this repository, so the RFC rests on an unreviewed mechanism. The header also says "Depends on: nothing for authority", while this design hands agent credentials to another server. §10 does not mention credential delegation, so reviewers cannot judge the trust boundary being added.

Comment thread RFC-014-surfaces.md
Comment on lines +231 to +234
- `instance` is a name the host proposes (the agent's or its own), or omits to let the server
choose one. A proposed name **MUST** be used as given, or the open refused (for example an
invalid name); the server **MUST NOT** substitute another, so that a retried open finds what
the first one opened. For a toolset without `instances`, `instance` is absent.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Instance name syntax is undefined, yet servers may refuse "invalid" names.

Hosts are told to propose instance names (SHOULD, for safe retries), and servers may refuse an "invalid name", but the RFC sets no allowed characters or length. Instance names also go into projected, host-namespaced tool names (§8.4). A host cannot know which names a server accepts, so a well-formed retry strategy can be refused for reasons that differ by server. A minimal grammar is needed.

… calls, retries, no-push hosts; ended for all ends; name grammar

From review of revision 4:
- Results of toolsets/open|close|list carrying hints take the next rev; tools/list takes none.
- A cancelled call that gets no result, and a server that cannot push, send tools/hints.
- A push carrying hints is never retried; a duplicate eventId's hints are ignored.
- toolsets/list is answered by every toolHints server and states dynamic tools outside
  declared toolsets; ended records every end the host did not ask for.
- Manifest changes removing a toolset close its instances; the domain maps to surface-changed.
- Instance name grammar.
- Security: all server-authored text; surface hosts and RFC-015.
- Vectors 27-35.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review

Recommendation: fix, then merge. 1 high severity finding should be fixed first.

Reviewed commit 170b97d with claude-opus-5-5, by reading the code. It did not run the code or look at the checks.

Does this change belong in the project?

It fits the project. This draft RFC adds an optional, backward-compatible extension to MCPL, written in the same format as the project's other RFCs, so it fits. The gaps in the amendment list and the ordering problems belong to the code review, not to the question of fit.

6 reasons with sources
  • It serves MCPL's stated purpose of extending MCP in a backward-compatible way so servers can take an active part in inference. The README describes MCPL as a backward-compatible extension to MCP. The RFC is opt-in on both sides through toolHints. Hosts and servers that don't advertise it keep today's behaviour (RFC §11). (README.md (intro, Features))
  • It follows the project's process: a numbered draft RFC file at the root, separate from the authoritative SPEC.md. The README says SPEC.md is the authoritative integrated text and that RFC files keep the proposal and review history. RFC-006, RFC-007, RFC-008 and RFC-013 sit at the root as drafts in the same layout. RFC-014 leaves SPEC.md untouched and lists the SPEC sections it would amend, as RFC-007 does. (README.md (Status))
  • It builds on existing MCPL mechanisms instead of adding a second way to do the same things. Hints use RFC-008's mcpl/ _meta prefix. Events go through the existing push/event and the event tags of §16, and coalescing goes through RFC-006. Toolset declarations ride on the §17 manifest-change mechanism. It explicitly grants no new authority beyond RFC-002. (RFC-007-tool-lifecycle.md, RFC-008-tool-classes.md, RFC-006-event-coalescing.md)
  • It is one coherent proposal; its revision notes and implementation status record the history of a single design. Earlier RFCs such as RFC-007 also record revision notes and implementation feedback in the same way. (RFC-007-tool-lifecycle.md (revision notes))
  • Several review findings say amendments are missing (§17.3's domain list, §9.4's retry rule); these are gaps in the draft to fix, not reasons it doesn't belong. The RFC already says it amends §17.1 and §9.2. Whether its amendment list is complete is a matter for the drafting review before the RFC is accepted. (SPEC.md (§9.4, §17))
  • Surface hosts (RFC §8.6) rely on RFC-015, which is not in the repository yet. Only that optional section depends on the companion PR. A maintainer may want to land the two together, but it does not change whether this RFC fits. (README.md (Status table))

Findings

9 findings of medium or high severity, see the inline comments:

6 more findings, less severe
  • Low severity, documentation, RFC-014-surfaces.md:179-183. 'Every toolset without instances is implicitly open' is contradicted in the next sentence. The rule says every toolset without instances is implicitly open for a host without toolHints. It then exempts toolsets "that cannot sensibly be open without options", which may also have no instances. The normative statement and its exception conflict, and nothing in the manifest lets a host or SDK tell which toolsets fall under the exception. The rule should be reworded, for example to use default or a declared flag.
  • Low severity, documentation, RFC-014-surfaces.md:626. Behaviour for default: "open" combined with instances: true is unspecified. ToolsetDecl allows any default with instances: true. The text says such a toolset "is always closed" but does not say whether declaring "open" is invalid, is ignored, or creates an implicit instance. Hosts and servers may disagree about whether an instance exists at session start.
  • Low severity, documentation, RFC-014-surfaces.md:307-309. ended example omits instance for an instanced toolset. The example ended record for toolset game, which §5.1 declares with instances: true, has no instance. A host with two open games cannot tell which one ended. The example should include instance, and the text should say it is required for instanced toolsets.
  • Low severity, documentation, RFC-014-surfaces.md:532-534. Projection requires mcpl/instance set even for toolsets without instances. RFC §8.4 says a host MUST route each projected call "with mcpl/instance set". RFC §5.2 says that for a toolset without instances, instance is absent. Read literally, a host must send an instance name that does not exist for non-instanced and undeclared toolsets. The requirement should apply only to instanced toolsets.
  • Low severity, test coverage, RFC-014-surfaces.md:433-434. No vector for hosts ignoring hints from servers that did not advertise toolHints. §6.1 says a host MUST ignore hint _meta and tools/hints from a server without toolHints, and §5.1 says a server that withdraws toolHints becomes hint-less. No vector in §13 covers either rule. Nor does any vector cover the mcpl/rev value a tools/list result carries before any rev has been taken, which the RFC also leaves unstated.
  • Low severity, documentation, RFC-014-surfaces.md:402-404. Unclear whether a duplicate-eventId push's rev advances the host's counter. A host ignores the hints of a push whose eventId it has already seen, but the RFC does not say whether that message's rev counts toward the consecutive sequence. If the host does not advance its counter, the next message looks like a gap and triggers an unnecessary recovery. If it does, its ignored changes are silently lost.

Comment thread RFC-014-surfaces.md Outdated
Comment on lines +389 to +416
**Order.** Every message that carries hint changes takes the next `rev`; a message without
any takes none. A server **MUST** number them consecutively on a connection — the first is
`rev` 1, each next one more — whichever way each travels, results of every kind included, and
**MUST** write them in that order (allocate the `rev` and write the message in one step). A
`tools/list` result carries no changes; its `mcpl/rev` names the last `rev` taken.

**Applying.** A host applies a message's hint changes **when it reads the message, in the order
messages arrive**, whatever it then does with the message: it applies the `hints` of a push it
refuses or has not yet decided on, and the `_meta["mcpl/hints"]` of a result it no longer waits
for (a timed-out call). Accepting an event is a decision about the event, not about the
server's state; a server never restates anything because a push was refused. Entries state
values (`visible: true`, a definition), not differences.

A push carrying hints is **never retried** (§9.4): its `rev` is spent when it is written. A host
that receives one whose `eventId` it has already seen ignores its hints. A call that will get no
result — cancelled under MCP, which then sends none — sends the changes it caused as
`tools/hints` instead.

A `tools/list` result states presence and declared definitions as of the `rev` it carries, and
a `toolsets/list` result states per-instance state as of its own `rev`; each applies at its
place in the arrival order.

**Recovering.** On an ordered connection nothing is lost. A host that nevertheless sees a `rev`
other than one more than the last — it dropped a message unread, or a bug — re-reads
`toolsets/list` and `tools/list` and goes on applying in arrival order. Each result states the
server's state as of the moment it was written, and everything applies in the order it was
written, so the two reads need no common baseline. A new connection starts from
`toolsets/list` (RFC §5.3).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High severity. Consecutive-rev, arrival-order rule assumes a totally ordered connection that MCP's Streamable HTTP does not provide.

RFC §6.1 requires one consecutive rev counter across tool results, toolsets/* results, pushes and tools/hints. Hosts must apply them "in the order messages arrive" and treat any rev other than last+1 as loss. SPEC §4.2 names MCP's stdio and Streamable HTTP as transports alongside WebSocket. Under Streamable HTTP, concurrent requests can be answered on separate HTTP responses or SSE streams, and server notifications travel on a separate GET stream, so nothing orders them relative to each other. A host can therefore receive rev 6 before rev 5. That is wrongly treated as a gap and forces a re-read. Worse, rev 5 is then applied after the newer state, which leaves visibility stale ('play' shown as invisible on your turn). The RFC should either limit rev ordering to ordered transports, or tell hosts to discard hint messages whose rev is not higher than the last one applied and to compare mcpl/rev on list results, instead of applying strictly in arrival order.

Comment thread RFC-014-surfaces.md
Comment on lines +293 to +298
What the principal has open on the server, and the hint state of its dynamic tools as of the
result's `rev`, in full: every tool visible in each open instance, and every visible dynamic
tool outside declared toolsets — **a dynamic tool not stated visible is invisible** — with every
current definition that differs from the declared one. A host replaces its dynamic-tool state
with it. Every server that advertised `toolHints` answers `toolsets/list`, with an empty `open`
if it declares no toolsets.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. An empty full statement in toolsets/list may carry no hints and so never clears stale visibility.

RFC §5.3 says the toolsets/list hints are a full statement that the host replaces its dynamic-tool state with. RFC §6.1 (line 389) says "a message without any [hint changes] takes none" (no rev), and the schema makes _meta["mcpl/hints"] optional (line 613-614, 620). When the principal has nothing visible, for example after every instance has ended, a server can reasonably omit mcpl/hints. The host then has no statement to replace its state with, and no rev to place it in the order, so tools it believed visible stay visible. The RFC should require toolsets/list to always carry mcpl/hints with a rev, even when tools is empty.

Comment thread RFC-014-surfaces.md
Comment on lines +402 to +405
A push carrying hints is **never retried** (§9.4): its `rev` is spent when it is written. A host
that receives one whose `eventId` it has already seen ignores its hints. A call that will get no
result — cancelled under MCP, which then sends none — sends the changes it caused as
`tools/hints` instead.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. 'Never retried (§9.4)' contradicts SPEC §9.4, and §9.4 is not in the amendment list.

SPEC §9.4 says hosts SHOULD deduplicate by eventId and servers SHOULD use stable eventId values for retries, which assumes pushes may be retried. RFC §6.1 forbids retrying any push that carries hints and cites §9.4 as if §9.4 said so. The amendment list in the header (lines 14-19) does not include §9.4. The rule also means that an explaining event whose delivery fails, for example a lost response or a timeout, is simply lost, which weakens push delivery for exactly the toolset events (an opponent's move) this RFC is about. The amendment should be stated explicitly. A cleaner fix is to let the push's content be retried without its hints.

Comment thread RFC-014-surfaces.md
Comment on lines +203 to +208
In the manifest (§17), so changes are announced by `mcpl/manifestChanged`. `toolHints`,
`toolsets` and `surface` form their own manifest domain, **`toolsets`** (amending §17.1's list
of domains). A change in it carries no grant consequence: its impact is §17.6's
`surface-changed`, disposition `applied`; the host emits its receipt as for any change (§17.5),
re-reads the declarations and refreshes its catalog. (The server-level `surface` member is
unrelated to the impact's name.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. New toolsets manifest domain leaves SPEC §17.3's closed domain list and existing domain derivation unamended.

SPEC §17.1 defines the capabilities domain as every member other than version, revision and featureSets. SPEC §17.3 says domains is a subset of capabilities | featureSets | tagOntology. RFC §5.1 moves toolHints, toolsets and surface into a new toolsets domain but lists only §17.1 and §17.6 as amended. A conforming host or library that does not know this RFC classifies these members as capabilities and may treat their removal as a capability-revoked impact. A host that does know it rejects or ignores a toolsets domain value that §17.3 does not allow. The two library implementations already pinned for domain derivation (§17.3) will diverge. The RFC should amend §17.3 and state how hosts that predate it classify these members.

Comment thread RFC-014-surfaces.md
Comment on lines +222 to +234
"game": { "title": "Go", "summary": "A game of Go against the bot or another agent",
"default": "closed", "instances": true,
"options": { "type": "object", "properties": { "size": { "enum": [9, 13, 19] } } } }
},
"surface": { "title": "Arcade", "summary": "Games" } } }
```

| Field | Meaning |
|---|---|
| `title`, `summary` | For the host's catalog and reminders |
| `default` | `"open"`: the server treats the toolset as open from the start of a session — it appears in `toolsets/list` without the host opening it. `"closed"`: the host opens it. A toolset with `instances` is always `"closed"` |
| `instances` | `true`: the toolset may be opened more than once, each a named instance |
| `options` | JSON Schema for `toolsets/open` options |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Toolset names and the options JSON Schema in the manifest can trigger §17.2's identifier_charset refusal.

SPEC §17.2 makes "capability member names at every depth" identifier positions that must match ASCII [A-Za-z0-9._:*-], and refuses the digest (identifier_charset) otherwise. RFC §5.1 puts free-form toolset names and a full JSON Schema (options) inside experimental.mcpl. Ordinary JSON Schema keys such as $schema, $ref and $defs, and non-ASCII property or toolset names, fall outside that charset. Such a manifest cannot get a revision and breaks §17 manifest-change handling. The RFC gives toolset names no grammar and does not amend §17.2 to exempt the toolsets subtree, or the options schema in particular.

Comment thread RFC-014-surfaces.md
Comment on lines +339 to +345
### 5.6 One principal, several connections

Toolsets belong to the principal, not to a connection: a second connection of the same
principal sees the same open toolsets (through `toolsets/list`, then hint changes). An open,
close or visibility change made through one connection reaches the others as ordinary hint
changes on their own `rev` counters — including `toolsets` state changes — not as
suggestions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Cross-connection sharing is keyed on a 'principal' that neither the SPEC nor the RFC defines.

RFC §5.6 and §5.2 make open toolsets, closes (which forfeit games) and ended records shared by every connection of the same "principal". SPEC §18.8 explicitly lists identity and principals as out of scope, and §4.2.6 leaves authentication endpoint-defined; deployments commonly use a shared token in the URL. If a server derives the principal from such a credential, distinct agents or hosts sharing it can see each other's open instances, titles and ended reasons through toolsets/list. They can also close each other's games, which they should not be able to do. The RFC needs to say how a server determines the principal, or at least require that sharing happens only under an authenticated identity the operator configured for it, and list this in §10.

Comment thread RFC-014-surfaces.md
Comment on lines +311 to +313
A server that keeps toolsets across connections reports them after a reconnect; one that does
not reports what was opened on this connection. Hosts send it after every (re)connect to a
server that advertised `toolHints`, and tell the agent about each `ended` record once.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity, documentation. Allowing per-connection toolset state contradicts the principal-scoped model the rest of the RFC requires.

Lines 311-313 allow a server that "does not" keep toolsets across connections to report only what was opened on this connection. RFC §5.6 states that toolsets belong to the principal, not to a connection. §5.2 says a close takes effect for every connection, and vectors 14 and 32 require cross-connection propagation. Implementers cannot tell whether principal scoping is a requirement or optional. A host that follows §5.2's policy-close guidance, which assumes closes affect every connection, behaves differently on the two kinds of server.

Comment thread RFC-014-surfaces.md
Comment on lines +504 to +514
### 8.2 Meta-tools

A host presenting dynamic tools exposes a small, constant set of its own, for example:

```
open {toolset | server, instance?, options?} look {toolset?, instance?, tool?}
act {tool, instance?, args} close {toolset | server, instance?}
```

They are the host's interface, aggregated across every connection and named by the host.
They are static.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity. Example meta-tools are aggregated across connections but act and look cannot name the server.

RFC §8.2 says the meta-tools are "aggregated across every connection". However, act {tool, instance?, args} and look {toolset?, instance?, tool?} take no server, and tool and toolset names are unique only within a server (RFC §4.1). Two connected servers can both have a game toolset with a play tool (games-mcpl and a Go server, §9). With the example signatures, act is then ambiguous or routed to the wrong server. Instance names are not unique across servers either. The example should carry a server parameter, or the RFC should require the host to namespace toolset and tool names.

Comment thread RFC-014-surfaces.md
Comment on lines +179 to +183
- **For a host that did not advertise `toolHints`, every toolset without instances is
implicitly open** and the server behaves as it would without this RFC. A toolset with
instances has no implicit instance; nor does one that cannot sensibly be open without options
(a game needs its settings). For those the server offers that host plain tools to open,
choose and close instances (RFC §11), or leaves them out.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium severity, test coverage. No conformance vector for the behaviour served to hosts without toolHints.

RFC §4.1 and §6.3 define what a server does for hosts that did not advertise toolHints: non-instanced toolsets implicitly open, the list may be tailored, and plain open, close and choose tools are offered. Among §13's vectors only 8 and 26 touch this. None checks that a non-instanced toolset's tools work with no open, or that a tailored list omits hint _meta. This is the compatibility promise in the PR description ("see today's behaviour") and it needs a vector.

antra-tess and others added 3 commits October 25, 2026 10:59
…principal, names

From review of revision 5:
- Hosts apply hints in rev order; on Streamable HTTP an early change is held briefly.
- toolsets/list always carries its hints and a rev, so an empty statement clears state.
- Principal takes RFC-015 §4.4's definition; servers that don't keep toolsets end them.
- Toolset name grammar; act/look can name the server.
- Amendment list names §9.4 and §17.3. Vectors 36-38.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… are known

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@antra-tess
antra-tess merged commit 7749193 into main Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant