Skip to content

[codex] Fix list_devices MCP schema#2988

Merged
Kitenite merged 1 commit into
mainfrom
bug-superset-list-devices-mcp-endpoint-returns-sch
Mar 29, 2026
Merged

[codex] Fix list_devices MCP schema#2988
Kitenite merged 1 commit into
mainfrom
bug-superset-list-devices-mcp-endpoint-returns-sch

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Mar 29, 2026

What changed

  • fixed the list_devices MCP tool so each returned device includes isOnline
  • added includeOffline to the tool input schema and implemented online/offline filtering in the handler
  • tightened the output schema to match the documented device contract, including enum-backed deviceType and ISO datetime validation for lastSeenAt
  • added a focused regression test covering schema registration plus online-only and offline-inclusive responses

Root cause

The MCP server registered list_devices with an output schema that did not match the behavior expected by callers and documented elsewhere in the repo. Downstream consumers expected isOnline, but the handler never returned it. That caused structured output validation to fail with data/devices/0 must have required property 'isOnline'.

Impact

  • list_devices no longer fails schema validation when called through the MCP endpoint
  • callers can discover device IDs again
  • includeOffline: true now behaves consistently instead of failing with the same schema error

Validation

  • bun test src/tools/devices/list-devices/list-devices.test.ts
  • bun run typecheck in packages/mcp
  • bunx biome check packages/mcp/src/tools/devices/list-devices/list-devices.ts packages/mcp/src/tools/devices/list-devices/list-devices.test.ts

Summary by cubic

Fixes the MCP list_devices tool to match the documented device contract so structured responses validate and device discovery works again. Adds online/offline handling and returns isOnline per device.

  • Bug Fixes
    • Return isOnline and default to online-only (last check-in within 60s); add includeOffline input to include all devices.
    • Tighten output schema: deviceType uses enum-backed deviceTypeValues, lastSeenAt is ISO datetime.
    • Add a regression test covering schema registration and online/offline responses.

Written for commit 063c700. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added includeOffline parameter to control visibility of offline devices (defaults to online only)
    • Devices now include an isOnline status indicator
    • Improved validation for device types and last-seen timestamps
  • Tests

    • Added comprehensive test suite for the list_devices tool

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

Added comprehensive test coverage and enhanced the list_devices MCP tool with a new includeOffline parameter. The tool now derives device online status from a 60-second activity cutoff and filters offline devices by default. Output schema refined to validate deviceType as an enum and lastSeenAt as ISO datetime.

Changes

Cohort / File(s) Summary
Test Coverage
packages/mcp/src/tools/devices/list-devices/list-devices.test.ts
New test suite with 182 lines covering list_devices tool registration and handler execution. Mocks MCP context and database client; validates input schema with includeOffline parameter, output schema conformance, and filtering behavior (online-only by default, both online/offline when includeOffline: true).
Device Listing Tool Implementation
packages/mcp/src/tools/devices/list-devices/list-devices.ts
Enhanced tool logic: added includeOffline boolean input parameter (defaults false); refined output schema with deviceType enum constraint, ISO datetime validation for lastSeenAt, and new isOnline boolean field; implemented 60-second activity cutoff to compute online status and filter offline devices.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A fuzzy friend hops through the code,
With devices online, a lighter load!
Sixty seconds to see who's alive,
Schema refined, the tests help us thrive!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[codex] Fix list_devices MCP schema' clearly and specifically summarizes the main change: fixing the schema for the list_devices MCP tool.
Description check ✅ Passed The description comprehensively covers the 'What changed', 'Root cause', 'Impact', and 'Validation' sections, exceeding the template requirements by providing detailed context and implementation details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bug-superset-list-devices-mcp-endpoint-returns-sch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 29, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ✅ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/mcp/src/tools/devices/list-devices/list-devices.ts (1)

42-55: Push default online filtering down to the DB query path.

On Lines 42-55 and Lines 57-67, offline filtering is done in memory after loading all devices. For larger orgs, this adds unnecessary DB/network and CPU cost. Consider applying lastSeenAt >= cutoff in SQL when includeOffline is false.

Suggested refactor
-import { desc, eq } from "drizzle-orm";
+import { and, desc, eq, gte } from "drizzle-orm";
...
-			const devices = await db
+			const devices = await db
 				.select({
 					deviceId: devicePresence.deviceId,
 					deviceName: devicePresence.deviceName,
 					deviceType: devicePresence.deviceType,
 					lastSeenAt: devicePresence.lastSeenAt,
 					ownerId: devicePresence.userId,
 					ownerName: users.name,
 					ownerEmail: users.email,
 				})
 				.from(devicePresence)
 				.innerJoin(users, eq(devicePresence.userId, users.id))
-				.where(eq(devicePresence.organizationId, ctx.organizationId))
+				.where(
+					includeOffline
+						? eq(devicePresence.organizationId, ctx.organizationId)
+						: and(
+								eq(devicePresence.organizationId, ctx.organizationId),
+								gte(devicePresence.lastSeenAt, new Date(onlineCutoff)),
+							),
+				)
 				.orderBy(desc(devicePresence.lastSeenAt));
...
-			const result = devices
+			const result = devices
 				.map((d) => {
 					const isOnline = d.lastSeenAt.getTime() >= onlineCutoff;
 
 					return {
 						...d,
 						lastSeenAt: d.lastSeenAt.toISOString(),
 						isOnline,
 					};
-				})
-				.filter((device) => includeOffline || device.isOnline);
+				});

Also applies to: 57-67

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mcp/src/tools/devices/list-devices/list-devices.ts` around lines 42
- 55, The query currently loads all devices then filters offline entries in
memory; instead when includeOffline is false compute a cutoff timestamp and push
the predicate into the DB query by adding a
.where(eq(devicePresence.organizationId, ctx.organizationId)) (or combine with
AND) plus a condition like devicePresence.lastSeenAt >= cutoff, and apply the
same change to the second query block that uses devicePresence and users so
filtering happens in SQL rather than in-memory; locate the queries that build
the select from devicePresence and users and add the cutoff condition when
includeOffline === false (use the existing variable names devicePresence, users,
includeOffline and the computed cutoff) so results are reduced at the DB level.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/mcp/src/tools/devices/list-devices/list-devices.ts`:
- Around line 42-55: The query currently loads all devices then filters offline
entries in memory; instead when includeOffline is false compute a cutoff
timestamp and push the predicate into the DB query by adding a
.where(eq(devicePresence.organizationId, ctx.organizationId)) (or combine with
AND) plus a condition like devicePresence.lastSeenAt >= cutoff, and apply the
same change to the second query block that uses devicePresence and users so
filtering happens in SQL rather than in-memory; locate the queries that build
the select from devicePresence and users and add the cutoff condition when
includeOffline === false (use the existing variable names devicePresence, users,
includeOffline and the computed cutoff) so results are reduced at the DB level.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 00fdd6b9-46b4-497d-b1da-319f479006a5

📥 Commits

Reviewing files that changed from the base of the PR and between 3c46f4f and 063c700.

📒 Files selected for processing (2)
  • packages/mcp/src/tools/devices/list-devices/list-devices.test.ts
  • packages/mcp/src/tools/devices/list-devices/list-devices.ts

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

@Kitenite Kitenite merged commit 1ac52d4 into main Mar 29, 2026
15 checks passed
siarhei-belavus pushed a commit to siarhei-belavus/localset that referenced this pull request Mar 30, 2026
saddlepaddle added a commit that referenced this pull request Apr 9, 2026
PR #2904 deliberately removed device heartbeat polling (desktop calls
device.registerDevice once at startup) because executeOnDevice already
handles unreachable devices via its command-poll timeout. PR #2988
reintroduced an isOnline field computed from a 60s lastSeenAt window
without restoring heartbeats, so every device looked offline ~60s after
app start — surfacing in the Slack integration agent as "status: offline".

Revert the isOnline reintroduction (keep #2988's schema hardening), drop
includeOffline from the input, update the Slack run-agent to stop
rendering the removed status, and refresh the docs + stubbed CLI flag.
Also fix a pre-existing mock-module bleed in list-devices.test.ts that
was stripping executeOnDevice from start-agent-session.test.ts.
saddlepaddle added a commit that referenced this pull request Apr 9, 2026
PR #2904 deliberately removed device heartbeat polling (desktop calls
device.registerDevice once at startup) because executeOnDevice already
handles unreachable devices via its command-poll timeout. PR #2988
reintroduced an isOnline field computed from a 60s lastSeenAt window
without restoring heartbeats, so every device looked offline ~60s after
app start — surfacing in the Slack integration agent as "status: offline".

Revert the isOnline reintroduction (keep #2988's schema hardening), drop
includeOffline from the input, update the Slack run-agent to stop
rendering the removed status, and refresh the docs + stubbed CLI flag.
Also fix a pre-existing mock-module bleed in list-devices.test.ts that
was stripping executeOnDevice from start-agent-session.test.ts.
MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 10, 2026
…et-sh#3299)

PR superset-sh#2904 deliberately removed device heartbeat polling (desktop calls
device.registerDevice once at startup) because executeOnDevice already
handles unreachable devices via its command-poll timeout. PR superset-sh#2988
reintroduced an isOnline field computed from a 60s lastSeenAt window
without restoring heartbeats, so every device looked offline ~60s after
app start — surfacing in the Slack integration agent as "status: offline".

Revert the isOnline reintroduction (keep superset-sh#2988's schema hardening), drop
includeOffline from the input, update the Slack run-agent to stop
rendering the removed status, and refresh the docs + stubbed CLI flag.
Also fix a pre-existing mock-module bleed in list-devices.test.ts that
was stripping executeOnDevice from start-agent-session.test.ts.
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