feat: accept a full page URL so the sandbox opens that exact page - #6
Open
rkttu wants to merge 1 commit into
Open
feat: accept a full page URL so the sandbox opens that exact page#6rkttu wants to merge 1 commit into
rkttu wants to merge 1 commit into
Conversation
A search MCP hands the model a deep URL (say a KB product page). Until now the only way into a sandbox was a catalog service id, so that URL had to be thrown away and the user landed on the bank's homepage — the page they actually wanted was gone. This wires up the TABLECLOTH_TARGET_URL channel end to end. `generate_wsb` / `launch_sandbox` take an optional `targetUrl` (either it or `serviceIds` is required). New `check_url` answers "does this URL need the sandbox?" without running anything, so the model can pick sandbox vs. plain browser. This server still never opens a host browser. No `tablecloth:` URI deep link is emitted — that handler is registered only by the Windows TableCloth app. The output is always a .wsb, and whatever runs it (Windows Sandbox or macSandbox) boots a Windows guest, so both env-var channels behave identically on every host. Template now matches the upstream no-install-spork-deeplink.wsb LogonCommand byte for byte, which also closes a pre-existing drift: shared/wsb-template.xml had its own `__SITEIDS__` statement-level splice instead of the canonical `__SPORK_*` value placeholders. URL handling: - Validate per spec §3.3 (http/https, no credentials, <=2048, no control chars). - Escape both layers: quotes -> %27/%22, then & < > -> XML entities. Also percent-encode non-ASCII: the canonical .wsb requires ASCII only (guest codepage varies by host language pack, and macSandbox routes the command through a .cmd file). - Resolve the URL against the catalog host-side, public-suffix aware. Skipping the KR two-level suffixes would collapse all 95 co.kr services into one registered domain. - On a tie, return candidates instead of guessing. The guest can only take the first catalog entry; the host has a model it can ask, and guessing wrong installs the wrong bank's plugins. - Reject URLs outside catalog domains. The guest would silently drop them and show the catalog UI, which reads to the model as success. Conformance covers 7 URL cases across both lanes plus the ASCII-only property; it caught one real drift (.NET emitted `unknownIds: []` where Node omitted it). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR extends TableClothMcp’s sandbox pipeline to accept and propagate a full deep-link page URL end-to-end (via TABLECLOTH_TARGET_URL), so the sandbox can open the exact requested page instead of only landing on a service’s default entry page. It also adds a read-only check_url tool to classify whether a given URL is sandbox-supported (catalog-backed) without launching anything.
Changes:
- Add
targetUrlsupport togenerate_wsb/launch_sandbox, including URL validation, catalog-based URL→service resolution, and.wsbinjection viaTABLECLOTH_TARGET_URL. - Introduce
check_url(url)tool that returns sandbox support status and (when resolvable) the catalog service + required packages. - Align
.wsbtemplate + serialization behavior (camelCase + omit nulls), and add conformance coverage for URL channel parity across .NET and Node.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| Tools/ToolModels.cs | Extends tool DTOs to include targetUrl, URL-resolution metadata, and check_url response types. |
| Tools/TargetUrl.cs | Adds .NET implementation for target URL validation, escaping, and catalog resolution logic. |
| Tools/SandboxTools.cs | Implements planning logic for (serviceIds, targetUrl) and injects both channels into generated .wsb. |
| Tools/CatalogTools.cs | Adds check_url tool implementation on the .NET side. |
| Tools/AppJsonContext.cs | Registers CheckUrlResponse for source-gen JSON serialization. |
| SPEC.md | Documents the new targetUrl channel contract and the check_url tool. |
| SharedResources.cs | Loads new shared strings used by URL validation/resolution flows and check_url notes. |
| shared/wsb-template.xml | Updates template to include TABLECLOTH_TARGET_URL injection point (__SPORK_TARGET_URL__). |
| shared/strings.json | Adds check_url strings and new sandbox messages for targetUrl behavior/errors. |
| README.md | Documents new tool signatures and provides an example deep-URL flow. |
| node/test/conformance.mjs | Adds cross-implementation parity checks and escaping/ASCII assertions for targetUrl cases. |
| node/src/tools/target-url.ts | Adds Node implementation for validation/escaping/URL→service resolution (parity with .NET). |
| node/src/tools/sandbox-tools.ts | Adds targetUrl planning/injection to Node generate_wsb / launch_sandbox. |
| node/src/tools/catalog-tools.ts | Adds Node implementation of check_url. |
| node/src/index.ts | Registers check_url and updates schemas for generate_wsb / launch_sandbox to accept targetUrl. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+29
to
+38
| public static string ReasonText(RejectReason r) => r switch | ||
| { | ||
| RejectReason.Empty => "empty", | ||
| RejectReason.TooLong => "tooLong", | ||
| RejectReason.BadChars => "badChars", | ||
| RejectReason.NotHttp => "notHttp", | ||
| RejectReason.Credentials => "credentials", | ||
| RejectReason.NoHost => "noHost", | ||
| _ => "unknown", | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
검색 MCP가 물어온 딥 URL(예: 국민은행 특정 상품 페이지)을 그대로 샌드박스에서 열 수 있게
TABLECLOTH_TARGET_URL채널을 끝까지 연결합니다.지금까지는 샌드박스로 들어가는 유일한 입구가 카탈로그 service id였습니다. 그래서 URL은 버려지고 사용자는 은행 첫 화면에 떨어졌습니다 — 정작 보려던 페이지는 사라진 채로요.
도구 변경
generate_wsb/launch_sandbox에targetUrl추가 (serviceIds와 둘 중 최소 하나 필요)check_url(url)— 이 URL이 보안프로그램이 필요한 사이트인지 판정만 하고 아무것도 실행하지 않습니다. 모델이 "샌드박스로 열기 / 그냥 브라우저로 열기"를 고르는 데 씁니다. 이 서버는 여전히 호스트 브라우저를 열지 않습니다.생성되는
.wsb:tablecloth:스킴은 만들지 않습니다그 핸들러는 Windows TableCloth 앱만 등록하므로 macOS/Linux에서 GA가 아닙니다. 이 서버의 산출물은 항상
.wsb이고, 그걸 실행하는 게스트는 Windows Sandbox든 macSandbox든 항상 Windows이므로 환경변수 채널 2개는 모든 호스트에서 동일하게 동작합니다.부수 효과: 기존 드리프트 해소
shared/wsb-template.xml이 정본 대신 자체__SITEIDS__구문 치환을 쓰고 있었습니다. 이제 상류 릴리스 자산no-install-spork-deeplink.wsb의 LogonCommand와 바이트 일치합니다(__SPORK_SITE_IDS__/__SPORK_TARGET_URL__값 자리 치환).URL 처리 규칙 (SPEC.md §7.1 신설)
%27/%22, 그 다음&<>→ XML 엔티티. 비ASCII도 퍼센트 인코딩 — 정본.wsb가 ASCII only를 요구합니다(게스트 코드페이지가 호스트 언어팩마다 다르고, macSandbox는 명령을.cmd파일로 한 번 더 경유시킵니다)co.kr서비스 95개가 한 덩어리가 됩니다candidates반환 — 게스트는 카탈로그 선순위를 택할 수밖에 없지만 호스트에는 되물을 수 있는 모델이 있고, 잘못 고르면 엉뚱한 은행 플러그인이 설치됩니다URL은 Base64 등으로 감추지 않고 평문으로 둡니다 — 실행 전에 어떤 주소가 열리는지 확인할 수 있어야 한다는 정본의 신뢰 모델 그대로입니다.
검증
-warnaserror경고 0 / Node typecheck 통과.wsb가 ASCII를 유지하는지 검증합니다unknownIds: []를 내보내는데 Node는 생략 → "비어 있으면 생략"으로 통일리뷰어가 봐주셨으면 하는 점
$env:두 줄이 늘어난 LogonCommand가.cmd왕복을 견디는지 확인이 필요합니다 —WSB-SUPPORT.md도 "multi-step 명령은 스크립트 파일 권장"이라고 적고 있습니다🤖 Generated with Claude Code