LogoSkills

/mcp-toolkit scenario — 시나리오 한 번에 자동 실행

YAML 각본에 적은 앱 테스트 단계를 순서대로 실행하고 단계별 응답·화면 캡처·BLoC 덤프·성패 요약(`summary.md`)을 폴더 하나로 남깁니다 (mcp_flutter 동적 도구 등록 필요).

/mcp-toolkit scenario — 시나리오 한 번에 자동 실행#

항목내용
실행 명령/cc-mcp-toolkit:scenario
분류Flutter
난이도●●● 높음
MCP 서버 mcp_flutter, marionette, dart, figma

한마디로#

여러 단계의 앱 테스트를 각본(YAML 파일) 하나에 적어두면, 순서대로 자동으로 실행해 주는 도구입니다. 연극 대본처럼 "준비 → 본 연기 → 정리"를 미리 적어 두면, 배우(앱) 대신 자동으로 무대를 진행하고 결과를 기록해 줍니다.

누가·언제 쓰나요#

  • 앱의 복잡한 흐름을 자동으로 검증하고 싶은 개발자/QA 담당자
  • 예: "로그인 → 데이터 준비 → 화면에서 당겨서 새로고침 → 결과가 맞는지 확인 → 화면 캡처"처럼 여러 동작이 섞인 시나리오를 반복 실행해야 할 때
  • 특히 앱 내부 상태 확인 + 화면 터치 + 화면 이미지 비교가 한꺼번에 필요한 경우(cocode의 대표적인 사용 사례)에 적합합니다.

👉 이럴 땐 쓰지 마세요: 단순히 화면을 터치하고 스크린샷만 찍는 정도라면, 더 가벼운 cc-marionette:sweepcc-pixel-loop:loop를 쓰는 게 낫습니다.

무엇을 해주나요#

각본대로 실행한 뒤, 결과를 폴더 하나에 정리해 줍니다. 폴더 안에는:

  • scenario.yaml — 실제로 실행한 각본 사본
  • steps/ — 각 단계별로 무엇을 했고 어떤 값이 돌아왔는지(01.json 등)
  • screens/ — 시나리오 중 찍은 화면 캡처 이미지
  • bloc-dumps/ — 앱 내부 상태 기록
  • teardown.json — 마무리 정리 단계 기록
  • summary.md — 성공/실패 요약(걸린 시간 포함)

어떻게 쓰나요#

# 각본 파일을 지정해 실행
/mcp-toolkit scenario --file test/scenarios/feed_pull_to_refresh.yaml

# 결과 저장 폴더를 직접 지정 (생략하면 자동 폴더에 저장)
/mcp-toolkit scenario --file ... --output .claude/docs/mcp-toolkit/my-run/

# 실제 실행 없이 각본 문법이 맞는지만 검사 (예행연습)
/mcp-toolkit scenario --file ... --dry-run
  • --file (필수): 실행할 각본 YAML 파일 경로
  • --output (선택): 결과를 저장할 폴더. 안 적으면 .claude/docs/mcp-toolkit/<시나리오이름>-<시각>/에 자동 저장
  • --dry-run (선택): 진짜로 돌리지 않고 각본이 올바른지만 확인

안에서 무슨 일이 벌어지나요#

이 도구는 한 번 쭉 실행하고 끝나는 정해진 순서의 자동 재생입니다(같은 걸 반복하는 루프가 아닙니다).

  1. 준비(setup) — 로그인, 데이터 초기화, 테스트용 데이터 심기, 화면 이동 등 사전 준비를 한 번 합니다.
  2. 본 흐름(steps) — 각본에 적힌 동작을 차례로 실행합니다. 앱 내부 상태를 확인하거나(expect로 "이런 결과가 나와야 한다" 검증), 화면을 터치/스크롤하거나, 화면을 캡처합니다.
  3. 정리(teardown) — 데이터 초기화 등 뒷정리를 합니다. 중간에 실패하더라도 이 정리 단계는 항상 실행됩니다.
  4. 사후 확인(asserts) — 마지막으로 최종 상태가 기대대로인지 한 번 더 점검합니다.

