Skip to content

Debug Actions experimental ​

Debug Actions turns an app's debug menu into typed actions the JetWhale host and AI agents can run: sign in as a test user, reset onboarding, shift the clock, open a deep link. The app declares each action once; the host builds a form for its arguments, and an agent gets the same action with a JSON Schema over MCP.

Setup ​

Install Debug Actions from Settings → Plugins → Add Plugins → Official Plugins, then add the agent to the app:

kotlin
dependencies {
    implementation("com.kitakkun.jetwhale:jetwhale-agent-runtime:<version>")
    implementation("com.kitakkun.jetwhale:jetwhale-debug-actions-agent:<version>")
    // Only for actions that belong to a screen:
    implementation("com.kitakkun.jetwhale:jetwhale-debug-actions-agent-compose:<version>")
}

The agent API is marked @ExperimentalJetWhaleApi and may change between releases; opt in with @OptIn(ExperimentalJetWhaleApi::class) where you use it.

kotlin
val actionsPlugin = JetWhaleDebugActionsAgentPlugin()

actionsPlugin.register {
    action("Reset onboarding") {
        perform { onboarding.reset() }
    }
}

startJetWhale { plugins { register(actionsPlugin) } }

Declaring actions ​

An action's arguments are one @Serializable class. Its properties become the fields of the host's form and of the MCP schema; a property with a default may be left out, and @McpDescription documents it for people and agents alike.

kotlin
@Serializable
data class SignIn(
    @McpDescription("The test account's address.")
    val email: String,
    val tier: Tier = Tier.FREE,
)

actionsPlugin.register {
    group("Account") {
        action<SignIn>("Sign in as test user") {
            description = "Replaces the session with a test account."
            options("email") { testAccounts.map { it.email } }
            perform { args -> auth.signIn(args.email, args.tier) }
        }
    }
}
SettingWhat it does
descriptionShown under the title and to agents
options(property) { … }Suggested values for a property, fetched from the app each time
destructive = trueThe host asks before running; an agent must pass confirmDestructive: true
runsOnMainThread = trueRuns on Dispatchers.Main, for UI objects bound to the main thread (Compose state does not need it; on the JVM the app needs a Dispatchers.Main provider such as kotlinx-coroutines-swing)
timeoutHow long a run may take (30 seconds unless set)

The value perform returns is shown to whoever ran it: a String as text, a JsonElement as JSON, anything else through toString(). A thrown exception is reported with its stack trace. An action can therefore also answer a question — "what is the current user id?" — rather than change anything.

Properties are entered by type: text for strings and numbers, a switch for a Boolean, a menu for an enum, and JSON for anything else (lists, nested classes).

Actions of a screen ​

With the Compose artifact, a composable registers actions that exist only while it is shown:

kotlin
@Composable
fun CheckoutScreen(form: CheckoutFormState) {
    actionsPlugin.DebugActions(form) {
        action("Fill test card") { perform { form.fill(TestCards.visa) } }
    }
}

The host marks them Screen and they appear and disappear as the user navigates. Pass what the actions capture as keys; they are declared again when a key changes.

In the host ​

  • Search — ⌘K (Ctrl+K) focuses the search field; Enter picks the first match.
  • Pins — pinned actions stay at the top of the list, across restarts.
  • Arguments — the form starts from the arguments the action last ran with.
  • Runs — the latest result under the form, and the action's recent runs, including those an AI agent made. A run in progress can be cancelled.

MCP tools ​

ToolWhat it does
com.kitakkun.jetwhale.actions.listActionsEvery action with its argument JSON Schema, suggested values, and whether it is destructive or belongs to the current screen
com.kitakkun.jetwhale.actions.runActionRuns an action by id with arguments; returns the outcome, the result and any error with its stack trace

The tool list of an MCP connection is fixed when it opens, while screen actions come and go, so actions are not tools of their own: an agent lists them, then runs one by id. List again after navigating.

Released under the Apache License 2.0.