Engineering Blog

SecsHost MCP — Unscripted Live Session

Cold agent session against live HSMS loopback equipment: connect, health check, events, alarms, and trace — with raw tool results.

Session
Aug 1, 2026, 12:18 AM UTC
Server
SecsHost.Mcp (bundled with SecsHost downloads)
Equipment
Loopback equipment at 127.0.0.1 (local simulator)
Operator
Cursor agent (cold read of tool descriptions via tools/list)

Operator brief

Connect to the tool, check it is alive, watch for events while I run the sim, tell me what happened.

Cold session: the agent only saw MCP tool descriptions from tools/list — no prior SecsHost MCP playbook.

Action timeline

  1. 1
    connect

    Tool `connect` matches the brief. Mode active, deviceId 1 (common GEM default), loopback port from sim.

    Arguments

    {
      "ip": "127.0.0.1",
      "port": 62238,
      "deviceId": 1,
      "mode": "active"
    }

    Result

    {
      "ok": true,
      "status": {
        "connectionName": "mcp",
        "isConnected": true,
        "selectedState": "Selected",
        "ipAddress": "127.0.0.1",
        "port": 62238,
        "deviceId": 1,
        "mode": "Active",
        "timerConfig": {
          "t3ReplyMs": 45000,
          "t5ConnectSeparationMs": 10000,
          "t6ControlMs": 5000,
          "t7NotSelectedMs": 10000,
          "t8NetworkIntercharacterMs": 5000,
          "notes": "T3 enforced by HsmsHostConnection.SendMessageAndWaitForReply; T6 partially used for active TCP connect wait; T5/T7/T8 configured on ConnectionSettings but not fully enforced in the engine today."
        }
      }
    }
  2. 2
    are_you_there

    `are_you_there` is the described health check (S1F1/S1F2). Prefer it over raw SML.

    Result

    {
      "ok": true,
      "replySml": "S1F2\n<L [2]\n  <A \"LIVE-SIM\">\n  <A \"3.0\">\n>\n.",
      "mdln": "LIVE-SIM",
      "softrev": "3.0",
      "transactionId": 2
    }
  3. 3
    get_status_variables

    `get_status_variables` will attempt S1F11 once; loopback ignores S1F11 — expect numeric SVIDs + note.

    Arguments

    {
      "svids": "[1,2]"
    }

    Result

    {
      "ok": true,
      "replySml": "S1F4\n<L [2]\n  <U4 100>\n  <U4 101>\n>\n.",
      "nameResolutionNote": "S1F11/S1F12 name resolution skipped or failed; status variables will use numeric SVIDs. [mcp] T3 reply timeout for S1F11 (TID 3).",
      "cachedSvidNames": 0,
      "values": [
        {
          "index": 0,
          "svid": 1,
          "value": "U4 100",
          "valueSml": "<U4 100>"
        },
        {
          "index": 1,
          "svid": 2,
          "value": "U4 101",
          "valueSml": "<U4 101>"
        }
      ]
    }
  4. 4
    get_recent_events

    Brief says watch for events — `get_recent_events` / `get_recent_alarms` fill passively; no subscribe call.

    Result

    {
      "count": 2,
      "nextSeq": 5,
      "entries": [
        {
          "seq": 1,
          "utcTimestamp": "2026-08-01T00:18:13.4764691Z",
          "kind": "Event",
          "ceidOrAlid": 2001,
          "reportDataSummary": "L[1] {L[2] {U4 1 L[1] {A \"ceid-2001\"}}}",
          "rawSml": "S6F11 W\n<L [3]\n  <U4 0>\n  <U4 2001>\n  <L [1]\n    <L [2]\n      <U4 1>\n      <L [1]\n        <A \"ceid-2001\">\n      >\n    >\n  >\n>\n.",
          "transactionId": 8001
        },
        {
          "seq": 2,
          "utcTimestamp": "2026-08-01T00:18:13.4795595Z",
          "kind": "Event",
          "ceidOrAlid": 2002,
          "reportDataSummary": "L[1] {L[2] {U4 1 L[1] {A \"ceid-2002\"}}}",
          "rawSml": "S6F11 W\n<L [3]\n  <U4 0>\n  <U4 2002>\n  <L [1]\n    <L [2]\n      <U4 1>\n      <L [1]\n        <A \"ceid-2002\">\n      >\n    >\n  >\n>\n.",
          "transactionId": 8002
        }
      ]
    }
  5. 5
    get_recent_alarms

    Result

    {
      "count": 2,
      "nextSeq": 5,
      "entries": [
        {
          "seq": 3,
          "utcTimestamp": "2026-08-01T00:18:13.5554911Z",
          "kind": "Alarm",
          "ceidOrAlid": 77,
          "alarmSetOrClear": true,
          "reportDataSummary": "L[3] {B[1] 128 U4 77 A \"LIVE SET\"}",
          "rawSml": "S5F1 W\n<L [3]\n  <B 0x80>\n  <U4 77>\n  <A \"LIVE SET\">\n>\n.",
          "transactionId": 8003
        },
        {
          "seq": 4,
          "utcTimestamp": "2026-08-01T00:18:13.6473956Z",
          "kind": "Alarm",
          "ceidOrAlid": 77,
          "alarmSetOrClear": false,
          "reportDataSummary": "L[3] {B[1] 0 U4 77 A \"LIVE CLEAR\"}",
          "rawSml": "S5F1 W\n<L [3]\n  <B 0x00>\n  <U4 77>\n  <A \"LIVE CLEAR\">\n>\n.",
          "transactionId": 8004
        }
      ]
    }
  6. 6
    get_recent_events

    Incremental poll with `sinceSeq=2` — expect only events at/after that seq (second rapid-fire CEID).

    Arguments

    {
      "sinceSeq": 2
    }

    Result

    {
      "count": 1,
      "nextSeq": 5,
      "entries": [
        {
          "seq": 2,
          "utcTimestamp": "2026-08-01T00:18:13.4795595Z",
          "kind": "Event",
          "ceidOrAlid": 2002,
          "reportDataSummary": "L[1] {L[2] {U4 1 L[1] {A \"ceid-2002\"}}}",
          "rawSml": "S6F11 W\n<L [3]\n  <U4 0>\n  <U4 2002>\n  <L [1]\n    <L [2]\n      <U4 1>\n      <L [1]\n        <A \"ceid-2002\">\n      >\n    >\n  >\n>\n.",
          "transactionId": 8002
        }
      ]
    }
  7. 7
    get_trace

    `get_trace` for receipts including ConnectionHandler acks (S6F12/S5F2), not Select frames.

    Result

    {
      "count": 13,
      "nextSeq": 14,
      "entries": [
        {
          "seq": 1,
          "utcTimestamp": "2026-08-01T00:18:13.4892019Z",
          "rawTimestamp": "2026-08-01T00:18:13.4892019Z",
          "direction": "Tx",
          "sxFy": "S1F1",
          "sml": "S1F1 /* bus */ S1F1 ()",
          "systemBytes": 2,
          "rawPayload": "S1F1 ()|TID:2"
        },
        {
          "seq": 2,
          "utcTimestamp": "2026-08-01T00:18:13.477Z",
          "rawTimestamp": "20:18:13.477",
          "direction": "Rx",
          "sxFy": "S6F11",
          "sml": "S6F11 /* bus */ S6F11 (L {U4 0} {U4 2001} L L {U4 1} L {A ceid-2001})",
          "systemBytes": 8001,
          "rawPayload": "S6F11 (L {U4 0} {U4 2001} L L {U4 1} L {A ceid-2001})|TID:8001"
        },
        {
          "seq": 3,
          "utcTimestamp": "2026-08-01T00:18:13.48Z",
          "rawTimestamp": "20:18:13.480",
          "direction": "Rx",
          "sxFy": "S6F11",
          "sml": "S6F11 /* bus */ S6F11 (L {U4 0} {U4 2002} L L {U4 1} L {A ceid-2002})",
          "systemBytes": 8002,
          "rawPayload": "S6F11 (L {U4 0} {U4 2002} L L {U4 1} L {A ceid-2002})|TID:8002"
        },
        {
          "seq": 4,
          "utcTimestamp": "2026-08-01T00:18:13.5036301Z",
          "rawTimestamp": "2026-08-01T00:18:13.5036301Z",
          "direction": "Rx",
          "sxFy": "S1F2",
          "sml": "S1F2 /* bus */ S1F2 (L[2] {A \"LIVE-SIM\" A \"3.0\"})",
          "systemBytes": 2,
          "rawPayload": "S1F2 (L[2] {A \"LIVE-SIM\" A \"3.0\"})|TID:2"
        },
        {
          "seq": 5,
          "utcTimestamp": "2026-08-01T00:18:13.510507Z",
          "rawTimestamp": "2026-08-01T00:18:13.5105070Z",
          "direction": "Tx",
          "sxFy": "S1F11",
          "sml": "S1F11 /* bus */ S1F11 (L[0] {})",
          "systemBytes": 3,
          "rawPayload": "S1F11 (L[0] {})|TID:3"
        },
        {
          "seq": 6,
          "utcTimestamp": "2026-08-01T00:18:13.539Z",
          "rawTimestamp": "20:18:13.539",
          "direction": "Tx",
          "sxFy": "S6F12",
          "sml": "S6F12 /* bus */ S6F12 (<BIN>0</BIN>)",
          "systemBytes": 8002,
          "rawPayload": "S6F12 (<BIN>0</BIN>)|TID:8002"
        },
        {
          "seq": 7,
          "utcTimestamp": "2026-08-01T00:18:13.539Z",
          "rawTimestamp": "20:18:13.539",
          "direction": "Tx",
          "sxFy": "S6F12",
          "sml": "S6F12 /* bus */ S6F12 (<BIN>0</BIN>)",
          "systemBytes": 8001,
          "rawPayload": "S6F12 (<BIN>0</BIN>)|TID:8001"
        },
        {
          "seq": 8,
          "utcTimestamp": "2026-08-01T00:18:13.555Z",
          "rawTimestamp": "20:18:13.555",
          "direction": "Rx",
          "sxFy": "S5F1",
          "sml": "S5F1 /* bus */ S5F1 (L {Bi System.Byte[]} {U4 77} {A LIVE SET})",
          "systemBytes": 8003,
          "rawPayload": "S5F1 (L {Bi System.Byte[]} {U4 77} {A LIVE SET})|TID:8003"
        },
        {
          "seq": 9,
          "utcTimestamp": "2026-08-01T00:18:13.615Z",
          "rawTimestamp": "20:18:13.615",
          "direction": "Tx",
          "sxFy": "S5F2",
          "sml": "S5F2 /* bus */ S5F2 (<BIN>0</BIN>)",
          "systemBytes": 8003,
          "rawPayload": "S5F2 (<BIN>0</BIN>)|TID:8003"
        },
        {
          "seq": 10,
          "utcTimestamp": "2026-08-01T00:18:13.647Z",
          "rawTimestamp": "20:18:13.647",
          "direction": "Rx",
          "sxFy": "S5F1",
          "sml": "S5F1 /* bus */ S5F1 (L {Bi System.Byte[]} {U4 77} {A LIVE CLEAR})",
          "systemBytes": 8004,
          "rawPayload": "S5F1 (L {Bi System.Byte[]} {U4 77} {A LIVE CLEAR})|TID:8004"
        },
        {
          "seq": 11,
          "utcTimestamp": "2026-08-01T00:18:13.708Z",
          "rawTimestamp": "20:18:13.708",
          "direction": "Tx",
          "sxFy": "S5F2",
          "sml": "S5F2 /* bus */ S5F2 (<BIN>0</BIN>)",
          "systemBytes": 8004,
          "rawPayload": "S5F2 (<BIN>0</BIN>)|TID:8004"
        },
        {
          "seq": 12,
          "utcTimestamp": "2026-08-01T00:18:58.5336041Z",
          "rawTimestamp": "2026-08-01T00:18:58.5336041Z",
          "direction": "Tx",
          "sxFy": "S1F3",
          "sml": "S1F3 /* bus */ S1F3 (L[2] {U4 1 U4 2})",
          "systemBytes": 4,
          "rawPayload": "S1F3 (L[2] {U4 1 U4 2})|TID:4"
        },
        {
          "seq": 13,
          "utcTimestamp": "2026-08-01T00:18:58.5368722Z",
          "rawTimestamp": "2026-08-01T00:18:58.5368722Z",
          "direction": "Rx",
          "sxFy": "S1F4",
          "sml": "S1F4 /* bus */ S1F4 (L[2] {U4 100 U4 101})",
          "systemBytes": 4,
          "rawPayload": "S1F4 (L[2] {U4 100 U4 101})|TID:4"
        }
      ]
    }
  8. 8
    get_connection_status

    Result

    {
      "connectionName": "mcp",
      "isConnected": true,
      "selectedState": "Selected",
      "ipAddress": "127.0.0.1",
      "port": 62238,
      "deviceId": 1,
      "mode": "Active",
      "timerConfig": {
        "t3ReplyMs": 45000,
        "t5ConnectSeparationMs": 10000,
        "t6ControlMs": 5000,
        "t7NotSelectedMs": 10000,
        "t8NetworkIntercharacterMs": 5000,
        "notes": "T3 enforced by HsmsHostConnection.SendMessageAndWaitForReply; T6 partially used for active TCP connect wait; T5/T7/T8 configured on ConnectionSettings but not fully enforced in the engine today."
      }
    }
  9. 9
    disconnect

    Result

    {
      "ok": false,
      "status": {
        "connectionName": "mcp",
        "isConnected": false,
        "selectedState": "NotSelected",
        "ipAddress": "127.0.0.1",
        "port": 62238,
        "deviceId": 1,
        "mode": "Active",
        "timerConfig": {
          "t3ReplyMs": 45000,
          "t5ConnectSeparationMs": 10000,
          "t6ControlMs": 5000,
          "t7NotSelectedMs": 10000,
          "t8NetworkIntercharacterMs": 5000,
          "notes": "T3 enforced by HsmsHostConnection.SendMessageAndWaitForReply; T6 partially used for active TCP connect wait; T5/T7/T8 configured on ConnectionSettings but not fully enforced in the engine today."
        }
      }
    }

