Skip to content

Add MCP headless API for the JavaSE port#5377

Merged
shai-almog merged 10 commits into
masterfrom
mcp-headless-api
Jul 14, 2026
Merged

Add MCP headless API for the JavaSE port#5377
shai-almog merged 10 commits into
masterfrom
mcp-headless-api

Conversation

@shai-almog

@shai-almog shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Expose the running simulator — or a Codename One desktop tool — to an LLM agent (Claude Desktop/Code, Codex, opencode, ...) over the Model Context Protocol. The agent can read the current screen, drive the UI, and call tools the app publishes.

Built on the accessibility semantics tree (now on master): the same immutable tree that describes the screen to VoiceOver/TalkBack drives it for an agent, so any UI is drivable with zero code.

Scope: the JavaSE port (simulator + tooling) only. Packaged desktop app / executable jar / cloud desktop builds are out of scope — there is no launcher-arg plumbing and no cn1:mcp-setup goal.

What's included

  • Core engine com.codename1.mcpMCP facade, MCPServer (JSON-RPC 2.0: initialize/tools/resources/ping), MCPTransport + SocketTransport, McpUiTools built-in tools (ui_snapshot, ui_perform_action, ui_activate, ui_set_text, ui_find) over the accessibility snapshot, a PNG screenshot resource, and MCPClientRegistrar/MCPClientDescriptor for host detection/registration. Developer tools reuse com.codename1.ai.Tool. Enablement is API-invocation only (no build hints).
  • ParparVM-safe core — the iOS/Catalyst runtime has no System.in/setOut/getenv and no BufferedReader, so the stdio transport lives in the JavaSE port (MCPStdioTransport, registered via a factory in JavaSEPort.init); core uses only System.getProperty + a hand-rolled line reader.
  • Simulator — a Tools > Model Context Protocol menu (socket attach + detect hosts), wired into both window modes.
  • Developer guide chapter + accept-list entries (passes Vale / paragraph-cap / LanguageTool locally).

Testing

  • MCPServerTest — 13 tests: capabilities handshake, protocol-version negotiation, JSON-RPC error codes, notifications, tools list/call/isError, and real UI driving (snapshot reads a live form, set-text mutates a field, activate fires a button listener, find-by-label).
  • Real MCP Inspector E2E (scripts/mcp-inspector-e2e.sh + scripts/mcp/McpStdioDemo.java) — tools/list, ui_snapshot, and ui_set_text verified against the reference @modelcontextprotocol/inspector client.
  • iOS/Catalyst — a local hello-app iOS build (ParparVM translate + Xcode gen) reaches BUILD SUCCESS with mcp in core; ParparVM prunes unreferenced mcp so it has zero impact on mobile/Catalyst apps; core mcp uses only ParparVM-present APIs (static audit).

🤖 Generated with Claude Code

Expose the running simulator / a Codename One desktop tool to an LLM agent over
the Model Context Protocol. Built on the accessibility semantics tree: the same
immutable tree that describes the screen to VoiceOver/TalkBack drives it for an
agent, so any UI is drivable with zero code.

Scope: the JavaSE port (simulator + tooling) only. Packaged desktop app / jar /
cloud desktop builds are out of scope, so there is no launcher-arg plumbing and
no cn1:mcp-setup goal.

- Core engine com.codename1.mcp: MCP facade, MCPServer (JSON-RPC 2.0),
  MCPTransport + SocketTransport, McpUiTools built-in tools (ui_snapshot,
  ui_perform_action, ui_activate, ui_set_text, ui_find), screenshot resource,
  MCPClientRegistrar/MCPClientDescriptor for host detection/registration.
  Developer tools reuse com.codename1.ai.Tool. Enablement is API-invocation only.
- ParparVM-safe core: no System.in/setOut/getenv or BufferedReader (missing on
  the iOS/Catalyst runtime). The stdio transport lives in the JavaSE port
  (MCPStdioTransport) behind a factory registered by JavaSEPort.init.
- Simulator gains a Tools > Model Context Protocol menu (socket attach + detect
  hosts) in both window modes.
- Unit + MCP-spec conformance tests (MCPServerTest), an MCP Inspector E2E
  harness (scripts/mcp*), and a developer guide chapter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog changed the title Add MCP headless API for desktop targets Add MCP headless API for the JavaSE port Jul 13, 2026
shai-almog and others added 3 commits July 13, 2026 22:15
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ore build)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 12 screenshots: 12 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

…ninit field)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 145 screenshots: 145 matched.

