화면을 받는다 — 클라이언트가 화면을 하나도 안 갖고 있으면
5 분목표MCP 클라이언트 강좌 4편. 서버에서 화면 정의를 읽어 런타임에 넘긴다. 이 파일에는 데스크가 어떻게 생겼는지에 대한 코드가 한 줄도 없다 — 그게 검사된다.
3편까지 클라이언트가 무엇을 보여줄지는 우리가 여기 적었기 때문에 알았다. 이번엔 안 적는다.
세 줄
final read = await client.readResource('ui://desk');
final text = read.contents.first.text!;
final screen = jsonDecode(text) as Map<String, dynamic>;ui://desk — 546 B, 6 lines, type "page", title "Desk"
nothing about this screen is written in the clientFlutter 호스트라면 여기서 MCPUIRuntime.initialize(screen) 로 넘어간다. 강좌 샘플은 누구나 돌릴 수 있게 순수 Dart 로 뒀고, 도착했다는 것과 그게 화면이라는 것까지만 확인한다.
무엇이 없는가
이 파일에 없는 것들이다.
- 위젯 트리
- 색상·글꼴 크기
- 버튼 라벨
- 어떤 버튼이 어떤 도구를 부르는지
전부 서버가 준 문서 안에 있다. 클라이언트는 그 문서를 받아 런타임에 넘기는 배관이다.
그래서 서버가 화면을 고치면 클라이언트는 아무것도 안 해도 된다. 서버 4편에서 fontSize 를 88에서 120으로 바꿨을 때 서버를 안 껐던 것처럼, 이쪽도 다시 안 빌드한다.
546 바이트
화면 하나가 546 바이트다. 6줄.
이 숫자가 작아 보이는 게 요점이다. 화면을 옮기는 비용이 작으면 옮기는 게 기본이 된다. 매번 앱을 배포해야 한다면 화면은 앱 안에 있는 게 맞고, 문서 하나 받는 게 전부라면 밖에 있는 게 맞다.
검증 — 클라이언트에 화면이 있으면 실패
run step4 > captures/s4.txt || die "step4: failed"
grep -q 'type "page"' captures/s4.txt || die "what arrived is not a screen"
grep -q 'fontSize' bin/step4.dart && die "step4: the screen is inside the client"bin/step4.dart 에 fontSize 가 한 번이라도 나오면 실패한다.
서버 쪽에도 같은 검사가 있다 — 서버 4편은 서버 코드에 화면이 있으면 실패한다. 양쪽 다 화면을 안 갖고 있어야 이 구조가 성립한다. 한쪽이라도 갖고 있으면 그건 그냥 하드코딩된 앱이다.
직접 돌려보기
dart run bin/step4.dart
bash verify.sh../course-server/ui/desk.json 을 고치고 다시 돌리면 바뀐 크기가 찍힌다. 클라이언트는 안 건드렸다.
가져갈 것
readResource→jsonDecode→ 런타임 세 줄- 클라이언트에 화면 없음 — 위젯도 색도 라벨도
- 양쪽 검사 — 서버와 클라이언트 둘 다 화면을 안 갖는지 본다
다음 편
값이 바뀌었는지 물어보는 걸 그만둔다.
샘플 실행하기
makemind-academy/course_client/git clone https://github.com/makemind-academy/course_client cd course_client dart pub get (cd course-server && dart pub get) dart run bin/step4.dart