Skip to content

Development and Integration

xjoker edited this page Jul 21, 2026 · 1 revision

Development and Integration

Part 1: Developing

Repo layout

delamain/
├── src/main/java/com/zin/delamain/   ← Java backend (Javalin + JadxDecompiler)
├── src/test/java/com/zin/delamain/   ← JUnit tests
├── gateway/                          ← Python MCP gateway (FastMCP)
│   ├── main.py
│   ├── src/
│   │   ├── tools/                   ← one MCP tool module per feature area
│   │   ├── config/                  ← TOML config loading
│   │   ├── auth/                    ← MCP client bearer-token auth
│   │   └── registry/                ← JADX backend instance registry
│   └── tests/
│       ├── *.py                     ← unit tests (mocked HTTP)
│       └── integration/             ← integration tests (needs a running service)
├── tests/                           ← Python integration tests against a live Java backend + gateway pair
├── tools/delamain-cli/               ← Rust out-of-band upload CLI
└── docker/
    └── Dockerfile                   ← fused image: Java + Python, no Xvfb/noVNC

Full architecture (Java package responsibilities, the on-disk index stack, auth model) is in docs/architecture-reference.md.

Requirements

Component Version
Java 21 (Maven build, Javalin runtime)
Maven 3.9+
Python 3.10+ (gateway)
uv latest
Rust stable (only if building delamain-cli)

Build & test commands

# Java backend — build
mvn -DskipTests clean package          # artifact: target/delamain-*.jar

# Java backend — unit tests
mvn test

# Python gateway — setup
cd gateway && uv sync

# Python gateway — unit tests (mocked HTTP, no live service needed)
cd gateway && pytest tests --ignore=tests/integration

# Python gateway — integration tests (needs a running gateway/backend)
cd gateway && pytest tests/integration

# Root integration tests (needs a live Java backend + gateway)
export JADX_JAVA_URL=http://localhost:28650
export GATEWAY_URL=http://localhost:8651
export JADX_AUTH_TOKEN="$DELAMAIN_AUTH_TOKEN"
export MCP_AUTH_TOKEN="$MCP_AUTH_TOKEN"
pytest tests/

# delamain-cli (Rust)
cd tools/delamain-cli
cargo build --release
cargo test

Recommended CI split: run the two unit-test commands (Java mvn test + gateway pytest tests --ignore=tests/integration) on every push; save the live-service integration suites for pre-release.

Adding a new MCP tool

  1. Create or pick a module under gateway/src/tools/.
  2. Implement async def tool_name(param: type, ...) -> dict.
  3. Register it with @mcp.tool() inside that module's register_*_tools(mcp).
  4. Wire the registration function into gateway/src/tools/__init__.py.
  5. Write a matching unit test (mock get_from_jadx, assert only on the HTTP call shape — endpoint, params/json_body, method — not on business logic).

Design conventions to follow (from docs/mcp-tool-design-guide.md — written specifically for AI-consumed tools, not human-consumed APIs):

  • Keep tool descriptions under ~200 characters; token overhead is paid on every call.
  • Always return a dict, never let an exception reach the AI — standard error shape {"error": "ERROR_CODE", "message": "..."}.
  • Every list-returning tool needs pagination (offset/count, capped page size, has_more).
  • Anything that can take longer than ~10 seconds must use the submit/poll ticket pattern (submit_x() → ticket → get_task_result(ticket)), or the MCP client will time out.
  • Tier timeouts by operation type (metadata search vs. code decompilation vs. code search vs. warmup) rather than one blanket value.
  • Provide a batch variant (batch_get_x) for any frequently-used single-item tool.
  • instance_id: Optional[str] = None is accepted on most tools for call-site compatibility but is otherwise ignored — the gateway is single-instance (see FAQ); don't build new routing logic on it.
  • Follow the naming convention: get_/search_/list_/submit_/generate_/export_ verbs, submit_x + get_x_result pairs for async work.

Part 2: Integrating an MCP client

Connecting

Point your MCP client at the gateway's Streamable-HTTP MCP endpoint:

http://<host>:8651/mcp
Authorization: Bearer <one of your DELAMAIN_AUTH_TOKENS values>

Any token in the DELAMAIN_AUTH_TOKENS whitelist grants identical, full access — see Configuration for the three-token model. There is no stdio fallback for the gateway; it runs strictly as an HTTP server (mcp.run(transport="http", ...) in gateway/main.py) with a single uvicorn worker.

Once connected, call get_jadx_guide(verbose=True) for the server's own full workflow guide, then typically:

  1. list_available_files_tool — see what's staged under the mounted APK directory.
  2. load_file_tool(path=...) — load a file relative to /apks.
  3. Poll get_decompile_status until the warmup/decompile state stabilizes, then use the decompilation/search/graph/resource/Frida/security tool families.

Async submit/poll ticket pattern

Any operation that could exceed ~10 seconds (security scans, callgraph export, code search over a cold or very large index) is exposed as a submit_x(...) tool that returns a ticket immediately, paired with a polling tool:

result = submit_security_scan(scan_type="full")
# → {"ticket": "...", "status": "submitted", "retry_after_seconds": 5}

status = get_task_result(ticket=result["ticket"])
# → status: running | done | error | not_found

Ticket TTL is 300s on the Python gateway side, 600s on the Java backend side (the longer Java-side TTL accounts for search requests queued during warmup).

delamain-cli for out-of-band uploads

For an APK too large (or too awkward) to place in the mounted directory, pair the create_transfer_token MCP tool with the standalone delamain-cli Rust CLI so the file's bytes never traverse the AI's context window:

  1. The AI calls create_transfer_token(filename=...), which returns token, upload_url, status_path, and expires_at_epoch_ms.
  2. The human (or an automation) runs:
    delamain-cli --server http://<host>:8651 --token <token> /path/to/app.apk
    (or --upload-url <upload_url> --token <token> ... using the URL returned directly). This resumes automatically from GET /transfer/status if a previous attempt was interrupted, streams the file once to compute SHA-256, then sends chunked, checksummed PUT requests.
  3. On success it prints the server-confirmed path, bytes, and sha256; the AI then calls load_file with that path.

A one-shot, non-resumable alternative needing no extra tooling:

curl -T /path/to/app.apk -H "X-Transfer-Token: <token>" <upload_url>

Full protocol (headers, TTL, size caps, sandbox rules) is in docs/file-upload.md; CLI build/cross-compile instructions are in tools/delamain-cli/README.md.

See also: Configuration for every relevant env var, Deployment for how to stand up an instance, FAQ for the token model and health-check quick reference.

Clone this wiki locally