Skip to content

Commit 8ef4167

Browse files
committed
feat(app): wire Android MCP through CLI, menu, and create flow
Add --mcp-android / --no-mcp-android flags, mcp-android subcommand parser, interactive create-flow prompt + display-settings row, and propagate enableMcpAndroid through frontend-lib config, web create draft, and API client request body. Mirrors the existing Playwright MCP integration. REF: issue-436
1 parent 66ed517 commit 8ef4167

30 files changed

Lines changed: 113 additions & 12 deletions

packages/app/src/docker-git/api-client-create.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export const buildCreateProjectRequest = (
4141
dockerNetworkMode: config.dockerNetworkMode,
4242
dockerSharedNetworkName: config.dockerSharedNetworkName,
4343
enableMcpPlaywright: config.enableMcpPlaywright,
44+
enableMcpAndroid: config.enableMcpAndroid ?? false,
4445
outDir: command.outDir,
4546
gitTokenLabel: config.gitTokenLabel,
4647
skipGithubAuth: config.skipGithubAuth,

packages/app/src/docker-git/cli/parser-apply.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const parseApply = (
4040
playwrightCpuLimit,
4141
playwrightRamLimit,
4242
gpu,
43-
enableMcpPlaywright: raw.enableMcpPlaywright
43+
enableMcpPlaywright: raw.enableMcpPlaywright,
44+
enableMcpAndroid: raw.enableMcpAndroid
4445
}
4546
})
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Either } from "effect"
2+
3+
import { type McpAndroidUpCommand, type ParseError } from "../frontend-lib/core/domain.js"
4+
5+
import { parseProjectDirWithOptions } from "./parser-shared.js"
6+
7+
// CHANGE: parse "mcp-android" command for existing docker-git projects
8+
// WHY: allow enabling Android MCP in an already created container/project dir
9+
// QUOTE(ТЗ): "Подключить mcp-android так же как работает MCP PLAYRIGHT"
10+
// REF: issue-436
11+
// SOURCE: n/a
12+
// FORMAT THEOREM: forall argv: parseMcpAndroid(argv) = cmd -> deterministic(cmd)
13+
// PURITY: CORE
14+
// EFFECT: Effect<McpAndroidUpCommand, ParseError, never>
15+
// INVARIANT: projectDir is never empty
16+
// COMPLEXITY: O(n) where n = |argv|
17+
export const parseMcpAndroid = (
18+
args: ReadonlyArray<string>
19+
): Either.Either<McpAndroidUpCommand, ParseError> =>
20+
Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
21+
_tag: "McpAndroidUp",
22+
projectDir,
23+
runUp: raw.up ?? true
24+
}))

packages/app/src/docker-git/cli/parser-options.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ const booleanFlagUpdaters: Readonly<Record<string, (raw: RawOptions) => RawOptio
110110
"--force-env": (raw) => ({ ...raw, forceEnv: true }),
111111
"--mcp-playwright": (raw) => ({ ...raw, enableMcpPlaywright: true }),
112112
"--no-mcp-playwright": (raw) => ({ ...raw, enableMcpPlaywright: false }),
113+
"--mcp-android": (raw) => ({ ...raw, enableMcpAndroid: true }),
114+
"--no-mcp-android": (raw) => ({ ...raw, enableMcpAndroid: false }),
113115
"--wipe": (raw) => ({ ...raw, wipe: true }),
114116
"--no-wipe": (raw) => ({ ...raw, wipe: false }),
115117
"--web": (raw) => ({ ...raw, authWeb: true }),

packages/app/src/docker-git/cli/parser.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { parseAttach } from "./parser-attach.js"
77
import { parseAuth } from "./parser-auth.js"
88
import { parseClone } from "./parser-clone.js"
99
import { buildCreateCommand } from "./parser-create.js"
10+
import { parseMcpAndroid } from "./parser-mcp-android.js"
1011
import { parseMcpPlaywright } from "./parser-mcp-playwright.js"
1112
import { parseOpen } from "./parser-open.js"
1213
import { parseRawOptions } from "./parser-options.js"
@@ -93,6 +94,7 @@ export const parseArgs = (args: ReadonlyArray<string>): Either.Either<Command, P
9394
Match.when("sessions", () => parseSessions(rest)),
9495
Match.when("scrap", () => parseScrap(rest)),
9596
Match.when("mcp-playwright", () => parseMcpPlaywright(rest)),
97+
Match.when("mcp-android", () => parseMcpAndroid(rest)),
9698
Match.when("help", () => Either.right(helpCommand)),
9799
Match.when("ps", () => Either.right(statusCommand)),
98100
Match.when("status", () => Either.right(statusCommand)),

