Skip to content

Add read-only Yamcs mission control action - #1265

Merged
Paul Lizer (paullizer) merged 2 commits into
Developmentfrom
paullizer-yamcs-plugin-action
Aug 18, 2026
Merged

Paul Lizer (paullizer) merged 2 commits into
Developmentfrom
paullizer-yamcs-plugin-action

Conversation

@paullizer

Copy link
Copy Markdown
Contributor

Summary

Adds a first-class, read-only yamcs action type that connects SimpleChat agents to a Yamcs mission control server using the official yamcs-client Python package.

The action gets its own configuration panel in the Add/Edit Action modal — including a Test Yamcs Connection button — rather than reusing the generic endpoint/auth form. It follows the existing Snowflake and Tableau full-stack patterns, and where those two differ it follows Tableau, since Yamcs is addressed by a real server URL rather than a synthetic endpoint constant.

Version bumped 0.250.2080.250.209.

Read-only by design

Yamcs supports commanding, parameter writes, script execution, and data link control. None of that is exposed. The guarantee is enforced in three independent places:

  1. Only read functions are declared in get_functions() and plugin metadata, so no write operation is ever offered to an agent.
  2. additionalFields.read_only is forced to true during manifest normalization and cannot be flipped from a stored manifest.
  3. The optional archive SQL function is disabled by default and, when enabled, is allowlisted to SELECT / SHOW / DESC / DESCRIBE with a forbidden-keyword guard and an automatic row limit.

list_commands returns mission database command definitions only and never issues a command. The plugin description states this explicitly so the model does not assume otherwise.

Functions

Function Purpose
list_instances Yamcs instances on the server
list_links Data links with status and packet counts
list_parameters MDB parameters, filterable by name text or type
describe_parameter One parameter: type, units, enumerations
list_commands Command definitions only
get_parameter_values Latest processed values
list_parameter_history Archived values over a time range
list_events Archived events by severity/source/text
list_packets Archived packet metadata
list_alarms Archived alarms
execute_archive_sql Guarded read-only archive SQL; off by default

Authentication

UI method auth.type Yamcs credential
Username and Password username_password Credentials(username=..., password=...)
API Key key APIKeyCredentials(key)x-api-key header
Bearer Token key Credentials(access_token=...)
No Authentication NoAuth None
Reusable Identity identity resolved from the identity auth type

Reusable identities are accepted for api_key, bearer_token, and username_password.

Test Connection

POST /api/plugins/test-yamcs-connection verifies reachability, validates credentials, and confirms the configured instance actually exists — reporting the Yamcs version and available instance count. When editing a saved action it resolves the stored credential from Key Vault, so the secret does not need re-entering to run a test. Failures map 401/403 → 403 and 404 → 404 with credential-scrubbed messages.

Two real bugs caught during verification

1. urlparse("host:8090") misreads the hostname as a URL scheme. My first normalizer relied on urlparse().scheme to decide whether to prepend https://. Because host:port is the normal Yamcs address form, urlparse parsed yamcs.example.com as the scheme, so the check passed and no scheme was added. The Yamcs client then defaults a schemeless address to plain HTTP — a silent TLS downgrade. Fixed with an explicit ^https?:// check. (Tableau's normalizer shares this pattern but isn't exposed to it, since Tableau URLs rarely carry ports. Left alone as out of scope.)

2. APIKeyCredentials stores the key on .password, not .key. Confirmed against the real library; the test fake was corrected so it can't encode a wrong assumption about the API surface.

Verified against the real library, not just fakes

Installed yamcs-client==2.1.0 in a throwaway venv and confirmed:

  • All three imports resolve (YamcsClient, Credentials, APIKeyCredentials)
  • The constructor accepts a full URL plus the kwargs the plugin passes, with no network I/O
  • ctx.session.request exists, so the timeout wrapper is valid
  • All four credential paths produce correct auth behavior (x-api-key header, Authorization: Bearer, lazy login, None)
  • Protobuf coexistence: yamcs-client vendorizes protobuf 7.35 internally, verified to work alongside the repo's pinned protobuf==6.33.5

⚠️ Licensing — needs a compliance look

yamcs-client is LGPL-3.0, the first LGPL dependency in this repository (Snowflake is Apache-2.0, tableauserverclient is MIT). It is consumed as an unmodified, dynamically linked pip dependency, which is the standard-compliant usage pattern — but flagging it explicitly so it's a conscious decision rather than something that slips through. Documented in both the feature doc and the release notes under Breaking Changes.

Testing

Suite Result
functional_tests/test_yamcs_action_plugin.py (new) 14/14 pass
functional_tests/route_tests/ (3 files) 12/12 pass
Snowflake + Tableau regression 9/9 pass

The new test covers normalization and clamping, factory manifest handling, metadata and the absence of write functions, the full auth mapping matrix, construction-time validation failures, health-checker validation for each failure mode, the reusable identity contract, all read-only retrievals, argument validation, the archive SQL default gate and read-only guard, row/byte truncation, secret redaction, and missing-dependency handling.

The new route is covered by the existing path-prefix policy rules in route_tests, so no test-data edits were needed there. Plugin discovery was separately confirmed to surface yamcs in the action-type picker. Python compile, JS syntax, JSON, and Jinja template checks are all clean.

Files

New (7): functions_yamcs_operations.py, semantic_kernel_plugins/yamcs_plugin.py, semantic_kernel_plugins/yamcs_plugin_factory.py, two JSON schemas, docs/explanation/features/YAMCS_ACTION.md, functional_tests/test_yamcs_action_plugin.py

Modified (14): both kernel loaders, health checker, Key Vault redaction, workspace identities, governance aliases/labels, route_backend_plugins.py, _plugin_modal.html, plugin_modal_stepper.js, view-utils.js, requirements.txt, config.py, features index, release notes

Notes for reviewers

  • Streaming/subscription APIs are intentionally not exposed, since agent tool calls are request/response.
  • Testing a connection that uses a reusable identity requires saving the action first; the UI states this.
  • No associated GitHub issue exists yet — happy to open one and link it if preferred.

Paul Lizer (paullizer) and others added 2 commits August 17, 2026 21:09
Adds a first-class `yamcs` action type built on the official `yamcs-client`
package, with its own configuration panel and Test Connection button in the
Add/Edit Action modal.

Exposes eleven read-only Semantic Kernel functions: list_instances,
list_links, list_parameters, describe_parameter, list_commands,
get_parameter_values, list_parameter_history, list_events, list_packets,
list_alarms, and a gated execute_archive_sql.

The action is strictly read-only. It cannot issue commands, set parameter
values, run scripts, or change data link state. This is enforced three ways:
only read functions are declared, `read_only` is forced true during manifest
normalization, and archive SQL is disabled by default and allowlisted to
SELECT/SHOW/DESC/DESCRIBE with a forbidden-keyword guard when enabled.
`list_commands` returns mission database definitions only.

Supports username/password, API key, bearer token, and unauthenticated Yamcs
servers, plus reusable workspace identities. Results are bounded by row count,
serialized byte limit, and request timeout, and error text is scrubbed of
credentials before being returned or logged.

Notes:
- `yamcs-client==2.1.0` is LGPL-3.0, the first LGPL dependency in this repo.
  It is consumed as an unmodified, dynamically linked pip dependency.
- It vendorizes its own protobuf runtime, verified not to conflict with the
  pinned protobuf==6.33.5.
- Server URL normalization detects the scheme with an explicit ^https?://
  check rather than urlparse, because urlparse misreads the common Yamcs
  `host:port` form as a URL scheme and the Yamcs client would then default a
  schemeless address to plain HTTP.

Validation:
- functional_tests/test_yamcs_action_plugin.py (14/14)
- functional_tests/route_tests (12/12)
- Snowflake and Tableau regression suites (9/9)
- Credential mapping, client constructor, and protobuf coexistence verified
  against a real yamcs-client==2.1.0 install

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Resolves the version and release notes conflicts created by Development
advancing to 0.250.211 while this branch was open.

Conflict resolution:
- config.py: VERSION moved from 0.250.209 to 0.250.212, the next version
  after Development's 0.250.211.
- release_notes.md: the Yamcs section is retitled to v0.250.212 and kept at
  the top, with Development's v0.250.211, v0.250.210, and v0.250.209 sections
  preserved below it in descending order.
- features/index.md auto-merged; both Development's additions and the Tableau
  and Yamcs links are retained.

Also updated the version references this branch owns so they match the new
version: YAMCS_ACTION.md header, config.py reference, and known-limitations
note, plus the functional test header and its assert_app_version_at_least
floor.

Validation after merge:
- functional_tests/test_yamcs_action_plugin.py (14/14)
- functional_tests/route_tests (12/12)
- Development's incoming suites: backup cosmos pagination (5/5), workspace
  section order (6/6), endpoints tab order (4/4), chat document search
  filename matching (8/8), chat voice turn response (pass)
- Python compile, JS syntax, and Jinja template checks clean

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) merged commit db0061b into Development Aug 18, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant