Skip to content

feat(mcp): device group read tools - #7719

Merged
cstns merged 6 commits into
feat/extend-mcp-tool-feature-setfrom
feat/mcp-tools-device-groups-read
Aug 26, 2026
Merged

feat(mcp): device group read tools#7719
cstns merged 6 commits into
feat/extend-mcp-tool-feature-setfrom
feat/mcp-tools-device-groups-read

Conversation

@andypalmi

@andypalmi andypalmi commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Add Phase 1 read-only MCP tools for fleet device groups, per #7699.

Tools added (forge/ee/lib/mcp/tools/deviceGroups.js):

  • platform_list_team_device_groups - lists the device groups across all applications in a team. GET /api/v1/teams/:teamId/device-groups
  • platform_list_application_device_groups - lists the device groups belonging to a single application. GET /api/v1/applications/:applicationId/device-groups
  • platform_get_application_device_group - GET /api/v1/applications/:applicationId/device-groups/:groupId

All routes require the deviceGroups team feature; when disabled they return the existing 404 gate response.

Write, membership, settings, and delete tools are tracked separately in sub-issues #7700 and #7701 and are not part of this PR.

Permissions:
#8185 removed the hardcoded user:expert-mcp scope allow-list, so expert MCP tokens now inherit the user's permissions gated by team role and no allow-list entries are needed.

Closes #7699

Test plan

  • eslint on changed files
  • mocha unit tests for the tool file

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.96%. Comparing base (d3419bb) to head (cc70506).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7719      +/-   ##
==========================================
+ Coverage   75.94%   75.96%   +0.01%     
==========================================
  Files         445      446       +1     
  Lines       23990    24004      +14     
  Branches     6404     6404              
==========================================
+ Hits        18220    18234      +14     
  Misses       5770     5770              
Flag Coverage Δ
backend 75.96% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from 9420520 to 6d62562 Compare July 4, 2026 13:33
@andypalmi andypalmi linked an issue Jul 6, 2026 that may be closed by this pull request
@andypalmi
andypalmi force-pushed the feat/mcp-tools-shared-schemas branch from 8bfb22e to 9742987 Compare July 6, 2026 13:13
@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from 6d62562 to d1583af Compare July 6, 2026 16:38
@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from d1583af to f462226 Compare July 6, 2026 22:01
@andypalmi
andypalmi marked this pull request as draft July 30, 2026 08:20
@andypalmi
andypalmi force-pushed the feat/mcp-tools-shared-schemas branch from 103f9ae to 5773bdb Compare August 3, 2026 13:19
Add forge/ee/lib/mcp/schemas.js, a shared module of composable zod
fragments the platform read tools import instead of redefining entity-id
and pagination/search/sort/audit-log query fields in each tool file.

- entity-id params: teamId, applicationId, hostedInstanceId (UUID),
  remoteInstanceId, snapshotId
- query fragments composed per route by spreading only the params the
  backing finder honors: cursorParam/limitParam (basePagination),
  pageParam, searchQuery, sortParams, auditLogFilters
- appendQuery serialises a tool's supported params onto the request URL

The module lives one level above tools/ so the tool loader does not
register it as a tool module.

Closes #7669
@andypalmi
andypalmi force-pushed the feat/mcp-tools-shared-schemas branch from eae081f to e04360f Compare August 3, 2026 14:13
@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from f462226 to 5b86464 Compare August 3, 2026 15:26
@andypalmi
andypalmi requested a review from cstns August 3, 2026 15:47
@andypalmi andypalmi self-assigned this Aug 3, 2026
@andypalmi
andypalmi marked this pull request as ready for review August 3, 2026 15:47
@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from 5b86464 to 5265992 Compare August 3, 2026 15:56
@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from 5265992 to 8833422 Compare August 4, 2026 10:52
@andypalmi
andypalmi force-pushed the feat/mcp-tools-device-groups-read branch from 8833422 to 972c237 Compare August 4, 2026 11:00
Base automatically changed from feat/mcp-tools-shared-schemas to main August 5, 2026 08:48
Comment thread forge/ee/lib/mcp/tools/deviceGroups.js Outdated
andypalmi added 2 commits August 19, 2026 17:23
Replace the single platform_list_device_groups tool, which took an
optional teamId or applicationId and routed to whichever was set, with
two tools: platform_list_team_device_groups and
platform_list_application_device_groups. The team and application
device-group endpoints are separate, with different scopes and a
different result shape, and the merged tool relied on an unenforced
'exactly one of teamId or applicationId' contract that could otherwise
build a request against an undefined id.

