Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ type ISessionManager = {

Core captures `sessionManager.getPlatformDriver?.()` before calling the consumer's `cleanup()`, then calls the driver's optional `dispose()` hook. Disposal runs even when consumer cleanup fails. This applies to `mm cleanup`, forced relaunch, failed launch, daemon shutdown, and server startup rollback. A failed disposal fails that cleanup call and is retained for a later attempt. The daemon owns that retention (one per `createServer` instance), so a later `mm cleanup`, forced relaunch, or daemon stop retries it, whatever wrapper of the session manager a tool receives. Only the current session's cleanup and disposal can fail a call. Drivers retained from earlier sessions are retried best-effort and in parallel. When they fail again, the daemon writes a `[WARN]` line to its log, and the current cleanup or launch continues. Tools reach this retention through `ToolContext.cleanupSession()`; without it (a context built outside the daemon), the tools fall back to retention per session-manager object.

`MobilePlatformDriver.dispose()` delegates to its backend's optional `dispose()` hook. With a device-mcp version that implements private IDB companions, this stops only that backend's companion and recording. Older device-mcp versions remain compatible, but do not gain companion teardown. Adopt the device-mcp lifecycle release in the consumer after it is published; this change does not update the core dependency range.
`MobilePlatformDriver.dispose()` delegates to its backend's optional `dispose()` hook. Core requires `@metamask/device-mcp` 0.7.0 or later. On iOS, the device-mcp `IdbBackend` uses a private IDB companion per backend, and disposal stops only that backend's companion and recording. The ADB backend has no disposal hook, so disposal does nothing on Android. The consumer must create its backend from device-mcp 0.7.0 or later to get companion teardown.

Consumers must expose a session-owned driver through `getPlatformDriver()` and create a fresh driver/backend for each launch. If launch fails before the driver is exposed, the consumer must dispose that locally held driver in its own failure path. Direct calls to a consumer's `cleanup()` also remain the consumer's responsibility.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"hono": "^4.12.25"
},
"dependencies": {
"@metamask/device-mcp": "^0.6.0",
"@metamask/device-mcp": "^0.7.0",
"cosmiconfig": "^9.0.0",
"express": "^5.2.1",
"zod": "^4.4.3"
Expand Down
2 changes: 1 addition & 1 deletion src/platform/mobile-platform-driver.dispose.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('MobilePlatformDriver disposal', () => {
expect(disposeB).toHaveBeenCalledTimes(1);
});

it('supports device-mcp versions without disposal', async () => {
it('completes for backends without a disposal hook', async () => {
const driver = new MobilePlatformDriver({
platform: 'ios',
} as DeviceBackend);
Expand Down
21 changes: 11 additions & 10 deletions src/platform/mobile-platform-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,18 @@ export class MobilePlatformDriver implements IPlatformDriver {
this.#excludeDevOverlay = options.excludeDevOverlay ?? true;
}

/** Release only this backend's resources; compatible with older device-mcp releases. */
/**
* Release only this backend's resources. `DeviceBackend.dispose()` is
* optional: backends without it (for example the ADB backend) have nothing
* to release.
*/
async dispose(): Promise<void> {
const backend = this.#backend as DeviceBackend & {
dispose?(): Promise<void>;
};
this.#disposing ??= Promise.resolve()
.then(async () => backend.dispose?.())
.catch((error: unknown) => {
this.#disposing = undefined;
throw error;
});
this.#disposing ??= (async (): Promise<void> => {
await this.#backend.dispose?.();
})().catch((error: unknown) => {
this.#disposing = undefined;
throw error;
});
await this.#disposing;
}

Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ __metadata:
"@lavamoat/allow-scripts": "npm:^3.0.4"
"@lavamoat/preinstall-always-fail": "npm:^2.0.0"
"@metamask/auto-changelog": "npm:^5.3.0"
"@metamask/device-mcp": "npm:^0.6.0"
"@metamask/device-mcp": "npm:^0.7.0"
"@metamask/eslint-config": "npm:^15.0.0"
"@metamask/eslint-config-nodejs": "npm:^15.0.0"
"@metamask/eslint-config-typescript": "npm:^15.0.0"
Expand Down Expand Up @@ -968,15 +968,15 @@ __metadata:
languageName: unknown
linkType: soft

"@metamask/device-mcp@npm:^0.6.0":
version: 0.6.0
resolution: "@metamask/device-mcp@npm:0.6.0"
"@metamask/device-mcp@npm:^0.7.0":
version: 0.7.0
resolution: "@metamask/device-mcp@npm:0.7.0"
dependencies:
"@modelcontextprotocol/sdk": "npm:^1.12.1"
zod: "npm:^4.4.3"
bin:
device-mcp: ./dist/cli/device-mcp.mjs
checksum: 10/5b9443598050734b8ba6569fe460e5ecad0f8f9301464d22cdcc72561df48d524809ecaa7ec1dab674eabd44ea24e69e36f13b453bb312dc114c7b4b332e92b9
checksum: 10/6a779002606d52f0be42f3a0f12668b9d277ac40a872334035dab45967c57f135272a3b0e8c3d1027ed5b58dc6440bda4015f4f1535da70868e918010b313111
languageName: node
linkType: hard

Expand Down
Loading