Skip to main content
Glama
claude-android

claude-android

by duonglkh
README.md
# claude-android

> MCP server + Claude Code skills toolkit for Android dev workflows. Expose Gradle / Manifest / R8 context to Claude, ship a curated library of skills that consume it.

[![Status](https://img.shields.io/badge/status-v0.1%20preview-orange)](#status)
[![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
[![Node](https://img.shields.io/badge/node-%E2%89%A520-brightgreen)](#requirements)
[![MCP](https://img.shields.io/badge/MCP-stdio-blueviolet)](https://modelcontextprotocol.io)

## What it does

Android projects with Kotlin DSL + convention plugins + KMP + AGP-upgrade pain repeat the same
tasks: read versions, scan dependencies, plan migrations, audit R8 rules. Each task makes Claude
grep files line by line.

`claude-android` is an **MCP server** that exposes a small set of read-only tools (Gradle,
Manifest, ProGuard, string resources) so Claude can answer "what's my AGP version, what
plugins, what permissions, which keep rule is this, which locale is missing this key" in
milliseconds — and a **skills library** that orchestrates those tools to deliver complete
workflows: `agp-upgrade`, `r8-audit`, `string-sync` (see [scope](#scope) below for exactly
what each ships today).

```
┌──────────────────┐    stdio     ┌─────────────────────┐    fs    ┌──────────────┐
│  Claude Code     │  ────────▶   │  claude-android     │  ──────▶ │ your Android │
│  (CLI / agent)   │              │  MCP server         │           │ project      │
└──────────────────┘   tools list └─────────────────────┘  parsed   └──────────────┘
                                        ▲                  JSON
                                        │
                                ┌────────────────┐
                                │ skills/        │
                                │  agp-upgrade/  │
                                │  r8-audit/     │
                                │  string-sync/  │
                                └────────────────┘
```

## Scope

What ships today:

| Component | Status |
| --- | --- |
| MCP server (stdio transport) | ✅ |
| Tool: `get_project_info` (settings.gradle + libs.versions.toml) | ✅ |
| Tool: `read_gradle` (module build.gradle.kts) | ✅ |
| Tool: `read_manifest` (AndroidManifest.xml) | ✅ |
| Tool: `read_proguard_rules` (ProGuard/R8 rules, comment-paired) | ✅ |
| Tool: `read_string_catalog` (values*/strings.xml per module) | ✅ |
| Skill: `agp-upgrade` (workflow only — no auto-apply) | ✅ |
| Skill: `r8-audit` (per-rule report, never edits the rules file) | ✅ |
| Skill: `string-sync` (per-module/locale gap report, delegates the actual fill) | ✅ |
| Tool: `run_lint` | ❌ not planned — see [Status](#status) for why |

## Requirements

- **Node ≥ 20**
- **Claude Code** installed and authenticated (https://docs.claude.com/en/docs/claude-code/quickstart)
- A real Android project with Kotlin DSL Gradle to point at

## Install

### Option A — From npm *(after publish)*

```bash
npm install -g @duonglkh/claude-android
```

### Option B — From source *(v0.1 preview)*

```bash
git clone https://github.com/duonglkh/claude-android.git
cd claude-android
npm install
npm run build
npm link    # so `claude-android-mcp` is on your PATH
```

## Register the MCP server with Claude Code

```bash
claude mcp add android claude-android-mcp
```

Verify:

```bash
claude mcp list
# expected: "android" with status "connected" (or similar)
```

## Use the skills

Skills live in `skills/`. To use them in Claude Code:

```bash
# Copy / symlink skills into Claude Code's skills directory
mkdir -p ~/.claude/skills
cp -r ./skills/agp-upgrade ~/.claude/skills/
cp -r ./skills/r8-audit ~/.claude/skills/
cp -r ./skills/string-sync ~/.claude/skills/

# Or, if installed via npm globally:
# Symlink from the global install:
ln -s "$(npm root -g)/@duonglkh/claude-android/skills/agp-upgrade" ~/.claude/skills/agp-upgrade
```

Then in any Android project:

```bash
cd ~/your-android-project
claude
> /agp-upgrade
> /r8-audit
> /string-sync
```

`/agp-upgrade`:
1. Call `mcp__android__get_project_info` to read AGP / Kotlin / modules.
2. Ask which AGP version to target.
3. For each module, call `mcp__android__read_gradle` to find breaking changes.
4. Produce a step-by-step migration plan **without applying any changes**.
5. Wait for confirmation before patching.

`/r8-audit`:
1. Call `mcp__android__read_gradle` to find the module's `proguardFiles`.
2. Call `mcp__android__read_proguard_rules` for each — every rule comes back paired with the
   comment written immediately above it, since that comment is often the only record of why a
   broad-looking rule is actually necessary.
3. Classify each rule (redundant / global optimization killer / needs justification / fine),
   checking the project's own `CLAUDE.md` for an explicit "don't touch without asking" marker
   before recommending anything.
4. Produce a per-rule report. **Never edits the rules file.**

`/string-sync`:
1. Call `mcp__android__get_project_info` for the module list, then
   `mcp__android__read_string_catalog` on each module that has its own resources.
2. Report missing/extra keys per module per locale, and flag any locale directory using a
   modern ISO code where Android's resource qualifier resolution still expects the historical
   one (`values-id` → should be `values-in`, etc.) — those locales are never served at all, not
   just incompletely translated.
3. For the actual fill, delegates to the user's `androidstrings` skill if installed, or offers
   to edit one module/locale at a time with confirmation. **Does not bulk-write files itself.**

## Tools reference

### `get_project_info`

Input:
```json
{ "projectPath": "/absolute/path/to/android/project" }
```

Output (example):
```json
{
  "projectPath": "/Users/me/MyApp",
  "rootProjectName": "MyApp",
  "modules": ["app", "core:ui", "feature:home"],
  "agpVersion": "8.7.3",
  "kotlinVersion": "2.0.21",
  "composeBomVersion": "2024.12.01",
  "isKmp": false,
  "hasBuildLogic": true,
  "warnings": []
}
```

### `read_gradle`

Input:
```json
{ "modulePath": "/absolute/path/to/module" }
```

Output includes: `plugins`, `namespace`, `compileSdk`, `minSdk`, `targetSdk`,
`applicationId`, `versionCode`, `versionName`, `composeEnabled`, `productFlavors`,
`buildTypes`, `dependencies`, `warnings`.

**Convention-plugin fallback:** if `compileSdk`/`minSdk`/`targetSdk` aren't declared in the
module's own `build.gradle.kts` (common once a project uses `build-logic` convention
plugins), the tool scans `build-logic/**/*.kt` for them instead and lists that in
`warnings`. Treat those three fields as a heuristic — not a Gradle-evaluated value — whenever
`warnings` is non-empty; a module could apply a different convention plugin than the one the
fallback happened to read.

### `read_manifest`

Input:
```json
{ "manifestPath": "/absolute/path/to/AndroidManifest.xml" }
```

Output includes: `packageAttr`, `permissions`, `applicationName`, `applicationLabel`,
`applicationIcon`, `applicationTheme`, `usesCleartextTraffic`, `activities`, `services`,
`receivers`, `providers`.

### `read_proguard_rules`

Input:
```json
{ "filePath": "/absolute/path/to/proguard-rules.pro" }
```

Output: `sections[]`, each `{ comment, rules[] }` — `comment` is the text of the comment
block immediately above that group of rules (`null` if there isn't one), stripped of leading
`#`. Each rule is `{ raw, directive, modifiers, target, members, lineStart }`. Also returns
`fileHeaderComment` (a leading pure-comment block with no rule attached, e.g. a file banner)
and `ruleCount`.

This tool only parses — it does not judge which rules are redundant or safe to remove. That
reasoning is the `r8-audit` skill's job, specifically so the comment-per-rule context (often
the only record of *why* a rule exists) is never thrown away before a judgment is made.

### `read_string_catalog`

Input:
```json
{ "modulePath": "/absolute/path/to/module" }
```

Output: `baseKeyCount`, `nonTranslatableKeys` (excluded from the missing-key check), and
`locales[]` — each `{ locale, keyCount, missingKeys, extraKeys }`. Resource qualifiers that
aren't locales (`values-sw600dp`, `values-v31`, `values-night`, density buckets, …) are
filtered out before counting, rather than being miscounted as under-translated languages — a
naive `values-*` directory count on a real project reported 24 "locales" for one module vs. 22
for its siblings purely because of two non-locale config-qualifier directories. `warnings`
flags locale directories using a modern ISO code where Android's resource resolution still
expects the historical one (`values-id` should be `values-in`, `values-he` should be
`values-iw`, `values-yi` should be `values-ji`) — those locales are never selected at all, for
any device.

## Roadmap

- **v0.1** — read-only tools (`get_project_info`, `read_gradle`, `read_manifest`) +
  `agp-upgrade` skill
- **v0.2** *(this release)* — `read_proguard_rules`; `r8-audit` skill
- **v0.3** *(this release)* — `read_string_catalog`; `string-sync` skill
- **v0.5** — first npm publish (`@duonglkh/claude-android`)
- **v1.0** — write tools (apply patches with confirmation), Crashlytics adapter

`run_lint` is off the roadmap — see [Status](#status) for the measurement behind that call.

See [issues](https://github.com/duonglkh/claude-android/issues) for current work.

## Status

**v0.1 preview** — tools are functional but parsers are regex-based, not full AST. They handle
modern Kotlin DSL Gradle projects well, including `build-logic` convention-plugin setups
(`read_gradle` falls back to scanning `build-logic/` for `compileSdk`/`minSdk`/`targetSdk` when
a module doesn't declare them itself, and flags it in `warnings`); legacy Groovy
`build.gradle` may parse poorly. Open an issue with a sample if you hit it.

**Why regex and not `./gradlew :app:properties` / an AGP-aware task?** Measured on a real
10-module Kotlin DSL + convention-plugin project: a cold Gradle daemon start adds ~9s, a warm
one ~1.5s — and the stock `properties` task doesn't even expose `compileSdk`/`minSdk`/
`targetSdk` (those live on the AGP DSL extension, not as plain Gradle project properties), so
getting them from Gradle itself would need a custom init-script task, not just a flag. That
also means invoking Gradle requires the project to fully configure — which can fail on things
unrelated to what you're asking for (e.g. a missing credential env var referenced by
`settings.gradle.kts`). For v0.1, regex-with-heuristic-fallback (and an honest `warnings`
array when it's guessing) stays the only mode. A `--resolve` flag that shells out to Gradle
for ground truth, with the custom task that implies, is a reasonable v0.2+ feature — not
something to half-build now.

**Why no `run_lint` tool?** Measured on the same real project: `./gradlew :app:lintStagingDebug`
— the cheapest single-variant lint task available, with the Gradle daemon already warm from a
prior invocation — took **7m9s, and still failed** (a KSP/Hilt codegen mismatch, most likely
from unrelated concurrent builds against the same project rather than a real lint defect — not
investigated further, out of scope here). Whatever the cause, a tool whose timing is "7+
minutes, and it might not even finish" is disqualifying on its own. That's not a "add a
timeout" problem: this MCP server is a synchronous stdio request/response loop with no
progress-reporting, and most MCP clients
apply their own timeout to a tool call well under 6 minutes. Wrapping `./gradlew lint` as a tool
call would mean the call times out client-side before Gradle finishes, which is worse than not
having the tool — it would look like a hang or a crash, not "still working." A lint integration
needs either a background-job tool shape (start, poll, fetch-result) or a client with
long-running-call support, not a single blocking `run_lint` call. That's a different enough
feature that it's not on the roadmap at all right now, rather than postponed to "later."

## Testing

```bash
npm test          # vitest; fixtures under test/fixtures/ (sanitized, derived from real projects)
npm run typecheck
```

## Contributing

PRs welcome. Open an issue first for non-trivial changes. New skills are especially valued — if
your team has an Android workflow that Claude could orchestrate, propose it.

## Part of the `hidev` open-source ecosystem

See [duonglkh.github.io](https://duonglkh.github.io/) for the full roster.

## License

MIT © 2026 Hidev (Hung Duong)

TDQS

A4/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct file type: project info, Gradle build file, and Android manifest. There is no overlap in functionality.

Naming Consistency5/5

All tool names follow a clear verb_noun pattern (get_project_info, read_gradle, read_manifest) with consistent snake_case. The verbs 'get' and 'read' are synonymous in this context.

Tool Count4/5

Three tools is on the low side but appropriate for a focused read-only server that inspects core Android project files. Each tool serves a distinct purpose without unnecessary bloat.

Completeness3/5

The set covers the essential files for reading an Android project, but lacks write capabilities and may miss some auxiliary files (e.g., settings.gradle.kts, proguard rules). This leaves some gaps for interactive modification.