Also correct the descriptions: the routes return a plain not-found when
the deviceGroups feature is disabled, not the descriptive message the
tools previously claimed.
…-groups-read

# Conflicts:
#	forge/routes/auth/permissions.js
@andypalmi

Copy link
Copy Markdown
Contributor Author

Testing results of new/changed tools

@cstns for visibility.

The three tools in this PR were invoked against a running local platform. The context the calls ran against:

  • A team with the deviceGroups team-type feature enabled, and a sibling team without it.
  • An application in that team with one device group, fully configured: a target snapshot, two member devices, and a settings block with one visible and one hidden environment variable.

Payloads below are redacted for ids, names and secrets; structural values (counts, statuses, and which fields are present) are verbatim.

platform_list_team_device_groups

Lists the device groups across all applications in a team.

Case Input params Verdict Returned payload (redacted)
team with one group {teamId: TEAM_ID, limit: 10} PASS {meta:{}, count:1, groups:[{id, name, description, deviceCount:2, targetSnapshot:{id,name,description,createdAt}, application:{id,name,description,links}}]}
unknown team {teamId:"doesnotexist", limit:10} PASS 404 isError, {code:"not_found"}
team without the deviceGroups feature {teamId: TEAM_ID_2, limit:10} PASS 404 isError, {code:"not_found"} - matches the tool's documented behaviour for a disabled feature

platform_list_application_device_groups

Lists the device groups belonging to a single application.

Case Input params Verdict Returned payload (redacted)
application with one group {applicationId: APP_ID, limit:10} PASS {meta:{}, count:1, groups:[{id, name, description, deviceCount:2, targetSnapshot:{...}}]}
query matching the group name {applicationId: APP_ID, limit:10, query:"harness"} PASS same single group returned
query matching nothing {applicationId: APP_ID, limit:10, query:"zzz-nomatch"} PASS {meta:{}, count:1, groups:[]} - matches the raw route byte-for-byte; the route's own count does not reflect the query filter, which is a pre-existing characteristic of the underlying endpoint, not something the tool introduces

platform_get_application_device_group

Fetches a single device group in an application, including its members and target snapshot.

Case Input params Verdict Returned payload (redacted)
existing group, fully configured {applicationId: APP_ID, groupId: GROUP_ID} PASS full detail including 2 devices (each with id, status, onlineStatus, mode, ownerType, name, type, activeSnapshot, targetSnapshot, links), settings.env with the hidden variable's value blanked, targetSnapshot, application
unknown group {applicationId: APP_ID, groupId:"doesnotexist"} PASS 404 isError, {code:"not_found"}

Findings

  • All three tools pass the route response through unchanged: each tool's output matched its corresponding raw REST route response byte-for-byte for the same parameters.
  • platform_get_application_device_group correctly surfaces the hidden environment variable's name and hidden flag while blanking its value, matching the underlying route's masking behaviour.
  • A team with the deviceGroups feature disabled returns a 404 from platform_list_team_device_groups, matching the tool's documented plan-gating behaviour.

Conclusion

All three tools verified against their descriptions, including feature-gating and the environment variable secret-masking guarantee. No open defects.

@cstns
cstns changed the base branch from main to feat/extend-mcp-tool-feature-set August 26, 2026 07:36
@cstns
cstns merged commit 57891a5 into feat/extend-mcp-tool-feature-set Aug 26, 2026
29 checks passed
@cstns
cstns deleted the feat/mcp-tools-device-groups-read branch August 26, 2026 07:38
@cstns
cstns deployed to staging August 26, 2026 07:38 — with GitHub Actions Active
@cstns cstns mentioned this pull request Aug 26, 2026
11 tasks
andypalmi added a commit that referenced this pull request Aug 26, 2026
## Description

Feature branch for extending mcp tools

## Related Issue(s)

#7727
#7726
#7724
#7723
#7722
#7721
#7720
#7719
#7718
#7717
#7716
#7715


Co-authored-by: andypalmi <andrea@flowfuse.com>
Co-authored-by: Andrea Palmieri <76187074+andypalmi@users.noreply.github.com>
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.

5.6-a Read tools (phase 1)

2 participants