name: add-mobile-module description: Add or modify an in-process hardware MCP module in the Edison-Watch/mobile Android client, and verify its logic without the Android SDK. Use when adding or editing a *Module under app/.../mcp/, wiring a new *Source, hitting a lint MissingPermission error, or when ./gradlew fails because there is no Android SDK in the sandbox.
Add a mobile hardware MCP module
The mobile app exposes each phone capability as an in-process "MCP server": a Kotlin module answering MCP JSON-RPC directly (no subprocess). This skill is the contract + how to verify it in the SDK-less cloud sandbox.
The module contract
Two files per module, plus a test.
-
FooModule.ktinapp/src/main/java/ai/sealgate/stdiod/mcp/- A
FooSourceinterface holding allandroid.*access (so module logic is pure JVM, testable with a fake source). class FooModule(private val source: FooSource) : BaseMcpModule()(BaseMcpModule+JsonRpclive inLocalMcpModule.kt).- Override
val name(the server name the backend addresses) andtoolDescriptors(): JsonElement(thetools/listarray). Dispatch tool calls in the body — copyBatteryModule(simplest) orUsbModule.
- A
-
AndroidFoo.kt/AndroidFooSource.kt— the realFooSourceimplementation over the Android APIs. This file is not JVM-compilable and is excluded from scratch verification (below). -
FooModuleTest.ktinapp/src/test/java/ai/sealgate/stdiod/mcp/— drive the module with a fakeFooSource; assert on the JSON it returns.
Then register it in TunnelService.connect():
FooModule(AndroidFooSource(this)),
And add a bullet to README.md's module list.
Hard rules
- Every precondition failure is in-band, never a crash and never a JSON-RPC
error: return
JsonRpc.textToolResult(id, text, isError = true)(missing permission, device not found, adapter off, …). The gateway must always get a tool result. - Binary payloads travel as hex strings in tool args/results (see
BluetoothModule/UsbModule), not base64, not byte arrays. - lint
MissingPermissionis checked per call-site and does NOT follow atry/catch (SecurityException)up into callers. A permission-requiring call (BLE GATT,UsbManager, etc.) must catchSecurityExceptionin the same function that makes the call. If lint fails only on a helper, wrap the helper's body in its own try/catch returning an error reason. This is one of the few things only the real toolchain (CI) catches — JVM tests won't. - Async Android callbacks bridge to the synchronous
handle()with latches/timeouts (CountDownLatchfor GATT) or reader threads +BlockingQueue(SPP, notifications). Hold live connections in aConcurrentHashMapkeyed by address/id.
Verify without the Android SDK
The cloud sandbox has no Android SDK (dl.google.com is proxy-blocked), so
./gradlew assembleDebug|lintDebug|testDebugUnitTest all fail locally. CI is the
real gate. To sanity-check module + test logic before pushing, compile just
the pure-JVM slice in a throwaway Gradle project:
/tmp/scratch/
build.gradle.kts # kotlin("jvm") + kotlin("plugin.serialization")
settings.gradle.kts
src/main/kotlin/ <- LocalMcpModule.kt + FooModule.kt (NOT AndroidFoo*.kt)
src/test/kotlin/ <- FooModuleTest.kt + a hand-written FakeFooSource
build.gradle.kts:kotlin("jvm")andkotlin("plugin.serialization")at the same version askotliningradle/libs.versions.toml; depskotlinx-serialization-json(1.7.x is fine) andjunit:junit:4.13.2.- Copy in only
LocalMcpModule.kt, yourFooModule.kt, and the test. Exclude everyAndroid*.kt— those need the SDK. gradle test. Green here means the JSON/dispatch logic holds; it does not cover lint or Android wiring — push and let CI confirm those.
After push
CI ("Build & unit test") runs assembleDebug + testDebugUnitTest +
lintDebug. Green there is the real pass. Then on a device:
git pull && ./gradlew installDebug. The new tools appear in MCP Tester once a
server row of name is registered and the tool list refreshed.
Next.js App Router Expert
Development
A skill that turns Claude into a Next.js App Router expert.
README Generator
Development
Creates professional and comprehensive README.md files for your projects.
API Documentation Writer
Development
Generates comprehensive API documentation in OpenAPI/Swagger format.