Agent summary

  • Connected active HSMS to loopback; SELECTED.
  • Are-You-There returned equipment identity from S1F2.
  • Status variables: S1F11 ignored by sim → graceful numeric SVIDs (see nameResolutionNote).
  • Two rapid S6F11 events (CEID 2001 then 2002) observed with monotonic seq; sinceSeq filtered correctly.
  • Alarm ALID 77 set then clear observed with alarmSetOrClear true/false.
  • Trace shows Rx reports + Tx ConnectionHandler acks; no HSMS control frames.

Verdict: tool descriptions were sufficient to operate without clarifying questions.

Tool descriptions the agent saw (9)Expand
are_you_there

Send S1F1 (Are You There) and wait for S1F2. Returns MDLN and SOFTREV when the equipment supplies the common L[2] {A MDLN, A SOFTREV} body. Use this as a quick comms health check after connect. Requires an HSMS-SELECTED session.

connect

Open the single HSMS-SS session owned by this MCP server. Active mode dials ip:port and sends Select.req (typical host→equipment). Passive mode listens on ip:port for an incoming Select (equipment-side). deviceId is the HSMS session/device id in data-message headers (often 0 or 1). On success the link is SELECTED; on Select timeout the error explains common causes (wrong role, device id, or peer already bound). Replaces any prior MCP connection.