Native Android coverage

  • 📊 Line coverage: 11.21% (11706/104413 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.97% (57689/578349), branch 5.11% (2700/52874), complexity 4.86% (2692/55436), method 7.30% (2084/28530), class 11.39% (461/4049)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 11.21% (11706/104413 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 9.97% (57689/578349), branch 5.11% (2700/52874), complexity 4.86% (2692/55436), method 7.30% (2084/28530), class 11.39% (461/4049)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 210ms / native 143ms = 1.4x speedup
SIMD float-mul (64K x300) java 103ms / native 139ms = 0.7x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 206.000 ms
Base64 CN1 decode 236.000 ms
Base64 native encode 1055.000 ms
Base64 encode ratio (CN1/native) 0.195x (80.5% faster)
Base64 native decode 775.000 ms
Base64 decode ratio (CN1/native) 0.305x (69.5% faster)
Image encode benchmark status skipped (SIMD unsupported)

…s/imports, preserve stack trace)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 168 screenshots: 168 matched.
✅ JavaScript-port screenshot tests passed.

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 225 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 85ms / native 4ms = 21.2x speedup
SIMD float-mul (64K x300) java 74ms / native 4ms = 18.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 337.000 ms
Base64 CN1 decode 258.000 ms
Base64 native encode 1509.000 ms
Base64 encode ratio (CN1/native) 0.223x (77.7% faster)
Base64 native decode 7593.000 ms
Base64 decode ratio (CN1/native) 0.034x (96.6% faster)
Base64 SIMD encode 111.000 ms
Base64 encode ratio (SIMD/CN1) 0.329x (67.1% faster)
Base64 SIMD decode 99.000 ms
Base64 decode ratio (SIMD/CN1) 0.384x (61.6% faster)
Base64 encode ratio (SIMD/native) 0.074x (92.6% faster)
Base64 decode ratio (SIMD/native) 0.013x (98.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 10.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.300x (70.0% faster)
Image applyMask (SIMD off) 189.000 ms
Image applyMask (SIMD on) 175.000 ms
Image applyMask ratio (SIMD on/off) 0.926x (7.4% faster)
Image modifyAlpha (SIMD off) 181.000 ms
Image modifyAlpha (SIMD on) 313.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.729x (72.9% slower)
Image modifyAlpha removeColor (SIMD off) 253.000 ms
Image modifyAlpha removeColor (SIMD on) 207.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.818x (18.2% faster)

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 216 screenshots: 216 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 139 screenshots: 139 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 138 screenshots: 138 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 640 seconds

Build and Run Timing

Metric Duration
Simulator Boot 97000 ms
Simulator Boot (Run) 1000 ms
App Install 24000 ms
App Launch 8000 ms
Test Execution 610000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 75ms / native 3ms = 25.0x speedup
SIMD float-mul (64K x300) java 75ms / native 4ms = 18.7x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 259.000 ms
Base64 CN1 decode 258.000 ms
Base64 native encode 2594.000 ms
Base64 encode ratio (CN1/native) 0.100x (90.0% faster)
Base64 native decode 14643.000 ms
Base64 decode ratio (CN1/native) 0.018x (98.2% faster)
Base64 SIMD encode 192.000 ms
Base64 encode ratio (SIMD/CN1) 0.741x (25.9% faster)
Base64 SIMD decode 333.000 ms
Base64 decode ratio (SIMD/CN1) 1.291x (29.1% slower)
Base64 encode ratio (SIMD/native) 0.074x (92.6% faster)
Base64 decode ratio (SIMD/native) 0.023x (97.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 20.000 ms
Image createMask (SIMD on) 5.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 81.000 ms
Image applyMask (SIMD on) 183.000 ms
Image applyMask ratio (SIMD on/off) 2.259x (125.9% slower)
Image modifyAlpha (SIMD off) 328.000 ms
Image modifyAlpha (SIMD on) 337.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.027x (2.7% slower)
Image modifyAlpha removeColor (SIMD off) 90.000 ms
Image modifyAlpha removeColor (SIMD on) 137.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.522x (52.2% slower)

@shai-almog

shai-almog commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 376 seconds

Build and Run Timing

Metric Duration
Simulator Boot 95000 ms
Simulator Boot (Run) 1000 ms
App Install 16000 ms
App Launch 5000 ms
Test Execution 447000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 59ms / native 3ms = 19.6x speedup
SIMD float-mul (64K x300) java 91ms / native 2ms = 45.5x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 200.000 ms
Base64 CN1 decode 165.000 ms
Base64 native encode 350.000 ms
Base64 encode ratio (CN1/native) 0.571x (42.9% faster)
Base64 native decode 275.000 ms
Base64 decode ratio (CN1/native) 0.600x (40.0% faster)
Base64 SIMD encode 61.000 ms
Base64 encode ratio (SIMD/CN1) 0.305x (69.5% faster)
Base64 SIMD decode 47.000 ms
Base64 decode ratio (SIMD/CN1) 0.285x (71.5% faster)
Base64 encode ratio (SIMD/native) 0.174x (82.6% faster)
Base64 decode ratio (SIMD/native) 0.171x (82.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 11.000 ms
Image createMask (SIMD on) 1.000 ms
Image createMask ratio (SIMD on/off) 0.091x (90.9% faster)
Image applyMask (SIMD off) 67.000 ms
Image applyMask (SIMD on) 54.000 ms
Image applyMask ratio (SIMD on/off) 0.806x (19.4% faster)
Image modifyAlpha (SIMD off) 44.000 ms
Image modifyAlpha (SIMD on) 36.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.818x (18.2% faster)
Image modifyAlpha removeColor (SIMD off) 55.000 ms
Image modifyAlpha removeColor (SIMD on) 183.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 3.327x (232.7% slower)

shai-almog and others added 4 commits July 14, 2026 05:27
…ools

Every desktop Codename One tool (the simulator, Codename One Settings, and any
other CN1-app tool) now gets a native "MCP" pull-down menu, added by the JavaSE
port with no per-tool code:

- Expose This Tool To Agents  - start/stop the loopback MCP server
- Install in MCP Hosts         - register the tool with detected hosts
                                 (Claude Desktop, Claude Code, ...)
- Remove From MCP Hosts        - unregister
- Detect MCP Hosts             - list installed hosts + config paths
- Debug Logging                - OFF / Errors / Summary / Full

Install registers MCPStdioLauncher, a stdio<->socket bridge, so a stdio host
such as Claude Desktop can drive the already-running, human-visible tool.

Debug logging (MCPVerbosity OFF/ERRORS/SUMMARY/FULL) echoes the MCP conversation
to the Codename One log so a user can watch and debug what an agent is doing.

- Core: MCPVerbosity, MCPServer.setVerbosity + per-level logging, MCP facade.
- JavaSE port: MCPDesktopMenu (the native menu), MCPStdioLauncher (the bridge),
  wired into buildNativeMenuBar (all desktop tools) and the simulator menu bar.
- Developer guide: documents the menu, install/uninstall and debug logging.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ild)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tall)

MCPClientRegistrar round-tripped the whole host config (e.g. Claude Desktop's
claude_desktop_config.json) through the default JSONParser, which turns JSON
booleans into "true"/"false" strings, integers into X.0 floats, and drops null
values. That corrupted the user's other settings in the config, which could make
the host reset its config and drop existing mcpServers entries.

Read with a JSONParser instance configured for useBoolean + useLongs +
includeNulls, and write with mapToJson (which preserves nulls) instead of toJson.
Editing a host config now leaves every other setting byte-faithful.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves the six review findings:

1/4 Socket transport moved from core to the JavaSE port
    (MCPSocketTransport). It binds java.net.ServerSocket to the loopback
    address only (the portable com.codename1.io.Socket bound the wildcard
    address, exposing the control channel on every interface) and owns the
    listening and accepted sockets so stop() closes them and unblocks a
    pending accept()/read(). A fresh instance is created per run; the core
    SocketTransport and MCPLineReader are deleted. MCP exposes a
    SocketTransportFactory the port registers in init().

2/5 MCPServer parses requests and re-serializes tool arguments through the
    new MCPJson helper, preserving booleans, integers (as longs) and nulls
    instead of turning them into strings/floats/dropped keys. initialize
    only echoes a protocolVersion the server actually implements.

3/6 MCPClientRegistrar refuses to overwrite a host config it could not
    read or that is not a complete JSON object (structural check, since
    CN1's parser returns a partial map rather than throwing), and writes
    through a temp file renamed into place so an interrupted write cannot
    truncate the config. Detection no longer treats the home directory as a
    present "parent" (a dotfile such as ~/.claude.json is only detected when
    the file itself exists), removing the false-positive Claude Code match.

Adds tests for protocol negotiation, faithful argument types and the
config completeness guard. PMD and SpotBugs clean; 17 mcp tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog shai-almog merged commit d584be4 into master Jul 14, 2026
35 checks passed
@shai-almog shai-almog deleted the mcp-headless-api branch July 14, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant