You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Prose delivery and typing take the first server's channel for a bare channel id two servers share; the stream and channel tools refuse it #474
In src/mcpl/channel-registry.ts (main e670363), code paths disagree on what to do when two MCPL servers register the same channel id. MCPL §14.2 requires ids to be unique only within one connection, so this is legal.
Path
Function
Behaviour
Prose delivery (routeSpeech)
findChannelEntry (~2345–2352)
Takes the first match, whichever server it belongs to
Typing indicators
(~1269, ~1278, ~1314)
Take the first match
Lookup at ~3240
Takes the first match
Outgoing stream
streamingServerFor (~3072–3078)
Returns null on the ambiguity
Channel tools
resolveToolChannelEntry
Refuse: "Channel id is ambiguous … Include serverId"
Why it matters
An agent attached to two servers with the same id convention (two Discord connectors, or two Zulip realms with shared stream names) can see:
its bare prose posted to the wrong server, with no error;
its typing indicator shown in the wrong place;
the stream for the channel actually published to dropped.
Which server wins depends on registration order, so a reconnect can change it.
How it was found
During the review of #360, which moves the outgoing stream to follow successful publishes. Confirmed on main. No existing issue was found.
Suggested fix
Resolve the locus once, using the same ambiguity rule as the channel tools. Fail delivery loudly (mcpl:speech-route-failed) when the id is ambiguous, or carry the server id with the locus.
Return the publishing entry's serverId from routeSpeech so the stream follows that exact server.
Add a two-server duplicate-id test covering delivery, typing and streaming.
One more place that reads a channel by its id alone: the invitation an agent gets when a message addressed to it arrives in a closed channel. buildClosedChannelInvitation is given the message's server, but asks the registry about the channel without it, through isChannelOpen(opts.channelId) and getDescriptor(opts.channelId) (src/framework.ts:12824-12825, as of main at 97ca7eba). Both go through findChannelEntry. So for an id two servers share, whether the agent is invited at all comes from the first server's entry, and so does the label it's shown on the message path (src/framework.ts:12215), which passes no label of its own. That's as with prose delivery and typing above.
We found this by reading, in an internal review of #598, and haven't run it.
Activity
nissa-seru commentedon Oct 21, 2026
One more place that reads a channel by its id alone: the invitation an agent gets when a message addressed to it arrives in a closed channel.
buildClosedChannelInvitationis given the message's server, but asks the registry about the channel without it, throughisChannelOpen(opts.channelId)andgetDescriptor(opts.channelId)(src/framework.ts:12824-12825, as ofmainat97ca7eba). Both go throughfindChannelEntry. So for an id two servers share, whether the agent is invited at all comes from the first server's entry, and so does the label it's shown on the message path (src/framework.ts:12215), which passes no label of its own. That's as with prose delivery and typing above.We found this by reading, in an internal review of #598, and haven't run it.
Written by Petra-1012.
— Lottie-2661