# Mcp Mobbin

> Mobbin MCP transport: read-only app/screen/flow design research via Code Mode; pair with sk-design-md-generator for a measured Style Reference (extracted design tokens).

- **Type:** Skill
- **Install:** `agentstack add skill-michelkerkmeester-skilled-harness-spec-driven-agent-loops-mcp-mobbin`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [MichelKerkmeester](https://agentstack.voostack.com/s/michelkerkmeester)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [MichelKerkmeester](https://github.com/MichelKerkmeester)
- **Source:** https://github.com/MichelKerkmeester/skilled-harness__spec-driven-agent-loops/tree/main/.opencode/skills/mcp-tooling/mcp-mobbin

## Install

```sh
agentstack add skill-michelkerkmeester-skilled-harness-spec-driven-agent-loops-mcp-mobbin
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Mobbin (mcp-mobbin)

Search **Mobbin's library of real app UI screenshots** ("the world's largest library of real app UI screenshots" per the official repo) from an agent through the **Mobbin MCP via Code Mode**: one documented tool, `search_screens`, answering app, screen, flow, and element research as query intents. This packet is a read-only TRANSPORT (`packetKind: transport`, `mutatesWorkspace: false`): every call goes against the external hosted Mobbin service, never this repo, and it **produces no design tokens or Style Reference of its own**. When a measured Style Reference of real, extracted design tokens is also wanted, pair with `sk-design-md-generator`. Deep operational detail lives in [`references/tool-surface.md`](references/tool-surface.md) and [`references/mcp-wiring.md`](references/mcp-wiring.md).

> **Discovery status (read first).** The `mobbin` Code Mode manual **IS REGISTERED** in this repo's `.utcp_config.json` (registered 2026-07-16 by an operator; this packet never edits the config), and **live discovery RAN 2026-07-16, pre-auth** (fixture: `references/discovery-fixture-2026-07-16.json`): `list_tools` returned **THREE** read tools — registry names `mobbin.mobbin.{search_screens,search_flows,search_sections}` (dot-separated), TS callables `mobbin.mobbin_search_screens(...)` etc. per the fixture's `Access as:` lines — superseding the research's one-public-tool baseline. Operator browser OAuth is still pending for CALLS. Per-session `tool_info` re-confirmation stays MANDATORY before relying on any name: confirm, then call, and fail closed on drift.
>
> **Access trap.** Mobbin MCP access is documented for **Pro, Team, and Enterprise — not Free**. Authentication is **browser OAuth only** (DCR + PKCE S256, `openid` scope): **no static API key or auth env var exists for MCP** — that question is answered in the negative, not open. Unauthenticated calls return HTTP 401. The service is rate-limited to **60 requests per 60 seconds per user**.

---

## 1. WHEN TO USE

### Activation Triggers

**Use when** the user wants to:
- Search Mobbin for real-app design references: screens for a UI pattern or state, apps for category comparison, flows for journey steps, or elements for component behavior in context.
- Find screen examples from shipped apps ("show me iOS banking onboarding screens", "web empty-state dashboard examples").
- Research how real products handle a flow or journey through screen evidence ("first-run onboarding progression", "forgot-password recovery").
- Wire, verify, or troubleshoot the registered `mobbin` Code Mode manual, its OAuth authentication, its plan gating, or its rate limit.

**Keyword Triggers**: "mobbin", "mobbin mcp", "app design research", "screen examples", "ux flow references", "real app screenshots", "search mobbin".

### Use Cases

**App research (read-only).** Name the app, company, or category plus the comparison goal ("banking apps onboarding identity verification"); compare `app_name`, platform, structure, and visible patterns across results. Multiple results are evidence, not a design chooser.

**Screen research (read-only).** Name the concrete screen, state, or job ("iOS subscription cancellation confirmation"); start at 5 results; cite each `mobbin_url` used.

**Flow research (read-only).** Describe the journey and target step. The public contract returns screens, not an ordered flow object — reconstruct sequence only when visual evidence supports it, and label the reconstruction as inference.

**Element research (read-only).** Name component plus context/state ("bottom-sheet destructive confirmation", "inline validation on signup"); analyze element behavior within returned screens.

**Wiring and auth verification.** Report the manual's registration state honestly (presence is the healthy result; absence is a failure symptom to escalate), explain the OAuth-only model and plan gate, and never repair auth state yourself.

### When NOT to Use

**Skip this skill when:**
- The work is measured design-reference extraction: turning a live website's real CSS into a Style Reference DESIGN.md of named design tokens (type scale, components, Quick-Start CSS/Tailwind). That is `sk-design-md-generator`; this packet only retrieves Mobbin screenshot evidence.
- The task is styles, screens, or flows research through Refero. That is `mcp-refero`, the sibling transport.
- The task is Figma work (inspect, tokens, render, Code Connect). That is `mcp-figma`.
- The task is browser automation, live-page inspection, or visual preview of a built page. That is `mcp-chrome-devtools`.
- The work is generic app coding with no design-reference input: use `sk-code`.
- The target is Mobbin's documentation search. `docs.mobbin.com/mcp` is a **separate** Mintlify docs-search MCP, not the design-reference server; this packet targets `api.mobbin.com/mcp` only.
- The user asks to change this repo's files, edit the registered `.utcp_config.json` manual, or repair auth state. This transport forbids Write/Edit/Task, never mutates the workspace, and the registered manual is operator-owned.

---

## 2. SMART ROUTING

### Primary Detection Signal

Route on **narrow Mobbin-specific signals only**. Generic "design", "UI", or "screen" phrasing is not a Mobbin signal; measured live-site extraction belongs to `sk-design-md-generator`, Refero references to `mcp-refero`, Figma work to `mcp-figma`, and browser work to `mcp-chrome-devtools`.

```bash
# Signal detection (pseudo)
echo "$REQUEST" | grep -qiE 'mobbin' && ROUTE="MCP_MOBBIN"
echo "$REQUEST" | grep -qiE 'app design research|real app (screenshots|screens)|ux flow reference' && ROUTE="MCP_MOBBIN"
# generic design/UI phrasing WITHOUT these signals -> not this packet (measured extraction -> sk-design-md-generator)
```

### Phase Detection

```text
TASK CONTEXT
    |
    +- STEP 0: Verify wiring state (mobbin manual registered; presence expected — absence is a failure to escalate)
    +- STEP 1: Score intent -> APPS | SCREENS | FLOWS | ELEMENTS | WIRING_AUTH | TROUBLESHOOT
    +- Phase 1: Measured Style Reference also wanted? -> pair sk-design-md-generator (extracted design tokens); retrieval stays here
    +- Phase 2: Discovery (list_tools / tool_info confirms the callable name)   [MANDATORY; needs a fresh Code Mode session]
    +- Phase 3: Retrieval (search_screens with intent-shaped query; limit 5; platform ios|web)
    +- Phase 4: Verify (evidence cited by mobbin_url; failed[] reported; unknowns preserved; no invented tools)
```

### Resource Domains

The router discovers markdown resources recursively from `references/` and `assets/`, then applies intent scoring. This skill uses a **flat intent router**: no keyed `references//` subdirectories. References are the primary loaded resources; the single asset is the registered manual's reference shape.

```text
references/tool-surface.md      # the one-tool surface, args, response shape, workflows, plan gating
references/mcp-wiring.md        # registered manual, mcp-remote bridge, OAuth/DCR/PKCE, naming, discovery
references/troubleshooting.md   # failure modes + fixes
assets/utcp-mobbin-manual.md    # Registered manual's reference shape + post-registration checklist
```

### Resource Loading Levels

| Level | When to Load | Resources |
| ----- | ------------ | --------- |
| CONDITIONAL | Research intent (apps/screens/flows/elements) | `references/tool-surface.md` (tool contract + workflow baseline) |
| CONDITIONAL | Wiring / auth intent | `references/mcp-wiring.md`, `assets/utcp-mobbin-manual.md` |
| CONDITIONAL | Setup / error intent | `references/troubleshooting.md` |
| FALLBACK | Zero-score routes only | `references/tool-surface.md` suggested (never auto-loaded) |
| PAIR (measured reference) | A measured Style Reference of extracted tokens is also wanted | `sk-design-md-generator`, the measured-extraction companion |

### Smart Router Pseudocode

> Resilience pattern: see [sk-doc smart-router template](../../sk-doc/sk-create-skill/assets/skill/skill-smart-router.md). Guard paths, discover at runtime, score intents, and fall back when unsure. Because this skill has no keyed resource subdirectories, intent selects from the flat resource inventory below.

```python
from pathlib import Path

SKILL_ROOT = Path(__file__).resolve().parent
RESOURCE_BASES = (SKILL_ROOT / "references", SKILL_ROOT / "assets")
DEFAULT_RESOURCE = "references/tool-surface.md"
# Fallback-only: DEFAULT_RESOURCE is a defer-time suggestion, never unioned
# into a route's loaded set. Scored routes load exactly RESOURCE_MAP[intent];
# zero-score routes load nothing and ask for disambiguation instead.
DEFAULT_RESOURCE_SEMANTICS = "fallback-only"
MIN_CONFIDENCE = 1
AMBIGUITY_DELTA = 1

INTENT_MODEL = {
    "APPS":         {"keywords": [("app design research", 4), ("app research", 4), ("competitor", 3), ("app comparison", 3), ("banking apps", 2), ("category", 2), ("how do apps", 2), ("real apps", 3)]},
    "SCREENS":      {"keywords": [("screen", 4), ("screen examples", 4), ("ui pattern", 3), ("empty state", 3), ("first open", 3), ("onboarding screen", 3), ("component example", 3), ("screenshot", 3), ("paywall", 2), ("settings", 2), ("dashboard", 2)]},
    "FLOWS":        {"keywords": [("flow", 4), ("ux flow", 4), ("user flow", 4), ("journey", 4), ("start to finish", 3), ("multi-step", 3), ("progression", 2), ("forgot password", 2), ("checkout", 2), ("signup process", 2)]},
    "ELEMENTS":     {"keywords": [("element", 4), ("bottom sheet", 3), ("inline validation", 3), ("component behavior", 3), ("button state", 2), ("tab bar", 2), ("modal", 2), ("confirmation dialog", 2)]},
    "WIRING_AUTH":  {"keywords": [("wiring", 4), ("utcp", 4), ("oauth", 4), ("mcp-remote", 4), ("authenticate", 4), ("pkce", 3), ("manual", 3), ("register", 3), ("token", 3), ("plan", 2), ("install", 2), ("setup", 2)]},
    "TROUBLESHOOT": {"keywords": [("error", 4), ("failed", 4), ("401", 4), ("not working", 4), ("not resolving", 4), ("429", 3), ("rate limit", 3), ("timeout", 3), ("unauthorized", 3), ("denied", 3)]},
}

# Benchmark-facing mirror of INTENT_MODEL in the standard {weight, keywords} shape
# the skill-benchmark router-replay reads (the tuple form above parses to zero
# intents there). Runtime scoring uses INTENT_MODEL; this block changes no routing.
# Per-intent weight = the MAX per-keyword weight in INTENT_MODEL for that intent
# (all six resolve to 4). Keys and keywords MUST stay identical to INTENT_MODEL;
# keep the two blocks in sync whenever either one is edited.
INTENT_SIGNALS = {
    "APPS":         {"weight": 4, "keywords": ["app design research", "app research", "competitor", "app comparison", "banking apps", "category", "how do apps", "real apps"]},
    "SCREENS":      {"weight": 4, "keywords": ["screen", "screen examples", "ui pattern", "empty state", "first open", "onboarding screen", "component example", "screenshot", "paywall", "settings", "dashboard"]},
    "FLOWS":        {"weight": 4, "keywords": ["flow", "ux flow", "user flow", "journey", "start to finish", "multi-step", "progression", "forgot password", "checkout", "signup process"]},
    "ELEMENTS":     {"weight": 4, "keywords": ["element", "bottom sheet", "inline validation", "component behavior", "button state", "tab bar", "modal", "confirmation dialog"]},
    "WIRING_AUTH":  {"weight": 4, "keywords": ["wiring", "utcp", "oauth", "mcp-remote", "authenticate", "pkce", "manual", "register", "token", "plan", "install", "setup"]},
    "TROUBLESHOOT": {"weight": 4, "keywords": ["error", "failed", "401", "not working", "not resolving", "429", "rate limit", "timeout", "unauthorized", "denied"]},
}

RESOURCE_MAP = {
    "APPS":         ["references/tool-surface.md"],
    "SCREENS":      ["references/tool-surface.md"],
    "FLOWS":        ["references/tool-surface.md"],
    "ELEMENTS":     ["references/tool-surface.md"],
    "WIRING_AUTH":  ["references/mcp-wiring.md", "assets/utcp-mobbin-manual.md"],
    "TROUBLESHOOT": ["references/troubleshooting.md", "references/mcp-wiring.md"],
}

UNKNOWN_FALLBACK_CHECKLIST = [
    "Confirm the research intent: apps, screens, flows, or elements (all are query intents over search_screens)",
    "Confirm the mobbin manual's registration state (registered; absence is a failure symptom to escalate, never repair)",
    "Confirm the account has a paid plan (Pro, Team, or Enterprise); Free has no MCP access",
    "If a measured Style Reference of extracted design tokens is also wanted, pair sk-design-md-generator; this transport only retrieves Mobbin screenshot evidence",
]

def _guard_in_skill(relative_path: str) -> str:
    resolved = (SKILL_ROOT / relative_path).resolve()
    resolved.relative_to(SKILL_ROOT)               # raises if path escapes the skill
    if resolved.suffix.lower() != ".md":
        raise ValueError(f"Only markdown resources are routable: {relative_path}")
    return resolved.relative_to(SKILL_ROOT).as_posix()

def discover_markdown_resources() -> set[str]:
    docs = []
    for base in RESOURCE_BASES:
        if base.exists():
            docs.extend(p for p in base.rglob("*.md") if p.is_file())
    return {d.relative_to(SKILL_ROOT).as_posix() for d in docs}

def classify_intents(request: str):
    text = (request or "").lower()
    scores = {i: 0 for i in INTENT_MODEL}
    for intent, cfg in INTENT_MODEL.items():
        for kw, w in cfg["keywords"]:
            if kw in text:
                scores[intent] += w
    ranked = sorted(scores.items(), key=lambda kv: kv[1], reverse=True)
    primary, top = ranked[0]
    if top == 0:
        return (None, None, scores)   # unrouted -> no intent selected; fallback branch disambiguates
    secondary, second = ranked[1]
    if second > 0 and (top - second)  bool:
        guarded = _guard_in_skill(rel)
        if guarded in inventory and guarded not in seen:
            load(guarded); loaded.append(guarded); seen.add(guarded)
            return True
        if guarded not in inventory:
            notices.append(f"Resource not found in inventory: {guarded}")
        return False

    if max(scores.values() or [0])  **Callable-name status.** The callable is confirmed by live discovery 2026-07-16 (`references/discovery-fixture-2026-07-16.json`): registry name `mobbin.mobbin.search_screens`, TS callable `mobbin.mobbin_search_screens(args)`. The call below quotes the confirmed form; the live RESPONSE behavior is still unexercised (authenticated calls pend operator OAuth), so re-confirm with `tool_info` per session.

```typescript
call_tool_chain({
  code: `
    const result = await mobbin.mobbin_search_screens({
      query: "iOS banking app onboarding identity verification",
      platform: "ios",
      limit: 5
    });
    return { success: true, data: result, errors: [], timestamp: new Date().toISOString() };
  `
});
```

Mandatory discovery-first sequence: `list_tools()` or `search_tools({ task_description: "Mobbin screen design research", limit: 10 })` -> filter to the `mobbin` manual -> `tool_info()` on the exact dotted name -> only then `call_tool_chain({ code })`. Cite every selected reference by its `mobbin_url`.

### Research workflows (query intents over the three live tools)

App, screen, and element research remain intent-specific query designs over `search_screens`; flow and website-section research now have dedicated live-discovered tools (2026-07-16):

1. **App research** — name the app/company/category and comparison goal ("banking apps onboarding identity verification"); compare `app_name`, platform, structure, visible patterns.
2. **Screen research** — name the concrete screen/state/job ("iOS subscription cancellation confirmation", "web empty-state dashboard"); start at 5 results; cite each `mobbin_url` used.
3. **Flow research** — use `search_flows`: describe one user journey ("onboarding with personalization steps"); it returns ordered flow objects (`screens[].position`, `actions[]`, `screen_count`), superseding the old screens-only reconstruction guidance. La

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [MichelKerkmeester](https://github.com/MichelKerkmeester)
- **Source:** [MichelKerkmeester/skilled-harness__spec-driven-agent-loops](https://github.com/MichelKerkmeester/skilled-harness__spec-driven-agent-loops)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-michelkerkmeester-skilled-harness-spec-driven-agent-loops-mcp-mobbin
- Seller: https://agentstack.voostack.com/s/michelkerkmeester
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
