8.3 KiB
2026-07-06 — ScreenConnect skill: full-fleet listing fix
User
- User: Howard Enos (howard)
- Machine: Howard-Home
- Role: tech
Session Summary
Fixed the screenconnect skill's reported inability to list the full agent fleet.
The prior state believed the RESTful API Manager extension on the ACG instance
(computerguru.screenconnect.com) exposed no full-fleet enumeration method — only
GetSessionsByName, an exact-name lookup — and the SKILL.md/reference docs framed
this as a gap needing Mike to update the extension.
That premise was wrong about the cause. Empirical probing of the live extension
found that GetSessionsByFilter does exist; earlier probing had simply never
tried it. It was distinguishable because it returned a different error
(InvalidOperationException: "Session filter cannot be null") than the
"web method does not exist" every other candidate returned. Discovery of its
signature followed: the parameter is named sessionFilter (not filter), it takes
a ScreenConnect session-filter expression, and string literals are single-quoted.
SessionType = 'Access' returns all 958 unattended agents; SessionType = 'Support'
returns the 24 on-demand sessions; CustomProperty1 = '<Company>' does per-client
listing; Name LIKE '*<substr>*' does partial-name search; AND/OR compose.
Implemented the fix across the client (get_sessions_by_filter, a
build_session_filter builder, list_sessions convenience), the CLI (sessions
now defaults to the whole Access fleet, with new --like, --company, --site,
--tag, --type, --filter, --limit flags; status reports real fleet counts),
the self-test (added coverage), and both doc files (documented the filter language,
removed the stale "MISSING / needs extension update" framing).
Ran /code-review at high effort via the workflow backend. It caught three real
regressions I had introduced plus four robustness/quality issues. All seven were
fixed and re-verified live. The self-test grew from the original set to 19 checks,
all passing against the live instance. The repo's background auto-sync committed and
pushed the changes (commits 7dc5198 then 12c3d83); main is level with
origin/main.
Key Decisions
- Probe the live extension rather than trust the documented gap. The "GetSessions
unavailable" note was a conclusion from incomplete probing; treating the API surface
as empirically discoverable (not fixed) surfaced
GetSessionsByFilter. - Route full-fleet listing through a filter builder, not ad-hoc strings. A
build_session_filter(**parts)keeps CLI flags declarative and centralizes literal escaping. sessionsdefaults to the whole Access fleet. The headline use case is "show me every agent"; a baresessionsnow does that (958), with--company/--like/etc. narrowing.- A targeted selector defaults
--typetoall, a bare listing defaults toaccess. This restores the old type-agnostic by-name behavior (Support/Meeting sessions resolve) while keeping the fleet default meaningful. statusauth check stays on the cheapGetSessionsByName(''); fleet counts are best-effort. Keepsstatusfast and functional even on an extension version lackingGetSessionsByFilter.- Kept SQL-style
''quote-doubling for escaping after verifying it round-trips live (matched "Andy's Mobile Fuel").
Problems Encountered
- Full-fleet listing believed impossible. Root cause:
GetSessionsByFilternever probed. Resolved by discovering the method, itssessionFilterparam name, and theSessionType = 'Access'match-all form. GetSessionsByName('')returns only blank-name sessions (3), not the fleet. Confirmed it is an exactName ==match, so it cannot be coaxed into listing — hence the filter method is required.- Three regressions from the first-pass rewrite (caught by
/code-review): (1)--limittruncated--json, breaking the "json is always full" contract — fixed so--limitonly caps the human-readable print; (2)--type allfell through to the Access-only default — fixed with an(SessionType = 'Access' OR SessionType = 'Support')clause (verified 982 = 958 + 24); (3)--namelost type-agnostic lookup — fixed via context-aware--typedefaulting. All re-verified live. statuswas downloading the full fleet twice just to count, and would fail if the filter method were absent. Decoupled the auth check (cheapGetSessionsByName) from best-effort fleet counting wrapped in try/except./tmpwrite blocked by the harness hook during a shell sanity check (expected Windows/tmprule); switched to a repo-relative scratch path.
Configuration Changes
Modified (all under .claude/skills/screenconnect/):
scripts/sc_client.py— addedget_sessions_by_filter(),_filter_literal(),build_session_filter(),list_sessions(); addedSESSION_TYPE_LITERALS; updated module/method docstrings.scripts/sc.py—cmd_statusdecoupled (cheap auth + best-effort counts);cmd_sessionsreworked with_effective_type(), limit-only-on-print; added--like/--company/--site/--tag/--type/--filter/--limitflags; updated usage docstring.scripts/selftest.py— restoredAuthenticatedassertion, added--type all,--like,--filter,--company,--limit, and a "limit does not truncate json" check (out_json_min_len). Now 19 checks.SKILL.md— documentedGetSessionsByFilteras the enumeration path + CLI examples; replaced the "MISSING, needs Mike" framing with "absent aliases, not needed".references/api-reference.md— addedGetSessionsByFilterrow + a session-filter language section.
Credentials & Secrets
No new credentials. API secret unchanged: SOPS vault msp-tools/screenconnect.sops.yaml
field credentials.api_secret (env override SCREENCONNECT_API_SECRET). Auth remains
two headers — CTRLAuthHeader: <raw api_secret> (no "Basic" prefix) and
Origin: https://computerguru.screenconnect.com.
Infrastructure & Servers
- Instance:
https://computerguru.screenconnect.com - Extension GUID:
2d558935-686a-4bd0-9991-07539f5fe749(RESTful API Manager) - Endpoint:
POST <instance>/App_Extensions/<guid>/Service.ashx/<Method> - Fleet snapshot (2026-07-06): 958 Access (unattended agents) + 24 Support = 982.
- Custom properties: CP1=Company, CP2=Site, CP3=Tag (up to CP8; CP4 seen holding a role like Server/Desktop, CP5 "Syncro-Matched" on some records).
- SessionType literals:
Access= 2,Support= 0,Meeting= none present.
Commands & Outputs
SC="bash .claude/scripts/py.sh C:/claudetools/.claude/skills/screenconnect/scripts/sc.py"
# full fleet (958), per-client, partial name, all types
$SC sessions
$SC sessions --company "Ridgetop Group" # 14
$SC sessions --like DELL --limit 3
$SC sessions --type all --json | ... # 982
# the discovery: filter method exists, param is sessionFilter
$SC raw --method GetSessionsByFilter --body '{"sessionFilter":"SessionType = '"'"'Access'"'"'"}'
Key probe results:
GetSessionsByFilter {}->InvalidOperationException: "Session filter cannot be null"(method EXISTS).{"filter":""}->"Unknown parameter: filter";{"sessionFilter":"x"}->[](valid, matched none).SessionType = 'Access'-> 958;'Support'-> 24;'Access' OR 'Support'-> 982.CustomProperty1 = 'Safesite'-> 62;Name LIKE '*DELL*'-> 26.- Escaping verified:
CustomProperty1 LIKE '*Andy''s*'-> 1 (matched "Andy's Mobile Fuel").
Self-test: 19/19 passed, 0 failed against the live instance.
Pending / Incomplete Tasks
- None for the skill itself. The GuruRMM addon plan it prototypes is unchanged and still gated on Mike's go (SPEC-024, RMM_THOUGHTS Feature 7 / Refinement 7a).
- Unrelated working-tree drift left untouched:
projects/dataforth-dos,projects/discord-bot,projects/msp-tools/guru-rmm,projects/msp-tools/security-assessment(submodule pointers),projects/radio-show(untracked). Not part of this work.
Reference Information
- Commits:
7dc5198(initial fix) and12c3d83(review fixes) — both auto-sync commits from HOWARD-HOME;main==origin/main. - Skill dir:
.claude/skills/screenconnect/ - Filter language ref:
.claude/skills/screenconnect/references/api-reference.md(session-filter section).