disconnect

Gracefully disconnect the MCP HSMS session (best-effort Separate.req then close). Returns the final connection status. Safe to call when already disconnected.

get_connection_status

Return the current HSMS connection status for this SecsHost MCP session: Selected/NotSelected, remote endpoint (ip/port), deviceId, active/passive mode, and the SEMI E37 timer budgets (T3/T5/T6/T7/T8) as configured by SecsService.Connect. Call this after connect/disconnect or when diagnosing timeouts.

get_recent_alarms

Return recent alarms (S5F1) from the typed EventBuffer. Buffers fill passively while connected — ConnectionHandler auto-acks with S5F2; MCP does not send acks. alarmSetOrClear is true when ALCD bit 7 indicates set, false for clear. rawSml is round-trippable SML. Optional sinceSeq for incremental polling.

get_recent_events

Return recent collection events (S6F11) from the typed EventBuffer. Buffers fill passively while connected — ConnectionHandler auto-acks with S6F12; MCP does not send acks and no subscription call is required. Each entry includes seq (for incremental polling via sinceSeq), ceidOrAlid (CEID), reportDataSummary, and rawSml — round-trippable SML suitable for send_secs_message-style replay. Optional sinceSeq returns only entries with seq >= sinceSeq.

get_status_variables

Query status variables via S1F3/S1F4. Optional svids is a JSON array of numeric SVIDs (e.g. [1001,1002]); omit or pass [] to request the equipment's default/empty list. Once per session this tool attempts S1F11/S1F12 name resolution and caches the map; if the equipment ignores S1F11, values are returned with numeric SVIDs and a note. Requires an HSMS-SELECTED session.

get_trace

Return the decoded SECS traffic log (ring buffer, default capacity 2000). Each entry has seq, utcTimestamp, direction (Tx=host→equipment, Rx=equipment→host), sxfy, sml-ish body, and systemBytes when known. Optional count limits how many recent entries to return; optional sinceSeq returns entries with seq >= sinceSeq. NOTE: HSMS control messages (Select/Linktest/Separate) do NOT appear here — ISecsTrafficBus only carries SECS-II data MessageSent events.

send_secs_message

Send any SxFy as standard SML and optionally wait for the secondary reply. This is the universal escape hatch when a typed GEM tool does not exist yet. SML grammar: 'S' stream 'F' function optional 'W' (reply expected), optional body items like <L [n] ...>, <A "text">, <U4 1>, <BOOLEAN TRUE>, then terminating '.'. Example (Are You There): S1F1 W. On success returns replySml (formatted via SmlFormatter). On T3 timeout returns structured diagnosis: which timer, connection state, lastTraceSeq. Malformed SML returns a parse error that names the offending token and character position.