실패는 이런 경우에 기록됩니다: 도구 호출 자체가 오류일 때, expect로 기대한 값과 실제가 다를 때, 외부 명령이 실패를 돌려줄 때.


⚙️ 상세 옵션·실행 명세 (개발자 / AI 에이전트용)

Parameters#

ParameterRequiredDescription
--filePath to a scenario YAML
--outputOutput dir (default .claude/docs/mcp-toolkit/<scenario-name>-<ts>/)
--dry-runValidate the YAML without executing

YAML Schema#

name:  < scenario-id >              # required, snake_case
description:  < free text > 

 # Optional preconditions — run once
setup:
  - tool:  < name >                # dynamic tool from mcp_flutter
    args: { ... }
  - external:  < plugin:command > 
     args: { ... }

# Main flow
steps:
  - tool:  < name > 
     args: { ... }
    expect: { ... }           # optional assertion on the tool ' s return
  - external:  < plugin:command > 
     args: { ... }

# Teardown — runs even on failure
teardown:
  - tool:  < name > 
     args: { ... }

# Post-conditions
asserts:
  - tool: bloc_dump_state
    args: { type: FeedBloc }
    expect: { status: loaded, posts: { length: 10 } }

tool: vs external:#

  • tool: — dynamic tool from mcp_flutter (the in-app tools you registered).
  • external: — call into another plugin's command. Currently supported:
    • cc-marionette:tap, enter_text, scroll_to, take_screenshots, get_interactive_elements, hot_reload
    • cc-pixel-loop:capture, compare
    • cc-jaspr-web:audit, match, regression — when the scenario covers a Web/Jaspr surface

Example Scenario#

# test/scenarios/feed_pull_to_refresh.yaml
name: feed_pull_to_refresh
description: Verify pull-to-refresh on Feed correctly hits the server and updates state.

setup:
  - tool: auth_impersonate
    args: { persona:  " alice "   }
  - tool: db_reset
  - tool: db_seed_fixture
    args: { name:  " feed_with_3 "   }
  - tool: nav_go_named
    args: { name:  " feed "   }

steps:
  # Confirm initial state has 3 posts
  - tool: bloc_dump_state
    args: { type:  " FeedBloc "   }
    expect: { status:  " loaded " , posts: { length: 3 } }

  - external: cc-pixel-loop:capture
    args: { route:  " /feed " , label:  " before-refresh "   }

  # Seed more posts on the backend
  - tool: db_seed_fixture
    args: { name:  " feed_with_10 "   }

  # User pulls to refresh
  - external: cc-marionette:scroll_to
    args: { key:  " feed_top "   }
  - external: cc-marionette:swipe   # if pull gesture supported
    args: { direction:  " down " , from_key:  " feed_top "   }

  - tool: bloc_dump_state
    args: { type:  " FeedBloc "   }
    expect: { status:  " loaded " , posts: { length: 10 } }

  - external: cc-pixel-loop:capture
    args: { route:  " /feed " , label:  " after-refresh "   }

teardown:
  - tool: db_reset
  - tool: auth_impersonate
    args: { persona:  " default "   }

asserts:
  # Final state was already asserted in steps; nothing extra here.

Convergence#

The scenario runner does not loop. It is a deterministic playback. Failures collected:

  • Tool error (non-2xx style response).
  • expect: mismatch.
  • external: command returned a failure summary.

On any failure, teardown still runs.

Output#

.claude/docs/mcp-toolkit/ < scenario-name > - < ts > /
├── scenario.yaml          # copy of the run scenario
├── steps/
│   ├── 01.json            # each step ' s args + return
│   └── ...
├── screens/               # any pixel-loop captures forwarded here
├── bloc-dumps/
├── teardown.json
└── summary.md             # pass/fail summary with timings

When to use vs when to avoid#

  • Use: complex flows that mix domain state + UI taps + visual diffs (the cocode bread-and-butter case).
  • Avoid for: pure tap-and-screenshot flows — those belong in cc-marionette:sweep or cc-pixel-loop:loop.