Skip to content

feat: migrate covered tools to go-flashduty SDK (dual-client transition)#61

Merged
ysyneu merged 6 commits into
mainfrom
feat/migrate-go-flashduty
May 31, 2026
Merged

feat: migrate covered tools to go-flashduty SDK (dual-client transition)#61
ysyneu merged 6 commits into
mainfrom
feat/migrate-go-flashduty

Conversation

@ysyneu
Copy link
Copy Markdown
Collaborator

@ysyneu ysyneu commented May 30, 2026

What

Migrate the MCP server's tools from the hand-written flashduty-sdk onto the new generated go-flashduty SDK — first half of consolidating onto one SDK.

20 of 23 tools (whose endpoints go-flashduty v0.3.0 covers) now call go-flashduty. A dual client keeps the rest working during the transition:

  • client.New — go-flashduty (backs every migrated tool)
  • client.Legacy — flashduty-sdk (only the tools below)

Still on Legacy (pending go-flashduty coverage via api-review)

Tool Endpoint Why
query_changes /change/list not in go-flashduty yet
query_status_pages /status-page/list not in go-flashduty yet
list_status_changes /status-page/change/active/list go-flashduty's ChangeList hits the general /status-page/change/list (different semantics + status required)
validate_template /template/preview not in go-flashduty yet

Each marked with a TODO to switch + drop the flashduty-sdk dep once covered.

Also

  • TOON output moved off sdk.Marshal onto github.com/toon-format/toon-go directly (go-flashduty is a thin client).
  • Dropped humanizeTimestamps() for covered tools — go-flashduty v0.3.0 emits RFC3339 via typed Timestamp. Kept for Legacy-backed tools.

Verification

  • go build + go vet + go test green.
  • In-process e2e (FLASHDUTY_E2E_DEBUG=1) 15/15 PASS against api-dev: incident lifecycle (create/update/ack/close), queries (incidents/channels/members/teams/fields), timeline, name filters, changes.
  • Docker e2e skipped locally (container build times out on this host's network); CI e2e covers it.

Status

Draft until go-flashduty covers the 4 pending endpoints, after which the Legacy client + flashduty-sdk dependency are removed.

ysyneu added 6 commits May 29, 2026 00:03
Flashduty's API returns time fields as bare Unix integers, which are opaque to
an LLM reading tool results. Humanize them to RFC3339 (in the local timezone)
in MarshalResultWithFormat so every tool response is readable. Detection is by
field name (*_time, *_at, timestamp), excluding ID-like fields; values below
1e9 stay numeric. Drop the unused, humanization-bypassing MarshalledTextResult.
Migrate the 20 tools whose endpoints go-flashduty v0.3.0 covers from the
hand-written flashduty-sdk onto go-flashduty. A dual client (New = go-flashduty,
Legacy = flashduty-sdk) keeps the 4 tools whose endpoints go-flashduty does not
cover yet working, each marked TODO to switch once covered:
query_changes (/change/list), query_status_pages (/status-page/list),
list_status_changes (/status-page/change/active/list), validate_template
(/template/preview).

- internal/flashduty: dual-client construction (New + Legacy).
- pkg/flashduty: covered tools call client.New.<Service>.<Method>; batch ops use
  go-flashduty batch request fields (Ack/Resolve), per-incident loops for Feed/
  AlertList; typed-envelope response unwrapping.
- TOON output moved off sdk.Marshal onto github.com/toon-format/toon-go directly.
- Dropped humanizeTimestamps() for covered tools (go-flashduty v0.3.0 emits
  RFC3339 via typed Timestamp); kept for Legacy-backed tools.

Verified: go build + vet + go test green; in-process e2e (FLASHDUTY_E2E_DEBUG)
15/15 PASS against api-dev (incident lifecycle, queries, timeline, filters).
…to go-flashduty

Bump go-flashduty to v0.4.0 (documents /change/list, /status-page/list,
/template/preview) and migrate the last three unblocked tools off the
hand-written flashduty-sdk (client.Legacy) onto the generated client
(client.New):

- query_changes: client.Legacy.ListChanges -> client.New.Changes.List
  (ListChangeRequest/ListChangeResponse). change_ids has no server-side
  filter on /change/list, so the direct-lookup param is honored by
  filtering the returned page client-side; switched to MarshalResult since
  ChangeItem uses the typed Timestamp (RFC3339) instead of bare Unix ints.
- query_status_pages: client.Legacy.ListStatusPages ->
  client.New.StatusPages.ReadPageList. The new endpoint lists all pages and
  takes no filter param, so the optional page_ids filter is applied
  client-side over Items.
- validate_template: client.Legacy.ValidateTemplate ->
  client.New.NotificationTemplates.ReadPreview (PreviewTemplateRequest/
  Response). The raw endpoint only renders + reports parse errors, so the
  size-limit validation (rendered_size/size_limit/errors/warnings,
  telegram/teams_app special cases) that the legacy SDK folded in is
  reproduced in the handler, keeping the output shape identical.

Stays on Legacy (flashduty-sdk dependency retained):
- list_status_changes (ListStatusChanges) -> /status-page/change/active/list,
  still a documented gap; go-flashduty's ChangeList hits the general
  /status-page/change/list with different (status+window) semantics.
- update_incident custom-field-names path (UpdateIncident) -> shape-divergent;
  /incident/reset does not carry custom fields.

Verified: go build, go vet, go test, golangci-lint, gofmt all clean;
e2e (in-process vs api-dev) 15/15 pass incl. TestQueryChanges.
The MCP server was dual-client (typed go-flashduty + hand-written flashduty-sdk).
The remaining three legacy-backed tools are now on go-flashduty v0.5.1, so the
legacy SDK and its dual-client scaffolding are removed entirely.

- statuspage list_status_changes: Legacy.ListStatusChanges -> StatusPages.ChangeActiveList
  (/status-page/change/active/list; no list-level total, count = len(items)).
- templates: vendor the static template metadata (channels, size limits, variable
  and function catalogs) into pkg/flashduty/templatemeta.go; the data is
  client-side authoring reference, not an API surface, so it lives in-repo.
- incidents update_incident custom fields: Legacy.UpdateIncident -> Incidents.FieldReset,
  unblocked by go-flashduty v0.5.1 typing field_value as any (was map[string]any).
- Remove Clients.Legacy, the legacy client construction in context.go, the
  Legacy.SetUserAgent call in server.go, and the dead getAPIClient e2e helper.
- Drop MarshalLegacyResult + humanizeTimestamps: they were the legacy-only stopgap
  for bare Unix-int time fields; go-flashduty's self-describing Timestamp type
  renders RFC3339 on the typed path (the code's own TODO predicted this removal).
- go mod tidy drops github.com/flashcatcloud/flashduty-sdk.

build/vet/test/lint all green; zero remaining flashduty-sdk/client.Legacy refs.
- go-flashduty v0.5.2: named string enums implement fmt.Stringer, fixing
  --output-format toon for timeline/feed event Type fields.
- query_incident_timeline now returns the raw IncidentFeedItem shape
  (created_at RFC3339, numeric creator_id) instead of the legacy enriched
  shape. Update the e2e test to assert this contract (it previously declared
  the old {timestamp int64} shape and only checked non-empty, masking the
  change) and document creator_id resolution via query_members in the tool
  description.
@ysyneu ysyneu marked this pull request as ready for review May 31, 2026 12:05
@ysyneu ysyneu merged commit c500265 into main May 31, 2026
13 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