Problem
appduct link --open android and the MCP appduct_connect tool deliver the bootstrap link with an implicit intent (packages/appduct/src/cli/open-target.ts, deliverAndroid):
adb shell am start -a android.intent.action.VIEW -d '<scheme>:///?appduct=...'
No package or component is named. Android resolves the intent by scheme alone, so whenever more than one installed app declares that scheme, the OS shows the "Open with" disambiguation dialog instead of opening the app. At that point the session is never claimed and appduct_wait_for_session blocks for its whole timeout, with nothing telling the agent why. This is the same class of failure as the iOS "Open in App?" alert, and defeats the point of the agent path, which exists so no one has to tap on the device.
This is not exotic. Typical ways to end up with two matching apps on one emulator:
- Product flavors with
applicationIdSuffix (com.example.app and com.example.app.dev) that share one manifestPlaceholders["appductScheme"] / expo.scheme.
- A released build and a debug build of the same app installed side by side.
- Two projects that both picked a generic scheme name.
Proposal
Pass the application id to am start so resolution is explicit:
adb shell am start -a android.intent.action.VIEW -d '<link>' -p <applicationId>
-p limits resolution to that package, so the chooser cannot appear, and a missing package fails loudly ("Activity not started, unable to resolve Intent") rather than hanging the wait. The CLI already reads project files statically to find the scheme (native-scheme.ts reads app/build.gradle for manifestPlaceholders, and app.json for Expo), so applicationId from the same Gradle file, or android.package from app.json, can be resolved the same way, with a --package flag / package argument as the override, mirroring how --scheme works today. If no application id can be resolved, keep today's implicit intent so nothing regresses.
Out of scope for this issue, and only worth considering later: a debug-only exported BroadcastReceiver so delivery needs no activity at all. The -p change is enough to close the chooser gap.
Notes
usesLoopbackAddress and the adb reverse step are unaffected.
- The
ios-device path already takes a bundleId and validates its shape (letters, digits, ., -); the same validation applies to an Android application id.
Problem
appduct link --open androidand the MCPappduct_connecttool deliver the bootstrap link with an implicit intent (packages/appduct/src/cli/open-target.ts,deliverAndroid):No package or component is named. Android resolves the intent by scheme alone, so whenever more than one installed app declares that scheme, the OS shows the "Open with" disambiguation dialog instead of opening the app. At that point the session is never claimed and
appduct_wait_for_sessionblocks for its whole timeout, with nothing telling the agent why. This is the same class of failure as the iOS "Open in App?" alert, and defeats the point of the agent path, which exists so no one has to tap on the device.This is not exotic. Typical ways to end up with two matching apps on one emulator:
applicationIdSuffix(com.example.appandcom.example.app.dev) that share onemanifestPlaceholders["appductScheme"]/expo.scheme.Proposal
Pass the application id to
am startso resolution is explicit:-plimits resolution to that package, so the chooser cannot appear, and a missing package fails loudly ("Activity not started, unable to resolve Intent") rather than hanging the wait. The CLI already reads project files statically to find the scheme (native-scheme.tsreadsapp/build.gradleformanifestPlaceholders, andapp.jsonfor Expo), soapplicationIdfrom the same Gradle file, orandroid.packagefromapp.json, can be resolved the same way, with a--packageflag /packageargument as the override, mirroring how--schemeworks today. If no application id can be resolved, keep today's implicit intent so nothing regresses.Out of scope for this issue, and only worth considering later: a debug-only exported
BroadcastReceiverso delivery needs no activity at all. The-pchange is enough to close the chooser gap.Notes
usesLoopbackAddressand theadb reversestep are unaffected.ios-devicepath already takes abundleIdand validates its shape (letters, digits,.,-); the same validation applies to an Android application id.