packages/app/src/docker-git/cli/usage.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ docker-git clone <url> [options]
77
docker-git open [<selector>] [options]
88
docker-git apply [<url>] [options]
99
docker-git mcp-playwright [<url>] [options]
10+
docker-git mcp-android [<url>] [options]
1011
docker-git attach [<project-dir>] [options]
1112
docker-git panes [<url>] [options]
1213
docker-git scrap <action> [<url>] [options]
@@ -27,6 +28,7 @@ Commands:
2728
open Open an existing docker-git project by selector, URL, or path
2829
apply Apply docker-git config to an existing project/container (current dir by default)
2930
mcp-playwright Enable Playwright MCP + nested Chromium browser for an existing project dir
31+
mcp-android Enable Android MCP (mobile-mcp) + nested Android emulator for an existing project dir
3032
attach, tmux Attach to an existing docker-git project workspace with tmux
3133
panes, terms List tmux panes for a docker-git project
3234
scrap Export/import project scrap (session snapshot + rebuildable deps)
@@ -78,6 +80,7 @@ Options:
7880
--up | --no-up Run docker compose up after init (default: --up)
7981
--ssh | --no-ssh Auto-open SSH after create/clone (default: clone=--ssh, create=--no-ssh)
8082
--mcp-playwright | --no-mcp-playwright Enable Rust browser MCP + noVNC/CDP session (default: --no-mcp-playwright)
83+
--mcp-android | --no-mcp-android Enable Android MCP (mobile-mcp) + nested Android emulator sidecar (default: --no-mcp-android)
8184
--auto[=claude|codex|gemini|grok] Auto-execute an agent; without value picks by auth, random if multiple are available
8285
-d, --daemon browser: run the browser frontend server in the background after build
8386
--active apply-all: apply only to currently running containers (skip stopped ones)

packages/app/src/docker-git/frontend-lib/core/command-builders-template.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type BuildTemplateConfigInput = {
2020
readonly geminiAuthLabel: string | undefined
2121
readonly grokAuthLabel: string | undefined
2222
readonly enableMcpPlaywright: boolean
23+
readonly enableMcpAndroid: boolean
2324
readonly agentMode: AgentMode | undefined
2425
readonly agentAuto: boolean
2526
/**
@@ -96,6 +97,7 @@ export const buildTemplateConfig = (input: BuildTemplateConfigInput): CreateComm
9697
dockerNetworkMode: input.dockerNetworkMode,
9798
dockerSharedNetworkName: input.dockerSharedNetworkName,
9899
enableMcpPlaywright: input.enableMcpPlaywright,
100+
enableMcpAndroid: input.enableMcpAndroid,
99101
bunVersion: defaultTemplateConfig.bunVersion,
100102
agentMode: input.agentMode,
101103
agentAuto: input.agentAuto,

packages/app/src/docker-git/frontend-lib/core/command-builders.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ type CreateBehavior = {
201201
readonly force: boolean
202202
readonly forceEnv: boolean
203203
readonly enableMcpPlaywright: boolean
204+
readonly enableMcpAndroid: boolean
204205
}
205206

206207
const resolveCreateBehavior = (raw: RawOptions): CreateBehavior => ({
@@ -209,7 +210,8 @@ const resolveCreateBehavior = (raw: RawOptions): CreateBehavior => ({
209210
skipGithubAuth: raw.skipGithubAuth ?? false,
210211
force: raw.force ?? false,
211212
forceEnv: raw.forceEnv ?? false,
212-
enableMcpPlaywright: raw.enableMcpPlaywright ?? false
213+
enableMcpPlaywright: raw.enableMcpPlaywright ?? false,
214+
enableMcpAndroid: raw.enableMcpAndroid ?? false
213215
})
214216

215217
type TokenLabelConfig = {
@@ -277,6 +279,7 @@ export const buildCreateCommand = (
277279
...tokenLabels,
278280
skipGithubAuth: behavior.skipGithubAuth,
279281
enableMcpPlaywright: behavior.enableMcpPlaywright,
282+
enableMcpAndroid: behavior.enableMcpAndroid,
280283
agentMode,
281284
agentAuto: isAgentAuto,
282285
clonedOnHostname: raw.clonedOnHostname

packages/app/src/docker-git/frontend-lib/core/command-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export interface RawOptions {
3434
readonly dockerNetworkMode?: string
3535
readonly dockerSharedNetworkName?: string
3636
readonly enableMcpPlaywright?: boolean
37+
readonly enableMcpAndroid?: boolean
3738
readonly archivePath?: string
3839
readonly scrapMode?: string
3940
readonly wipe?: boolean

packages/app/src/docker-git/frontend-lib/core/domain.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export interface TemplateConfig {
114114
readonly dockerNetworkMode: DockerNetworkMode
115115
readonly dockerSharedNetworkName: string
116116
readonly enableMcpPlaywright: boolean
117+
readonly enableMcpAndroid?: boolean | undefined
117118
readonly bunVersion: string
118119
readonly agentMode?: AgentMode | undefined
119120
readonly agentAuto?: boolean | undefined
@@ -204,6 +205,12 @@ export interface McpPlaywrightUpCommand {
204205
readonly runUp: boolean
205206
}
206207

208+
export interface McpAndroidUpCommand {
209+
readonly _tag: "McpAndroidUp"
210+
readonly projectDir: string
211+
readonly runUp: boolean
212+
}
213+
207214
export interface ApplyCommand {
208215
readonly _tag: "Apply"
209216
readonly projectDir: string
@@ -219,6 +226,7 @@ export interface ApplyCommand {
219226
readonly playwrightRamLimit?: string | undefined
220227
readonly gpu?: GpuMode | undefined
221228
readonly enableMcpPlaywright?: boolean | undefined
229+
readonly enableMcpAndroid?: boolean | undefined
222230
}
223231

224232
// CHANGE: add apply-all command to apply docker-git config to every known project; support --active flag
@@ -262,6 +270,7 @@ export type Command =
262270
| SessionsCommand
263271
| ScrapCommand
264272
| McpPlaywrightUpCommand
273+
| McpAndroidUpCommand
265274
| ApplyCommand
266275
| ApplyAllCommand
267276
| HelpCommand

0 commit comments

Comments
 (0)