Version: v0.4
Status: Released
Release Date: 2025-09-26
Intended Status: Experimental
Editors: RJ Corwin (https://github.com/rjcorwin)
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY are to be interpreted as described in RFC 2119 and RFC 8174.
MEW Protocol (Multi-Entity Workspace Protocol) provides a unified context where all agent-to-agent and agent-to-tool interactions are visible and controllable at the protocol layer. The name playfully evokes "herding cats" - the quintessential challenge of coordinating multiple independent, autonomous agents who each have their own goals and behaviors. Unlike traditional systems where AI coordination happens in hidden contexts, MEW Protocol broadcasts all messages within a shared workspace, enabling participants to observe everything and privileged users to approve or reject actions through a proposal-review pattern. The protocol extends MCP semantics while adding capability-based access control, allowing untrusted agents to propose operations that trusted participants execute. This transparency enables progressive automation, where systems learn from human decisions to safely expand their autonomous capabilities over time - essentially teaching the "cats" to work together effectively.
- Unified Context: All participants share the same visible stream of interactions
- Protocol-Level Control: Control actions through capabilities and proposals, not application wrappers
- Progressive Automation: Learn from human decisions to safely expand autonomous operations
- Tool Compatibility: Preserve existing MCP tool and resource interfaces
- End-to-end encryption or private channels (all messages visible within space)
- Message persistence or replay (spaces are ephemeral)
- Cross-space routing or participant discovery
- Guaranteed delivery or ordering (WebSocket best-effort)
- Application-specific semantics beyond MCP operations
- Configuration management (implementation-specific)
All data flowing over a space MUST be a single top-level JSON envelope:
{
"protocol": "mew/v0.4",
"id": "2f6b6e70-7b36-4b8b-9ad8-7c4f7d0e2d0b",
"ts": "2025-08-26T14:00:00Z",
"from": "robot-alpha",
"to": ["coordinator"],
"kind": "mcp/request",
"correlation_id": ["env-1"],
"context": "workflow-123/step-1",
"payload": { /* kind-specific body */ }
}Field semantics:
protocol: MUST be"mew/v0.4"for this versionid: globally unique message id (e.g., UUIDv4)ts: RFC3339 timestampfrom: stable participant id within the spaceto: array of participant ids (empty/omitted = broadcast)kind: Message type using minimal kinds:mcp/request- MCP request/notificationmcp/response- MCP responsemcp/proposal- MCP proposal for operationsmcp/withdraw- Withdraw a proposalmcp/reject- Reject a proposalreasoning/start- Begin reasoning sequencereasoning/thought- Reasoning monologue (thinking out loud)reasoning/conclusion- End reasoning sequencereasoning/cancel- Abort an in-progress reasoning sequencecapability/grant- Grant capabilities to participantcapability/revoke- Revoke capabilities from participantcapability/grant-ack- Acknowledge capability grantspace/invite- Invite participant to spacespace/invite-ack- Gateway response to invite with credentialsspace/kick- Remove participant from spaceparticipant/pause- Pause outbound activity from a participantparticipant/resume- Resume a participant after a pauseparticipant/status- Report usage status (e.g., tokens consumed vs. maximum) including current message counts per contextparticipant/request-status- Ask a participant to emit its current statusparticipant/forget- Instruct a participant to drop cached context (oldest or newest data)participant/compact- Request that a participant compact its working memory to free headroomparticipant/compact-done- Participant acknowledgement that compaction is completeparticipant/clear- Wipe the participant's entire conversational context or scratchpadparticipant/restart- Restart a participant back to a known baseline stateparticipant/shutdown- Request an orderly shutdown that ends activity without restartingstream/request- Ask the gateway to establish a new data streamstream/open- Gateway confirms stream creation with assigned identifierstream/close- Close a previously opened streamstream/grant-write[s2w] - Grant write access to another participantstream/revoke-write[s2w] - Revoke write access from a participantstream/transfer-ownership[s2w] - Transfer stream ownership to another participantstream/write-granted[s2w] - Acknowledgement that write access was grantedstream/write-revoked[s2w] - Notification that write access was revokedstream/ownership-transferred[s2w] - Notification that ownership was transferredchat- Chat messageschat/acknowledge- Confirm receipt of a chat messagechat/cancel- Withdraw attention from a chat message that no longer needs actionsystem/presence- Join/leave eventssystem/welcome- Welcome message for new participantssystem/error- System error messages
correlation_id: array of message IDs this message relates to (always an array, even for single values)- Response → Request:
mcp/responseMUST reference the originatingmcp/request - Fulfillment → Proposal:
mcp/requestMAY reference amcp/proposalwhen fulfilling it - Withdraw → Proposal:
mcp/withdrawMUST reference the proposal being withdrawn - Reject → Proposal:
mcp/rejectMUST reference the proposal being rejected - Error → Trigger:
system/errormessages SHOULD reference the envelope that caused the error - Reply → Message: Any envelope MAY reference others to indicate explicit reply intent (use
contextfield for threading) - Workflow → Multiple: Messages in complex workflows MAY reference multiple related messages (e.g., a summary referencing several analyses, a decision based on multiple proposals, a response addressing multiple questions)
- Fork/Join → Multiple: A response that combines results from multiple parallel requests
- The array structure supports complex multi-party coordination and DAG-like message flows
- Response → Request:
context: (optional) string representing the context or sub-context for this message- Path-based hierarchy using forward slashes:
"parent/child/grandchild" - Messages in sub-contexts (reasoning, workflows) include this field for organization
- Context groups related messages together (e.g., all thoughts in a reasoning sequence)
- When prompting LLMs, implementations MAY filter contexts to reduce noise
- IMPORTANT: Messages addressed to a participant (via
tofield) MUST be handled regardless of context
- Path-based hierarchy using forward slashes:
payload: object; structure depends onkind
MCP messages use minimal kinds with method information in the payload:
kind: "mcp/request"for requests/notificationskind: "mcp/response"for responseskind: "mcp/proposal"for proposalskind: "mcp/withdraw"for withdrawing proposalskind: "mcp/reject"for rejecting proposals- The specific method (e.g.,
tools/call,resources/read) is inpayload.method
Example tool call request:
{
"protocol": "mew/v0.4",
"id": "env-call-1",
"from": "trusted-agent",
"to": ["target-agent"],
"kind": "mcp/request",
"payload": {
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "dangerous_operation",
"arguments": {"target": "production"}
}
}
}Responses MUST include the correlation_id field referencing the original request's id. This is critical for translating MCP's client-server request/response model to MEW Protocol's multi-participant broadcast paradigm, allowing all participants to match responses to their corresponding requests.
Response to the above tool call:
{
"protocol": "mew/v0.4",
"id": "env-resp-1",
"from": "target-agent",
"to": ["trusted-agent"],
"kind": "mcp/response",
"correlation_id": ["env-call-1"],
"payload": {
"jsonrpc": "2.0",
"id": 42,
"result": {
"content": [{
"type": "text",
"text": "Operation completed"
}]
}
}
}Note: The MCP payload structure remains unchanged from standard MCP.
Participants with restricted capabilities MUST use proposals instead of direct MCP operations. Proposals have the same payload structure as requests:
{
"protocol": "mew/v0.4",
"id": "env-req-1",
"from": "untrusted-agent",
"to": ["target-agent"],
"kind": "mcp/proposal",
"payload": {
"method": "tools/call",
"params": {
"name": "dangerous_operation",
"arguments": {"target": "production"}
}
}
}This is a MEW Protocol-specific extension that proposes an MCP operation requiring fulfillment by a participant with appropriate capabilities.
A participant with appropriate capabilities fulfills the proposal by making the real MCP call. The correlation_id field is critical for matching approvals to their original proposals:
- Proposals typically don't include
correlation_id(they originate new workflows) - Fulfillment messages MUST include
correlation_idreferencing the proposal'sid - This allows participants to track which fulfillment corresponds to which proposal
- The correlation chain enables audit trails and multi-step approval workflows
Example fulfillment:
{
"protocol": "mew/v0.4",
"id": "env-fulfill-1",
"from": "human-user",
"to": ["target-agent"],
"kind": "mcp/request",
"correlation_id": ["env-req-1"],
"payload": {
"jsonrpc": "2.0",
"id": 44,
"method": "tools/call",
"params": {
"name": "dangerous_operation",
"arguments": {"target": "production"}
}
}
}The response goes only to the fulfiller who made the request. The response's correlation_id references the fulfillment message (env-fulfill-1), maintaining the standard request/response pairing. The original proposer can observe the outcome since messages are broadcast by default:
{
"protocol": "mew/v0.4",
"id": "env-fulfill-resp-1",
"from": "target-agent",
"to": ["human-user"],
"kind": "mcp/response",
"correlation_id": ["env-fulfill-1"],
"payload": {
"jsonrpc": "2.0",
"id": 44,
"result": {
"content": [{
"type": "text",
"text": "Operation completed successfully"
}]
}
}
}The proposer can follow the correlation chain to track the outcome:
- Proposer sends proposal (
env-req-1) - Fulfiller sends request with
correlation_id: ["env-req-1"](env-fulfill-1) - Target sends response with
correlation_id: ["env-fulfill-1"](env-fulfill-resp-1) - Proposer observes the broadcast response and can trace back through the correlation chain
MEW Protocol extends the proposal pattern with lifecycle operations for cancellation and feedback.
Proposers can withdraw their own proposals that are no longer needed:
{
"protocol": "mew/v0.4",
"id": "withdraw-456",
"from": "untrusted-agent",
"correlation_id": ["env-req-1"],
"kind": "mcp/withdraw",
"payload": {
"reason": "no_longer_needed"
}
}- Only the original proposer can withdraw their proposals
correlation_idMUST reference the proposal being withdrawnreasonfield uses standardized codes (see below)
Any participant with the capability can reject proposals. Rejections provide early feedback to avoid unnecessary waiting:
{
"protocol": "mew/v0.4",
"id": "reject-789",
"from": "filesystem-agent",
"to": ["untrusted-agent"],
"correlation_id": ["env-req-1"],
"kind": "mcp/reject",
"payload": {
"reason": "unsafe"
}
}Purpose and Behavior:
- Prevents proposers from waiting unnecessarily for fulfillment that won't come
- Particularly important when explicitly targeted in a proposal's
tofield - Proposers MAY give up after the first rejection (fail-fast approach)
- Proposers MAY continue waiting for other participants to fulfill (resilient approach)
- The choice depends on the proposer's implementation and use case
- Multiple participants may reject the same proposal for different reasons
Requirements:
correlation_idMUST reference the proposal being rejectedreasonfield uses standardized codes (see below)
Lifecycle operations may use the following suggested reason codes (optional and non-normative):
"disagree"- Don't agree with the proposed action"inappropriate"- Not suitable for the current context"unsafe"- Could cause harm or unintended consequences"busy"- Currently handling other work"incapable"- Don't have the required tools/access"policy"- Violates security or operational policy"duplicate"- Already being handled by another participant"invalid"- Proposal is malformed or impossible"timeout"- Too old or expired"resource_limit"- Would exceed resource constraints"no_longer_needed"- Proposer no longer needs the operation"other"- Reason not covered above
These reason codes are suggestions only; participants MAY use other reason strings and MUST handle unknown codes gracefully.
Detailed explanations can be provided via follow-up chat messages using correlation_id:
{
"protocol": "mew/v0.4",
"id": "chat-790",
"from": "filesystem-agent",
"to": ["untrusted-agent"],
"correlation_id": ["reject-789"],
"kind": "chat",
"payload": {
"text": "I'm currently processing a large batch operation that requires exclusive file access. Please retry in about 5 minutes.",
"format": "plain"
}
}Important timing consideration: The reject message with a reason code should be sent immediately for fast feedback. Detailed explanations may arrive later as generating them (especially with LLMs) can take time. Participants implementing proposers should:
- Process the reject immediately upon receipt
- Optionally wait for follow-up explanations if they need details
- Not block on explanations that may never come
Chat messages are MEW Protocol-specific and do not pollute the MCP namespace:
{
"protocol": "mew/v0.4",
"id": "env-chat-1",
"from": "user-alice",
"kind": "chat",
"payload": {
"text": "Hello everyone!",
"format": "plain"
}
}text(required): The message contentformat(optional): "plain" or "markdown", defaults to "plain"- Chat messages are typically broadcast (empty/omitted
toarray)
Acknowledges receipt of a chat message without generating additional conversation content. Useful when participants need to signal that they observed a chat entry but have no substantive reply:
{
"protocol": "mew/v0.4",
"id": "chat-ack-1",
"from": "agent-1",
"kind": "chat/acknowledge",
"correlation_id": ["env-chat-1"],
"payload": {
"status": "received"
}
}correlation_idMUST reference the chat envelope being acknowledgedpayload.statusconveys acknowledgement semantics (implementations MAY standardize values like"received"or"read")- Gateways MAY suppress broadcasting acknowledgements to reduce noise, but if delivered they provide an auditable confirmation that the message was seen
- User interfaces often surface a queue of chat messages awaiting acknowledgement so that humans and agents can close the loop explicitly. Clearing that queue SHOULD emit acknowledgements for each entry to provide an auditable trail.
Signals that an earlier chat message no longer requires attention. This helps UIs and agents clear acknowledgement queues and avoid stale follow-ups:
{
"protocol": "mew/v0.4",
"id": "chat-cancel-1",
"from": "user-alice",
"kind": "chat/cancel",
"to": ["agent-1"],
"correlation_id": ["env-chat-1"],
"payload": {
"reason": "handled elsewhere"
}
}correlation_idMUST reference the chat message that is being cancelledpayload.reasonSHOULD communicate why the message no longer needs attention (e.g.,"handled_elsewhere","duplicate","timeout")- UIs MAY provide a shortcut to cancel all unacknowledged chat messages, emitting one cancellation per message to keep participants in sync
MEW Protocol supports transparent reasoning through dedicated message kinds and the context field. This allows agents to share their thinking process while maintaining clear boundaries between reasoning sequences and regular operations.
Common patterns for transparent reasoning:
Response Pattern:
- Agent receives a request (e.g.,
mcp/request) - Agent sends
reasoning/startwithcorrelation_idreferencing the request - The
reasoning/startmessage ID becomes thecontextfor subsequent messages - Agent MAY send
reasoning/thoughtmessages for internal monologue (optional) - Agent MAY use
chatmessages within context for dialogue with other participants - Agent MAY send
reasoning/conclusionto summarize findings (optional) - Agent sends final response with
correlation_idto original request
Autonomous Pattern:
- Agent sends
reasoning/startat any time (e.g., periodic analysis, monitoring) - The
reasoning/startcreates a context for transparent thinking - Agent shares thoughts as they occur
- No response required - purely for transparency
Note: reasoning/start creates a context for transparency. Whether responding to requests or reasoning autonomously, it lets other participants observe the agent's thinking process.
Signals the beginning of a reasoning sequence:
{
"protocol": "mew/v0.4",
"id": "reason-start-1",
"from": "agent-1",
"kind": "reasoning/start",
"correlation_id": ["req-123"],
"payload": {
"message": "Analyzing the request to modify the configuration file..."
}
}Shares internal reasoning steps (monologue, no reply expected):
{
"protocol": "mew/v0.4",
"id": "thought-1",
"from": "agent-1",
"kind": "reasoning/thought",
"context": "reason-start-1",
"payload": {
"message": "The configuration file appears to use YAML format. I should validate the syntax before making changes."
}
}Concludes the reasoning sequence:
{
"protocol": "mew/v0.4",
"id": "reason-end-1",
"from": "agent-1",
"kind": "reasoning/conclusion",
"context": "reason-start-1",
"payload": {
"message": "Configuration update is safe to proceed. The changes are valid YAML and won't break existing settings."
}
}Terminates an active reasoning sequence without providing a conclusion. Useful when the initiating trigger is withdrawn or the participant needs to abort due to higher-priority work:
{
"protocol": "mew/v0.4",
"id": "reason-cancel-1",
"from": "agent-1",
"kind": "reasoning/cancel",
"context": "reason-start-1",
"payload": {
"reason": "superseded"
}
}contextMUST reference thereasoning/startenvelope establishing the sequencecorrelation_idMAY reference the triggering request/proposal if the reasoning was in response to another messagepayload.reasonSHOULD use deployment-defined codes (e.g.,"timeout","superseded","error") to simplify monitoring- Upon cancellation, participants SHOULD stop sending additional
reasoning/thoughtmessages for that context - User-facing surfaces (CLIs, dashboards) SHOULD provide an explicit affordance to issue
reasoning/cancelfor any active reasoning sequence so humans can interrupt long-running work without crafting raw envelopes
- Messages with a
contextfield are part of a sub-context - Participants MAY filter out contexts when constructing prompts (e.g., excluding other agents' reasoning traces to reduce noise)
- Exception: Participants MUST NOT ignore
mcp/requestormcp/proposalmessages, even if they have a context field - The context field supports hierarchical paths:
"parent/child/grandchild" - Context enables parallel reasoning by multiple agents without interference
A space in MEW Protocol is a communication context where participants exchange messages. It's analogous to a channel or room - a bounded environment where a specific set of participants collaborate. Each space has its own:
- Set of participants (who can send/receive messages)
- Capability assignments (what each participant can do)
- Message history (the stream of envelopes exchanged)
A gateway may manage multiple spaces, each isolated from the others. The gateway enforces capabilities and routing rules for all its spaces.
MEW Protocol extends with operations for managing these spaces dynamically. These operations use the capability/* and space/* namespaces to clearly indicate their purpose.
Allows participants with appropriate capabilities to grant capabilities to other participants:
{
"protocol": "mew/v0.4",
"id": "grant-789",
"from": "trusted-orchestrator",
"to": ["untrusted-agent"],
"kind": "capability/grant",
"payload": {
"recipient": "untrusted-agent",
"capabilities": [
{
"kind": "mcp/request",
"payload": {
"method": "tools/call",
"params": {
"name": "read_file"
}
}
}
],
"reason": "Demonstrated safe file handling"
}
}- The grant message ID becomes the
grant_idfor future revocation - Capabilities use the same JSON pattern matching format from Section 4.1
- Gateway SHOULD add granted capabilities to recipient's capability set
- Multiple grants are cumulative
Capability Update Flow:
When the gateway processes a capability/grant:
- Validate: Check sender has
capability/grantpermission - Store: Add granted capabilities to recipient's runtime capability set
- Notify: Send updated
system/welcometo recipient with combined static + runtime capabilities - Acknowledge: Recipient sends
capability/grant-ackafter updating capabilities
Important: The gateway MUST resolve logical participant names to actual participant IDs when sending the updated welcome. If the recipient's participant ID differs from their logical name (e.g., "mew" vs "client-123"), the gateway must maintain a mapping (see Section 3.6.4).
Example Flow:
1. Human → Gateway: capability/grant (to: ["mew"])
2. Gateway: Validates, stores capabilities for "mew"
3. Gateway → Agent: system/welcome (to: ["client-123"], with new capabilities)
4. Agent: Updates participantInfo.capabilities
5. Agent → Gateway: capability/grant-ack (correlation_id: ["grant-789"])
Revokes previously granted capabilities:
{
"protocol": "mew/v0.4",
"id": "revoke-123",
"from": "orchestrator",
"kind": "capability/revoke",
"payload": {
"recipient": "agent",
"grant_id": "grant-789",
"reason": "Task completed"
}
}Alternatively, revoke by capability pattern (removes ALL matching capabilities):
{
"protocol": "mew/v0.4",
"id": "revoke-456",
"from": "orchestrator",
"kind": "capability/revoke",
"payload": {
"recipient": "agent",
"capabilities": [
{
"kind": "mcp/request",
"payload": {
"method": "tools/*"
}
}
],
"reason": "No longer needed"
}
}Recipients acknowledge capability grants:
{
"protocol": "mew/v0.4",
"id": "ack-001",
"from": "agent",
"correlation_id": ["grant-789"],
"kind": "capability/grant-ack",
"payload": {
"status": "accepted"
}
}Gateways MUST maintain a mapping between logical participant names (from configuration) and runtime participant IDs (from WebSocket connections):
- Logical names are defined in space configuration (e.g., "mew", "orchestrator")
- Runtime IDs are assigned during connection (e.g., "client-1234567-abc")
- Messages addressed to logical names must be resolved to runtime IDs for delivery
When processing capability/grant, capability/revoke, or other messages that reference participants by name, the gateway MUST:
- Resolve the logical name to the actual runtime participant ID
- Use the runtime ID to locate the participant's WebSocket connection
- Deliver messages using the runtime ID
Implementation Strategy:
Gateways can maintain this mapping by:
- Creating a map of
logicalName → runtimeClientIDwhen participants join - Looking up tokens:
logicalName → token(from config) andruntimeClientID → token(from connection) - Matching tokens to resolve logical names to runtime IDs
Example:
Space config defines: "mew" with token "abc123"
Agent connects with: participant_id = "client-1759310701273-q9i1e5jkg", token = "abc123"
Gateway stores: "mew" → "client-1759310701273-q9i1e5jkg"
When processing capability/grant with recipient="mew":
1. Look up logical name "mew" → token "abc123"
2. Find runtime participant with token "abc123" → "client-1759310701273-q9i1e5jkg"
3. Send system/welcome to "client-1759310701273-q9i1e5jkg"
Invites new participants to the space:
{
"protocol": "mew/v0.4",
"id": "invite-123",
"from": "admin",
"kind": "space/invite",
"payload": {
"participant_id": "new-agent",
"email": "agent-operator@example.com", // Optional
"initial_capabilities": [
{"kind": "mcp/proposal"},
{"kind": "chat"}
],
"reason": "Adding specialized analysis agent"
}
}Fields:
participant_id: Identifier for the new participantemail: (optional) Email address to send invitation toinitial_capabilities: Capabilities the participant will have when they joinreason: Explanation for the invitation
Gateway Behavior:
- Generate authentication token for the new participant
- Register the participant in space configuration with initial capabilities
- Send
space/invite-ack(with credentials) directly to the inviter only - Broadcast a
system/presenceevent (without credentials) to inform other participants
If email is provided, the gateway SHOULD send an invitation email with connection details.
Gateway responds to the inviter with credentials for the new participant:
{
"protocol": "mew/v0.4",
"id": "invite-ack-123",
"from": "system:gateway",
"to": ["admin"],
"correlation_id": ["invite-123"],
"kind": "space/invite-ack",
"payload": {
"status": "created",
"participant_id": "new-agent",
"token": "generated-secure-token-xyz",
"connection_url": "ws://localhost:8080/participants/new-agent/ws"
}
}Fields:
status: Result of the invite ("created", "already_exists", "error")participant_id: The ID of the invited participanttoken: Authentication token for the new participant (only sent to inviter)connection_url: (optional) WebSocket URL for the new participant to connect
Security: This message MUST only be sent to the participant who initiated the invite. The token MUST NOT be broadcast to other participants.
Removes participants from the space:
{
"protocol": "mew/v0.4",
"id": "kick-456",
"from": "admin",
"kind": "space/kick",
"payload": {
"participant_id": "misbehaving-agent",
"reason": "Repeated capability violations"
}
}- Only participants with
capability/grantcapability can grant capabilities - Participants cannot grant capabilities they don't possess
- Gateway SHOULD track grant chains for audit purposes
- Revocations take effect immediately
- All space management operations generate audit logs
Presence messages are broadcast to notify all participants about join/leave events:
{
"protocol": "mew/v0.4",
"id": "env-presence-1",
"from": "system:gateway",
"kind": "system/presence",
"payload": {
"event": "join",
"participant": {
"id": "new-agent",
"capabilities": [
{"kind": "mcp/proposal"},
{"kind": "chat"}
]
}
}
}event(required): Either"join"or"leave"participant(required): Information about the participantid: The participant's identifiercapabilities: The participant's capabilities (forjoinevents)
Note: Presence messages are broadcast to all participants. The joining participant also receives a welcome message addressed specifically to them (via the to field) with their authoritative capabilities and the current participant list.
When a participant connects, the gateway MUST send a welcome message addressed specifically to that participant (using the to field):
{
"protocol": "mew/v0.4",
"id": "env-welcome-1",
"ts": "2025-01-09T10:00:00Z",
"from": "system:gateway",
"to": ["new-participant"],
"kind": "system/welcome",
"payload": {
"you": {
"id": "new-participant",
"capabilities": [
{"kind": "mcp/proposal"},
{"kind": "chat"}
]
},
"participants": [
{
"id": "agent-1",
"capabilities": [
{"kind": "mcp/*"},
{"kind": "chat"}
]
},
{
"id": "agent-2",
"capabilities": [
{"kind": "mcp/response"},
{"kind": "chat"}
]
}
],
"active_streams": [
{
"stream_id": "stream-42",
"owner": "agent-1",
"direction": "upload",
"created": "2025-01-09T10:00:00Z",
"content_type": "application/json",
"format": "jsonl",
"description": "Chain-of-thought reasoning",
"expected_size_bytes": 1048576
}
]
}
}you: The joining participant's own information and authoritative capabilitiesparticipants: Current list of other participants in the space and their capabilitiesactive_streams(optional): Array of currently active streams in the space [j8v]. Each stream object contains:stream_id(required): Unique identifier for the streamowner(required): Participant ID that requested the streamdirection(required): "upload" or "download"created(required): ISO 8601 timestamp when the stream was openedexpected_size_bytes(optional): Size hint from original requestdescription(optional): Human-readable description from original requestcontent_type(optional): MIME type or format identifier (e.g., "application/json", "application/x-game-positions")format(optional): Stream schema/encoding (e.g., "jsonl", "binary-vector3")target(optional): Array of participant IDs receiving stream data frames [t5d]. If present, only listed participants receive frames; if absent, stream broadcasts to allmetadata(optional): Arbitrary key-value pairs from original request- Additional custom fields from the original
stream/requestare preserved - May be omitted or empty array if no streams are active
- Design principle: All fields from the original
stream/requestpayload are preserved to ensure late joiners have complete context about stream format and purpose
Important: Other participants SHOULD ignore welcome messages not addressed to them when constructing prompts, as these can cause significant bloat. The message is already targeted via the to field.
When a participant attempts an operation they lack capability for:
{
"protocol": "mew/v0.4",
"id": "env-violation-1",
"from": "system:gateway",
"to": ["violator"],
"kind": "system/error",
"correlation_id": ["env-bad-call"],
"payload": {
"error": "capability_violation",
"attempted_kind": "mcp/request",
"your_capabilities": [
{"kind": "mcp/proposal"},
{"kind": "chat"}
]
}
}Participant control envelopes allow coordinators and the gateway to modulate how agents operate in a space. They provide a uniform mechanism for pausing work, requesting telemetry, and resetting state without overloading the MCP namespace.
Requests that a participant temporarily stop emitting new work:
{
"protocol": "mew/v0.4",
"id": "pause-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/pause",
"payload": {
"reason": "rate_limit",
"timeout_seconds": 60
}
}- Target participant SHOULD stop sending envelopes other than acknowledgements or cancellations until resumed
payload.timeout_secondsMAY be provided so the participant can automatically resume if no explicit resume arrives- Gateways MAY enforce pauses by rejecting other outbound traffic while in effect
Lifts a previously applied pause:
{
"protocol": "mew/v0.4",
"id": "resume-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/resume",
"correlation_id": ["pause-1"],
"payload": {}
}correlation_idSHOULD reference the pause being cleared when known- Participants MAY resume autonomously when a pause timeout expires, but SHOULD still emit a
participant/resumeto document the change in state
Asks a participant to emit its current utilization telemetry. Earlier drafts referenced this as participant/requestStatus; the
hyphenated name is normative:
{
"protocol": "mew/v0.4",
"id": "status-request-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/request-status",
"payload": {
"fields": ["tokens", "latency"]
}
}payload.fieldsMAY limit which metrics are requested- Participants SHOULD answer with
participant/statuspromptly
Reports runtime telemetry such as token consumption and context occupancy. This message is typically emitted in response to
participant/request-status but MAY be sent proactively:
{
"protocol": "mew/v0.4",
"id": "status-1",
"from": "agent-1",
"kind": "participant/status",
"correlation_id": ["status-request-1"],
"payload": {
"tokens": 12345,
"max_tokens": 20000,
"messages_in_context": 128,
"latency_ms": 350
}
}tokensandmax_tokensprovide the usage context introduced in this releasemessages_in_contextMUST be included and counts envelopes currently retained for the active conversational context- Additional metrics are allowed and should use descriptive snake_case keys
- Participants MAY broadcast status to improve observability, but gateways MAY limit dissemination to reduce noise
- Participants MAY also emit
participant/statusproactively when approaching a resource limit (for example, when only 10% of the context window remains) so orchestrators can intervene before prompts truncate unexpectedly
Instructs a participant to drop cached context. The payload.direction field indicates whether to trim from the oldest or
newest data, corresponding to the (Oldest|Newest) notation in the request:
{
"protocol": "mew/v0.4",
"id": "forget-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/forget",
"payload": {
"direction": "oldest",
"entries": 10
}
}directionMUST be either"oldest"or"newest"entries(optional) specifies how many records or messages to discard; implementations MAY interpret as tokens- Participants SHOULD emit a follow-up
participant/statusorchat/acknowledgeonce the context is dropped - Agents SHOULD monitor their context usage and automatically trigger a forget/compaction cycle before hitting hard limits.
When compacting autonomously, they MUST send a
participant/statusupdate indicating the compaction status and resulting resource headroom so observers understand why history changed.
Requests that a participant run its compaction routine to free headroom in its working memory or conversational history:
{
"protocol": "mew/v0.4",
"id": "compact-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/compact",
"payload": {
"reason": "token_pressure",
"target_tokens": 16000
}
}payload.reason(optional) provides operator context the participant MAY log or surface in status updatespayload.target_tokens(optional) indicates a desired ceiling after compaction; participants MAY ignore if unsupported- Participants that maintain working memory SHOULD attempt compaction promptly and emit interim
participant/statusupdates such ascompacting/compacted - Participants with no compactable state MAY ignore the request entirely or MAY reply with
participant/compact-doneincluding"skipped": trueto document that nothing changed
Acknowledges the completion (or intentional skip) of a requested compaction:
{
"protocol": "mew/v0.4",
"id": "compact-done-1",
"from": "agent-1",
"to": ["orchestrator"],
"kind": "participant/compact-done",
"correlation_id": ["compact-1"],
"payload": {
"freed_tokens": 2048,
"freed_messages": 12,
"status": "compacted"
}
}correlation_idMUST reference the originatingparticipant/compactrequest when one existsfreed_tokens/freed_messages(optional) report how much context was reclaimed- When compaction is skipped, participants SHOULD include
"skipped": trueand MAY add a human-readable"reason" - Participants MUST send
participant/compact-doneafter completing work triggered byparticipant/compact, even if no context was removed, so orchestrators can clear pending actions
Instructs a participant to wipe its entire retained conversational or working memory:
{
"protocol": "mew/v0.4",
"id": "clear-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/clear"
}participant/clearSHOULD be treated as stronger thanparticipant/forgetand MUST erase all cached context, scratchpads, and working memories- No additional payload fields are required; implementations MAY omit the
payloadproperty or include an empty object if their transport requires it - Participants SHOULD emit a follow-up
participant/statusconfirming an empty context (e.g.,messages_in_context = 0)
Returns a participant to a known baseline configuration:
{
"protocol": "mew/v0.4",
"id": "restart-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/restart"
}- No standard payload fields are required; implementations MAY include vendor-specific restart hints if all participants support them
- Participants SHOULD terminate outstanding workflows and streams when restarting
- Gateways MAY treat a restart as implicit acknowledgement that previous capability grants remain in effect unless explicitly revoked
Requests that a participant cease all activity without restarting, allowing the orchestrator to keep the slot idle or replace it later:
{
"protocol": "mew/v0.4",
"id": "shutdown-1",
"from": "orchestrator",
"to": ["agent-1"],
"kind": "participant/shutdown"
}- No standard payload fields are required; orchestrators MAY include optional context if their counterpart supports it
- Participants SHOULD acknowledge shutdown via
chat/acknowledgeor by emitting a terminalsystem/presenceleave event - After shutdown, gateways SHOULD reject further outbound envelopes from the participant until it rejoins the space
Streaming envelopes coordinate high-volume or binary data transfer while keeping the core message bus lightweight.
Sent by whichever entity initiates stream negotiation (typically a participant asking the gateway for bulk-transfer resources):
{
"protocol": "mew/v0.4",
"id": "stream-req-1",
"from": "agent-1",
"to": ["gateway"],
"kind": "stream/request",
"payload": {
"direction": "upload",
"expected_size_bytes": 10485760,
"description": "Large dataset export",
"content_type": "application/json",
"format": "jsonl",
"target": ["aggregator"],
"metadata": {
"compression": "gzip",
"schema_version": "1.0"
}
}
}Payload Fields:
direction(required): "upload" or "download" - clarifies data flow directionexpected_size_bytes(optional): Advisory size hint for the streamdescription(optional): Human-readable description of stream purposecontent_type(optional): MIME type or custom format identifier (e.g., "application/json", "application/x-game-positions")format(optional): Stream schema/encoding specification (e.g., "jsonl", "binary-vector3", "utf8-newline-delimited")target(optional): Array of participant IDs for targeted delivery [t5d]. If specified and non-empty, stream data frames are delivered only to listed participants. If omitted, null, or empty, frames are broadcast to all participants (default behavior)metadata(optional): Arbitrary key-value pairs for application-specific metadata- Additional custom fields are allowed and will be preserved [j8v]
Gateway Behavior:
- The gateway MUST intercept all
stream/requestmessages and reply withstream/opencontaining a uniquestream_id - The gateway MUST preserve ALL payload fields from the request and include them in
active_streamswhen sendingsystem/welcometo late joiners [j8v] - The gateway is responsible for stream ID allocation and management, regardless of the request's
tofield correlation_id(not shown) MAY reference the message that motivated the stream (e.g., a proposal or request)- If
targetis specified, the gateway MUST validate all target participants exist in the space; if any target does not exist, return asystem/errorwitherror: "target_not_found"[t5d]
Usage Notes:
- Agents that expose long-form reasoning SHOULD consider issuing a
stream/requestalongsidereasoning/startso subscribers can follow high-volume traces without bloating the shared envelope log - Applications with custom stream formats (like games) SHOULD include
content_typeandformatto enable late joiners to properly parse stream data
Confirms that a stream has been established and assigns the identifier used for raw frames:
{
"protocol": "mew/v0.4",
"id": "stream-open-1",
"from": "gateway",
"to": ["agent-1"],
"kind": "stream/open",
"correlation_id": ["stream-req-1"],
"payload": {
"stream_id": "stream-42",
"encoding": "binary",
"target": ["aggregator"]
}
}payload.stream_idMUST be unique per active stream on the connectionpayload.targetechoes the target from the request if specified [t5d]. Clients can use this to confirm targeted delivery is active- Additional metadata such as
encodingorcompressionMAY be provided for the receiver - Note: The
stream/openmessage itself is broadcast to all participants for observability (per MEW Protocol visibility). Only the stream data frames are affected by targeting [t5d]
Once a stream is open, binary or chunked data travels on the same WebSocket without the JSON envelope wrapper. Frames are prefixed using the pattern #streamID#data, matching the #streamID#data example provided with this update. Implementations MUST ensure framing preserves boundaries so receivers can demultiplex concurrent streams.
Targeted Delivery [t5d]:
- If the stream has a
targetspecified, the gateway delivers data frames only to the listed participant(s) - If the stream has no target (or empty target), data frames are broadcast to all participants in the space
- This enables O(N) delivery for aggregation patterns instead of O(N²) broadcast
- Example: Multiple players send position updates targeted to a game server, which broadcasts world state to all
Terminates a stream and releases associated resources:
{
"protocol": "mew/v0.4",
"id": "stream-close-1",
"from": "gateway",
"to": ["agent-1"],
"kind": "stream/close",
"correlation_id": ["stream-open-1"],
"payload": {
"reason": "complete"
}
}- Either side MAY send
stream/close payload.reasonSHOULD distinguish between normal completion and errors (e.g.,"complete","cancelled","timeout")- After closing, no additional
#streamID#...frames MUST be sent
Grants write access to another participant, allowing them to publish frames to the stream:
{
"protocol": "mew/v0.4",
"id": "grant-1",
"from": "stream-owner",
"to": ["gateway"],
"kind": "stream/grant-write",
"payload": {
"stream_id": "stream-42",
"participant_id": "player1",
"reason": "Player claimed character control"
}
}Behavior:
- Only the stream owner can grant write access
- The target participant must exist in the space
- Granting is idempotent (re-granting to same participant has no effect)
- Gateway broadcasts acknowledgement (
stream/write-granted) to all participants
Error conditions:
- Stream does not exist →
system/errorwithstream_not_found - Sender not owner →
system/errorwithunauthorized - Participant not in space →
system/errorwithparticipant_not_found
Revokes write access from a participant:
{
"protocol": "mew/v0.4",
"id": "revoke-1",
"from": "stream-owner",
"to": ["gateway"],
"kind": "stream/revoke-write",
"payload": {
"stream_id": "stream-42",
"participant_id": "player1",
"reason": "Player disconnected"
}
}Behavior:
- Only the stream owner can revoke write access
- Owner cannot revoke own write access
- Revoking is idempotent (revoking non-authorized participant has no effect)
- Gateway broadcasts notification (
stream/write-revoked) to all participants - Gateway automatically revokes write access when participants disconnect (except owner)
Error conditions:
- Stream does not exist →
system/errorwithstream_not_found - Sender not owner →
system/errorwithunauthorized - Owner attempts self-revoke →
system/errorwithinvalid_operation
Transfers complete ownership of a stream to another participant:
{
"protocol": "mew/v0.4",
"id": "transfer-1",
"from": "current-owner",
"to": ["gateway"],
"kind": "stream/transfer-ownership",
"payload": {
"stream_id": "stream-42",
"new_owner": "ai-agent",
"reason": "Permanent control delegation"
}
}Behavior:
- Only the current owner can transfer ownership
- New owner receives all ownership privileges (grant/revoke/transfer/close)
- Previous owner loses ownership (but may retain write access if previously granted)
- New owner is automatically added to authorized writers if not present
- Gateway broadcasts transfer event (
stream/ownership-transferred) to all participants
Error conditions:
- Stream does not exist →
system/errorwithstream_not_found - Sender not owner →
system/errorwithunauthorized - New owner not in space →
system/errorwithparticipant_not_found
The system/welcome message includes authorized_writers for each active stream, showing which participants can publish frames:
{
"kind": "system/welcome",
"payload": {
"active_streams": [
{
"stream_id": "stream-42",
"owner": "character-server",
"authorized_writers": ["character-server", "player1"],
"direction": "upload",
"created": "2025-11-23T12:00:00Z",
"content_type": "application/json",
"format": "seacat/character-position-v1",
"metadata": {
"character_id": "character-player1"
}
}
]
}
}authorized_writers(optional): Array of participant IDs authorized to write to this stream- If omitted, defaults to
[owner](backward compatible) - Always includes owner (owner cannot be revoked)
The gateway enforces security through two mechanisms:
-
Identity Validation: The gateway MUST verify that the
fromfield matches the authenticated participant's ID. Participants cannot spoof messages from other participants. -
Capability Matching: The gateway assigns capabilities to each connection using JSON pattern matching. Capabilities are JSON objects that match against the message structure (kind and payload).
Capability Pattern Structure:
{
"kind": "pattern",
"payload": {
"field": "pattern"
}
}Pattern Types:
- String with wildcards:
"mcp/*"matchesmcp/request,mcp/response, etc. - Exact match:
"chat"only matches"chat" - Nested patterns: Match specific payload fields
Reserved Namespace:
The system/* namespace is reserved exclusively for the gateway. Participants MUST NOT send messages with kind starting with system/. The gateway MUST reject any such attempts. Only the gateway can generate system messages (system/welcome, system/presence, system/error).
Example Capability Sets:
Gateways may implement various capability profiles using JSON patterns:
-
Full Access example:
[ {"kind": "mcp/*"}, {"kind": "chat"} ]Can send any MCP messages (requests, responses, proposals)
-
Proposal-Only example:
[ {"kind": "mcp/proposal"}, {"kind": "mcp/response"}, {"kind": "chat"} ]Cannot send direct MCP requests, must use proposals
-
Tool-Specific example:
[ { "kind": "mcp/request", "payload": { "method": "tools/call", "params": { "name": "read_*" } } }, {"kind": "mcp/response"}, {"kind": "chat"} ]Can only call tools matching
read_*pattern
Progressive Trust Example:
Capabilities can be expanded as trust increases:
- Initial:
[{"kind": "mcp/proposal"}, {"kind": "chat"}]- Proposals only - Basic: Add read capabilities with payload patterns
- Trusted: Add tool usage with specific tool patterns
- Full: Replace with
[{"kind": "mcp/*"}]- Unrestricted MCP access
The gateway enforces capabilities using JSON pattern matching against the message structure:
What the Gateway Does:
- Matches the message's
kindandpayloadagainst participant's capability patterns - Uses wildcards, regex patterns, and JSONPath for flexible matching
- Blocks messages if no capability matches
- Returns error responses for capability violations
What the Gateway Does NOT Do:
- Parse or validate JSON-RPC payloads
- Understand MCP semantics
- Detect spoofing (where
kinddoesn't match payload content)
Pattern Matching Examples:
- Message with
kind: "mcp/request"andpayload.method: "tools/call"matches:{"kind": "mcp/*"}{"kind": "mcp/request"}{"kind": "mcp/request", "payload": {"method": "tools/*"}}
- Message with
kind: "chat"only matches:{"kind": "chat"}{"kind": "*"}(wildcard for all kinds)
Receiving participants SHOULD validate message structure:
- Verify
kindis appropriate for the message type - Validate payload structure matches expected schema
- Drop or report malformed messages
- MAY track misbehaving participants for reputation scoring
Schema Mismatch Warning: Participants may observe messages with schemas that differ from what was advertised:
- Other participants might advertise one tool schema but send different parameters
- Participants joining mid-conversation may see responses to requests they never observed
- Always parse messages defensively and validate structure before processing
The gateway determines capabilities during authentication. Implementation-specific policies may include:
- Token-based: Tokens map to specific capability sets
- Implementations may encode capabilities in tokens or map tokens to capability profiles
- Identity-based: Map authenticated identities to capability sets
- Space access control: Spaces may be public, private, or restricted
- Dynamic capabilities: Capabilities may be modified during a session
Connect: GET /ws?space=<name> with Authorization header
The gateway:
- Validates the bearer token (if provided)
- Maps the token to a participant ID (implementation-specific)
- Determines capabilities based on implementation policy
- MUST send welcome message with the assigned participant ID and accurate capabilities
- MUST enforce capability rules on all subsequent messages
Note: Participants only provide their bearer token. The gateway is responsible for mapping tokens to participant IDs through its own implementation-specific mechanism.
The capabilities in the welcome message constitute the authoritative list of what operations the participant can perform. Participants SHOULD use this list to understand their allowed operations.
Gateways MAY expose HTTP endpoints that let trusted callers inject MEW envelopes without maintaining a WebSocket connection. These endpoints mirror the WebSocket semantics and MUST enforce the same capability rules as live connections.
POST /participants/{participant_id}/messages
Authorization: Bearer {token}
Content-Type: application/json
{
"kind": "chat",
"payload": {
"text": "Hello world"
}
}
Typical response
{
"id": "msg-123",
"status": "accepted",
"timestamp": "2025-01-09T10:00:00Z"
}Gateways MAY also expose a retrieval endpoint for polling scenarios:
GET /participants/{participant_id}/messages?since={timestamp}
Authorization: Bearer {token}
Responses MUST contain complete envelopes and respect the caller's capabilities; messages the caller lacks permission to view MUST be omitted.
For each incoming message from a participant:
if not matches_any_capability(message, participant.capabilities):
send_error(f"Capability violation: you lack capability for this operation")
broadcast_violation_notice() # optional
return
send_to_space(message)This is a v0.x release. Breaking changes are expected between minor versions until v1.0:
- v0.4 is NOT backward compatible with v0.3
- Clients and gateways MUST use matching protocol versions
- No compatibility guarantees until v1.0 release
Key updates in v0.4:
- Protocol identifier changed from
mew/v0.3tomew/v0.4 - Added participant control messages for runtime management:
participant/request-status,participant/status,participant/forget,participant/clear,participant/restart, andparticipant/shutdown - Introduced stream lifecycle support (
stream/request,stream/open,stream/close) plus binary frame guidance for high-volume data - Documented optional HTTP message injection endpoints alongside the WebSocket channel
Key breaking changes from v0.2 (cumulative):
- Protocol name changed from MEUP to MEW Protocol
- Protocol identifier changed from
meup/v0.2tomew/v0.4 - Full name is now "Multi-Entity Workspace Protocol"
Key breaking changes from v0.0 (cumulative):
- Protocol identifier changed from
mcp-x/v0tomew/v0.4 - Message kinds now use slash notation (e.g.,
mcp/requestinstead ofmcp/request:tools/call) - Method information moved to payload
- Capabilities use JSON pattern matching instead of string wildcards
correlation_idis now always an array- Chat remains as dedicated
chatkind
- v0.x series is experimental and subject to breaking changes
- v1.0 will establish stable protocol with compatibility guarantees
- Production deployments should pin specific v0.x versions
MEW Protocol's security model relies on capability-based access control enforced by the gateway, with additional validation responsibilities distributed among participants.
- Identity Validation: Gateway MUST verify the
fromfield matches the authenticated participant ID (Section 4.1) - Capability Enforcement: Gateway enforces JSON pattern-based capabilities on all messages (Section 4.1, 4.2)
- Reserved Namespaces: Only the gateway can send
system/*messages, preventing spoofing (Section 3.8) - Proposal Isolation: Untrusted agents use
mcp/proposalto suggest rather than execute operations (Section 3.3) - Lazy Gateway Enforcement: Gateway validates message structure but not payload semantics (Section 4.2)
Gateway Responsibilities:
- Authenticate participants and assign stable IDs
- Enforce capability patterns on message kinds and payloads
- Prevent identity spoofing via
fromfield validation - Block
system/*messages from participants
Participant Responsibilities:
- Validate envelope
kindmatches payload content - Detect and report malformed or spoofed messages
- Parse messages defensively (schemas may differ from advertised)
- Track misbehaving participants for reputation scoring
- Start Restricted: New agents begin with
mcp/proposalcapability only - Progressive Trust: Expand capabilities based on observed behavior over time
- Validate Strictly: Check that
kindmatches the payload type (e.g.,mcp/requestfor MCP requests) - Monitor Proposals: Log proposal/fulfillment patterns for audit trails
- Report Violations: Track and broadcast capability violations for transparency
-
Connection Management
- WebSocket server at
GET /ws?space=<name> - Bearer token validation from Authorization header
- Token-to-participant-ID mapping (implementation-specific)
- Connection state tracking per participant
- WebSocket server at
-
Capability Enforcement
- Pattern matching engine for wildcard capabilities
- Validate message
kindagainst sender's capabilities - Block messages that don't match capabilities
- Return
system/errorfor capability violations
-
System Messages
- Send
system/welcomewith participant ID and capabilities on connect - Broadcast
system/presencefor join/leave events - Generate
system/errorfor protocol violations - Ensure only gateway can send
system/*messages
- Send
-
Message Routing
- Route messages based on
tofield (broadcast if empty/omitted) - Preserve envelope structure without modification
- NO payload parsing or validation (lazy enforcement)
- Route messages based on
-
Message Validation
- Verify envelope structure before processing
- Validate
kindmatches the payload type (e.g.,mcp/requestfor requests withpayload.method) - Validate payload structure matches expected schema for the
kind - Drop messages that fail validation
- Track/report misbehaving participants
-
MCP Integration
- Implement MCP server capabilities
- Handle incoming MCP requests/responses
- Generate proper correlation IDs for responses
- Include correlation ID in all response messages
-
Proposal Handling (if applicable)
- Recognize
mcp.proposalmessages - Implement fulfillment logic for approved proposals
- Include proposal ID as correlation_id when fulfilling
- Recognize
-
Connection Behavior
- Process
system/welcometo learn own ID and capabilities - Handle
system/presenceto track other participants - Respect capability restrictions from welcome message
- Process
The following examples demonstrate multi-agent orchestration patterns enabled by MEW Protocol's capability-based security model. The core pattern is delegated fulfillment, where untrusted agents propose operations that are reviewed and executed by trusted participants.
In this example, an agent with limited capabilities uses proposals:
- Agent connects → receives proposal-only capabilities
- Agent attempts MCP request → gateway blocks, returns error
- Agent sends
mcp/proposal→ visible to all participants - Another participant with appropriate capabilities fulfills it
- Fulfiller sends real
mcp/requestmessage to target - Target executes and returns result to fulfiller (proposer observes via broadcast)
One possible administrative workflow:
- Agent operates successfully over time
- Administrator observes behavior patterns
- Administrator expands agent capabilities
- Agent gains access to additional operations
A gateway might assign different capability profiles:
- Administrative users:
{"kind": "mcp/*"}(full access) - Service agents:
{"kind": "mcp/response"}(response-only) - New agents:
{"kind": "mcp/proposal"}(proposal-only) - Monitoring tools:
{"kind": "mcp/request", "payload": {"method": "*/list"}}(read-only)
All participants operate in the same space with their assigned capabilities.
A human supervises an untrusted agent through intermediary agents:
- Proposer Agent (capability:
mcp/proposal) proposes writing a file - Human reviews the proposal and decides to approve it by directing the Orchestrator Agent (capability:
mcp/request) to fulfill the proposal - Orchestrator Agent sends the MCP request to Worker Agent
- Worker Agent executes the actual file write operation and responds
- Over time, Human teaches Orchestrator Agent rules about which proposals to auto-approve
- Orchestrator Agent begins autonomously fulfilling certain proposals without human intervention
This pattern demonstrates progressive automation - a key goal of MEW Protocol. As the orchestrator observes human decisions over time, it learns which types of proposals can be safely auto-approved. This transforms human judgment into automated policies, creating a system that becomes more autonomous while maintaining safety through learned boundaries.
Minimal state required per connection:
{
"connection_id": "ws-123",
"participant_id": "agent-a",
"capabilities": [{"kind": "mcp/proposal"}, {"kind": "chat"}],
"connected_at": "2025-08-26T14:00:00Z"
}Optional proposal tracking for fulfillment (maintained by agents, not gateway):
{
"proposalId": "env-proposal-1",
"proposer": "untrusted-agent",
"target": "target-agent",
"method": "tools/call",
"params": {"name": "dangerous_operation", "arguments": {...}},
"status": "pending",
"createdAt": "2025-08-26T14:00:00Z",
"expiresAt": "2025-08-26T14:05:00Z"
}- MCP Specification 2025-06-18
- MEW v0.0 Specification (formerly MEUP)
- ADR-003: Tool Access Control
- OAuth 2.0 RFC 6749 (for token patterns)