Skip to content

Caller-side DX: login personas, unit mismatch, target reachability, and 5 more friction points from integrating into developerz.ai #47

Description

@sebyx07

Integration report from wiring ui-debugger into the developerz.ai monorepo (dashboard SPA on :5173, marketing SSR on :3000) and writing a team skill that teaches agents to drive it.

These are friction points found while reading the source and building the caller-side workflow — not a bug hunt against a running instance. Each is cited to source so they can be triaged independently. Happy to split into separate issues or send PRs.


1. No auth/persona concept — the single biggest cost per run

debugLogin (src/config/schema.ts:19-21) skips captcha only, not auth. There is no place in .ui-debugger-mcp.json to say how to log into this app, so every single goal string has to re-explain the login flow:

"Go to /login, type user@dev.local in the email field, type the password, click Submit, then …"

That is re-typed for every run, burns driver steps before the actual goal starts, and puts credentials into a free-text field that gets logged.

Proposal — named personas on the target, referenced per-run:

{
  "targets": {
    "dashboard": {
      "adapter": "browser",
      "url": "http://localhost:5173",
      "auth": {
        "admin": { "path": "/login", "fields": { "email": "admin@dev.local", "password": "admin" }, "submit": "Sign in" },
        "user":  { "path": "/login", "fields": { "email": "user@dev.local",  "password": "user"  }, "submit": "Sign in" }
      }
    }
  }
}

Then start_debug({ target: "dashboard", as: "admin", goal: "open Audit and …" }), with the recipe composed into the system prompt as its own section (alongside the existing address/story/criteria sections in src/agent/prompts/compose.ts:57-100), and persona names surfaced by describe. Values should be redacted in logs/agent.log the way log-format.ts:51 already redacts password/token query params.

This matters most for exactly the case the README sells — an AI verifying its own app — because almost every interesting screen is behind a login.

2. timeout is seconds but wait is milliseconds

  • start_debug.timeout — "Wall-clock cap in SECONDS" (src/mcp/tools/start-debug.ts:60-68)
  • get_findings.wait — long-poll "up to this many ms" (src/mcp/tools/get-findings.ts:55-62)

Two adjacent time parameters on the same workflow in different units. timeout: 120000 is silently accepted (it's under the 2_147_483 ceiling) and yields a 33-hour run. I had to call this out explicitly as a gotcha in our skill doc, which is a smell.

Suggest either normalising both to ms, or naming them timeout_s / wait_ms.

3. describe.operational doesn't mean the target is reachable

operational is documented as "whether this adapter is wired" (src/services/debug-service.ts:98-99, src/mcp/tools/output.ts:44) — and all three adapters are shipped, so it is effectively always true. It says nothing about whether http://localhost:5173 is actually serving.

The failure mode: dev server is down → the driver launches, navigates, sees nothing, and burns a full run reporting an empty/broken page. To a caller this is indistinguishable from a genuine UI bug.

Suggest either a cheap reachability probe (liveness: "up" | "down" | "unknown" on the describe entry) or a fail-fast in start_debug when the first navigation gets a connection error, with an error that names the port rather than a generic finding.

4. A forgotten end_session wedges the project

One run per cwd (start_debug description; SessionManager keyed by cwd, debug-service.ts:130-131). If the caller crashes, loses context, or simply forgets end_session, the next start_debug is blocked and the only recovery is out-of-band ui-debugger-mcp stop.

An MCP client that dropped the session id mid-conversation has no way back in-band. Suggest start_debug({ replace: true }), or having describe report the active session id so a caller can adopt/end it.

5. Whole-object get_findings caps lists at 20

toToolResult(..., { capLists: args.fields === undefined }) (src/mcp/tools/get-findings.ts:78-79). The steering note pointing at fields is good design, but the default read is the one an agent makes first, and on a long run it silently under-reports — a caller that doesn't notice the note concludes there were 20 issues.

Suggest including an explicit truncated: true + total counts in the capped payload so it's structurally visible, not just prose.

6. Empty data reads as a broken UI

Not a code bug, but a recurring false positive: a freshly-migrated app with no seed data renders empty states everywhere, and the driver correctly reports "the table is empty" as a defect. There is no way to declare preconditions ("this app needs seeded data; empty tables are expected on /new").

A notes / context field on the target config (distinct from the per-run goal) that gets composed into the prompt once would cover this, and would also carry things like "this app is dark-mode by default" or "the first load shows an onboarding modal — dismiss it".

7. Smaller notes

  • VISION_SYSTEM_PROMPT tells the vision model to reply with only a JSON object (src/agent/belt/look.ts:98-140) but nothing in the shown path appears to enforce a JSON response mode — a chatty model that wraps it in prose costs a step. Worth generateObject/schema-constrained decoding if the router supports it.
  • The README's .mcp.json example uses OPENAI_API_KEY/OPENAI_BASE_URL; for a server whose whole value proposition is model-per-role, the env names read as single-provider. Not worth breaking, but documenting a per-role key override would help.
  • docs/idea/ is genuinely good and I leaned on it heavily. Linking mcp-tools.md and config.md directly from the README's Setup section would shorten the ramp for a caller who only wants to use the server.

Filed rather than PR'd because there was active uncommitted work in the checkout at the time and I didn't want to collide with it.

8. npx without @latest pins callers to a stale cached version

The README's .mcp.json snippet and init's printed snippet both use:

"args": ["-y", "@developerz.ai/ui-debugger-mcp"]

npx -y <pkg> will reuse an already-cached version rather than resolving the newest one, so a caller who installed at 1.2.0 keeps launching 1.2.0 after you ship 1.5.0 — silently, with no warning that their tool schemas are out of date. Suggest @developerz.ai/ui-debugger-mcp@latest in the README, in init's printed snippet, and in .mcp.example.json.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions