Examples and local development guide
These examples target macp-runtime v0.4.0 and the stream-capable freeze profile.
They intentionally use the local-development security shortcut:
export MACP_ALLOW_INSECURE=1
export MACP_ALLOW_DEV_SENDER_HEADER=1
cargo runThe example binaries in src/bin attach x-macp-agent-id metadata so the runtime can derive the authenticated sender.
Ground rules for sessions with strict validation
For these standards-track modes and built-in extensions:
macp.mode.decision.v1macp.mode.proposal.v1macp.mode.task.v1macp.mode.handoff.v1macp.mode.quorum.v1ext.multi_round.v1(built-in extension)
SessionStartPayload must include all of the following:
participantsmode_versionconfiguration_version- positive
ttl_ms
The example clients use:
mode_version = "1.0.0"configuration_version = "config.default"policy_version = "policy.default"
Example 1: Decision Mode
Run:
cargo run --bin clientFlow:
InitializeListModesSessionStartbycoordinatorProposalbycoordinatorEvaluationby a participantVoteby a participantCommitmentbycoordinatorGetSession
Important runtime behavior:
- initiator/coordinator may emit
ProposalandCommitment - declared participants may also emit
Proposal,Evaluation,Objection, andVote - duplicate proposal IDs are rejected
- votes are tracked per proposal, per sender
CommitmentPayloadversion fields must match the bound session versions
Example 2: Proposal Mode
Run:
cargo run --bin proposal_clientFlow:
- buyer starts the session
- seller creates a proposal
- buyer counters
- both required participants accept the same live proposal
- buyer emits
Commitment
Important runtime behavior:
- a proposal session does not resolve merely because a proposal exists
Commitmentis accepted only after acceptance convergence or a terminal rejection
Example 3: Task Mode
Run:
cargo run --bin task_clientFlow:
- planner starts the session
- planner sends
TaskRequest - worker sends
TaskAccept - worker sends
TaskUpdate - worker sends
TaskComplete - planner emits
Commitment
Example 4: Handoff Mode
Run:
cargo run --bin handoff_clientFlow:
- owner starts the session
- owner sends
HandoffOffer - owner sends
HandoffContext - target sends
HandoffAccept - owner emits
Commitment
Example 5: Quorum Mode
Run:
cargo run --bin quorum_clientFlow:
- coordinator starts the session
- coordinator sends
ApprovalRequest - participants send ballots
- coordinator emits
Commitmentafter threshold is satisfied
Example 6: Multi-round mode (built-in extension)
Run:
cargo run --bin multi_round_clientFlow:
- coordinator starts the session with mode
ext.multi_round.v1and strictSessionStart(participants, mode_version, configuration_version, ttl_ms) - participants exchange contributions across multiple rounds
- convergence is tracked by the runtime
- coordinator emits
Commitmentafter convergence
Important runtime behavior:
ext.multi_round.v1is a built-in extension mode, discoverable viaListExtModes- convergence is tracked but does not auto-resolve the session — an explicit
Commitmentis required - uses the same strict
SessionStartcontract as standards-track modes
Example 7: StreamSession
StreamSession provides per-session bidirectional streaming. The Initialize response advertises stream: true.
StreamSession emits only accepted canonical MACP envelopes. A single gRPC stream binds to one session. If a client needs negative per-message acknowledgements, it should continue to use Send.
Practical notes:
- bind a stream by sending a session-scoped envelope for the target session
- use
SessionStartto create a new session over the stream - stream attachment starts observing future accepted envelopes from the bind point; it does not replay earlier history
- use a session-scoped
Signalenvelope with the correctsession_idandmodeto attach to an existing session without mutating it - mixed-session streams are rejected with
FAILED_PRECONDITION
Example 8: Extension mode lifecycle
The runtime supports dynamic extension mode management via gRPC:
ListExtModes— discover available extension modes (e.g.ext.multi_round.v1)RegisterExtMode— register a new extension mode by providing aModeDescriptorwith the mode name, message types, and terminal message types; the runtime creates a passthrough handlerUnregisterExtMode— remove a dynamically registered extension (built-in modes likeext.multi_round.v1cannot be removed)PromoteMode— promote an extension to standards-track, optionally renaming the identifier (e.g.ext.foo.v1tomacp.mode.foo.v1)
Important runtime behavior:
- extension mode names must not use the reserved
macp.mode.*namespace - dynamically registered modes use a passthrough handler that accepts any listed message type and requires explicit
Commitmentfrom the initiator to resolve WatchModeRegistrysubscribers receiveRegistryChangedevents on every register, unregister, or promote operationGetManifestandInitializealways include all modes (standards-track + extensions);ListModesonly returns standards-track
Example 9: Freeze-check / error-path client
Run:
cargo run --bin fuzz_clientThis client exercises common failure paths for the freeze profile, including:
- invalid protocol version
- empty mode
- invalid payloads
- duplicate messages
- unauthorized sender spoofing
- payload too large
- session access without membership
Session ID policy
Session IDs must be either:
- UUID v4/v7 in hyphenated lowercase canonical form (36 characters, e.g.
550e8400-e29b-41d4-a716-446655440000) - Base64url token of at least 22 characters using only
[A-Za-z0-9_-]
Human-readable or short IDs (e.g. "my-session", "s1") are rejected with INVALID_SESSION_ID. The example clients generate UUID v4 session IDs automatically.
Common troubleshooting
UNAUTHENTICATED
Either send a bearer token that exists in the configured auth map, or start the runtime with:
export MACP_ALLOW_DEV_SENDER_HEADER=1and ensure the client sets x-macp-agent-id.
INVALID_ENVELOPE on SessionStart
For a standards-track mode or built-in extension, check that:
- the mode name is canonical
- the payload is not empty
mode_versionis presentconfiguration_versionis presentttl_ms > 0- participants are present and unique
SESSION_NOT_OPEN
The session is already resolved or expired. Use GetSession to confirm the terminal state.
RATE_LIMITED
Increase the limits only if you understand the operational impact:
export MACP_SESSION_START_LIMIT_PER_MINUTE=120
export MACP_MESSAGE_LIMIT_PER_MINUTE=2000