Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/CONNECTION_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@ Idle → Connecting → Connected
| Connected | Error/Rejected | Degraded |
| Connected | PairingRequired | PairingRequired |
| Connected | Connecting | Connecting |
| Connected | Disabled/Off | Connected |
| Connected | Idle while Node mode is intended | Degraded |
| Connected | Disabled/Off | Ready |

`GatewayConnectionSnapshot.NodeConnectionIntended` records the Node mode intent used by the manager's state machine. If Node mode is enabled but node startup is skipped, blocked, or missing a node credential, the manager publishes a blocked node snapshot (`NodeState=Error`, `NodeError=...`) instead of leaving the node idle and letting tray surfaces report a healthy connection.

## Gateway registry and persistence

Expand Down
2 changes: 2 additions & 0 deletions docs/MCP_MODE.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ Settings UI exposes both toggles in the Advanced section, with the live MCP endp

A legacy `McpOnlyMode` field is migrated automatically on load and never re-written.

MCP startup is reported from the actual listener state. `NodeService.McpStartupError` is populated when capability registration or the HTTP listener fails, and MCP-only startup is not treated as successful unless the loopback MCP server is running. Tray, Permissions, and Command Center surfaces show local MCP-only separately from gateway connectivity so a working local MCP listener is never presented as a gateway connection.

## Why this matters

### Testing
Expand Down
18 changes: 18 additions & 0 deletions src/OpenClaw.Connection/ConnectionStateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,16 @@ public void SetNodeEnabled(bool enabled)
{
_nodeEnabled = enabled;
if (!enabled)
{
_nodeState = RoleConnectionState.Disabled;
_nodeError = null;
_nodeCredentialSource = null;
}
else if (_nodeState == RoleConnectionState.Disabled)
{
_nodeState = RoleConnectionState.Idle;
_nodeError = null;
}
RebuildSnapshot();
}

Expand Down Expand Up @@ -199,6 +206,15 @@ internal void SetNodeCredentialSource(string? source)
RebuildSnapshot();
}

internal void BlockNodeStart(string detail)
{
_nodeEnabled = true;
_nodeState = RoleConnectionState.Error;
_nodeError = detail;
_nodeCredentialSource = null;
RebuildSnapshot();
}

/// <summary>Update the operator pairing request ID in the snapshot.</summary>
internal void SetOperatorPairingRequestId(string? requestId)
{
Expand Down Expand Up @@ -304,6 +320,7 @@ private void ApplyTransition(ConnectionTrigger trigger, string? detail)

case ConnectionTrigger.NodePairingRequired:
_nodeState = RoleConnectionState.PairingRequired;
_nodeError = null;
break;

case ConnectionTrigger.NodePaired:
Expand Down Expand Up @@ -340,6 +357,7 @@ private void RebuildSnapshot()
// Clear requestId when no longer in PairingRequired to prevent stale reads
OperatorPairingRequestId = _operatorState == RoleConnectionState.PairingRequired
? Current.OperatorPairingRequestId : null,
NodeConnectionIntended = _nodeEnabled,
NodeState = _nodeState,
NodeError = _nodeError,
NodeCredentialSource = _nodeCredentialSource,
Expand Down
Loading