Time: 10 minutes
You'll run the complete application and explain its state, tool boundaries, permission boundary, and report limitations.
:::language dotnet The finished application is an agent host. Its session coordinates a model, an application-owned function, and a browser running in another process:
Console application
|
+-- CopilotClient -------- runtime connection
|
`-- CopilotSession --- one conversation and its context
|
+-- accessibility_rule_lookup
| same process, application-owned data
|
`-- Playwright MCP
separate process, scoped permission handler
|
`-- Browser target
:::
:::language nodejs The finished application is an agent host. Its session coordinates a model, an application-owned function, and a browser running in another process:
Node.js application
|
+-- CopilotClient -------- runtime connection
|
`-- CopilotSession --- one conversation and its context
|
+-- accessibility_rule_lookup
| same process, application-owned data
|
`-- Playwright MCP
separate process, scoped permission handler
|
`-- Browser target
The completed report is also in
finished/nodejs/accessibility-report.
:::
:::language python The finished application is an agent host. Its session coordinates a model, an application-owned function, and a browser running in another process:
Python application
|
+-- CopilotClient -------- runtime connection
|
`-- session ---------- one conversation and its context
|
+-- accessibility_rule_lookup
| same process, application-owned data
|
`-- Playwright MCP
separate process, scoped permission handler
|
`-- Browser target
The completed report is also in
finished/python/accessibility-report.
:::
:::language go The finished application is an agent host. Its session coordinates a model, an application-owned function, and a browser running in another process:
Go application
|
+-- Client ---------------- runtime connection
|
`-- Session ---------- one conversation and its context
|
+-- accessibility_rule_lookup
| same process, application-owned data
|
`-- Playwright MCP
separate process, scoped permission handler
|
`-- Browser target
:::
:::language rust The finished application is an agent host. Its session coordinates a model, an application-owned function, and a browser running in another process:
Rust application
|
+-- Client ---------------- runtime connection
|
`-- Session ---------- one conversation and its context
|
+-- accessibility_rule_lookup
| same process, application-owned data
|
`-- Playwright MCP
separate process, scoped permission handler
|
`-- Browser target
:::
:::language java The finished application is an agent host. Its session coordinates a model, an application-owned function, and a browser running in another process:
Java application
|
+-- CopilotClient -------- runtime connection
|
`-- session ---------- one conversation and its context
|
+-- accessibility_rule_lookup
| same process, application-owned data
|
`-- Playwright MCP
separate process, scoped permission handler
|
`-- Browser target
:::
Understanding these boundaries lets you reuse the design in another application instead of only reproducing the workshop code. A database lookup, deployment service, or issue tracker may use different tools, but the same ownership and trust questions apply.
:::language dotnet
The complete flow is
URL -> Playwright inspection -> C# WCAG lookup -> structured accessibility report.
:::
:::language nodejs
The complete flow is
URL -> Playwright inspection -> TypeScript WCAG lookup -> structured accessibility report.
:::
:::language python
The complete flow is
URL -> Playwright inspection -> Python WCAG lookup -> structured accessibility report.
:::
:::language go
The complete flow is
URL -> Playwright inspection -> Go WCAG lookup -> structured accessibility report.
:::
:::language rust
The complete flow is
URL -> Playwright inspection -> Rust WCAG lookup -> structured accessibility report.
:::
:::language java
The complete flow is
URL -> Playwright inspection -> Java WCAG lookup -> structured accessibility report.
:::
There is no code to change. Keep the Step 6 implementation in place so this run tests the application you built.
:::language dotnet
dotnet run::: :::language nodejs
npm start -- "{{TARGET_APP_URL}}"::: :::language python
python main.py "{{TARGET_APP_URL}}"::: :::language go
go run . "{{TARGET_APP_URL}}"::: :::language rust
cargo run -- "{{TARGET_APP_URL}}"::: :::language java
./mvnw compile exec:java -Dexec.args="--allow-local-demo-mcp {{TARGET_APP_URL}}"Java local-demo warning: This explicit flag is a temporary workaround for github/copilot-sdk#2273. Without it, the callback fails closed unless it can verify the exact URL from the permission payload. With it, the session approves only the
mcppermission kind, one request at a time, under the configured Playwrightbrowser_navigateallowlist; it cannot enforce the exact target. Use it only for the controlled local workshop target, never for production, shared, or untrusted URLs. ::: Use the workshop target:
{{TARGET_APP_URL}}
Watch for all five stages:
- The client connects and creates one session.
- Playwright navigates to the exact target and creates an accessibility snapshot.
- The narrow local reader returns that current-run snapshot.
- The local catalog is called for browser-supported findings.
- The response follows the report contract and states its limits.
:::language dotnet Your transcript will vary, but it should have this shape:
=== Accessibility Report Generator ===
Enter URL to analyze: {{TARGET_APP_URL}}
Connected to the Copilot runtime: ...
Analyzing: {{TARGET_APP_URL}}
[tool:start] browser_navigate / playwright-browser_navigate
[tool:done] success=True
[tool:start] read_latest_accessibility_snapshot
[tool:done] success=True
[tool:start] accessibility_rule_lookup
[tool:done] success=True
...
# Accessibility review
## Finding 1: ...
- Evidence: ...
- WCAG criterion: ...
- Recommended remediation: ...
## Review limits
...
:::
:::language nodejs Your transcript will vary, but it should have this shape:
[tool:start] browser_navigate
[tool:done] success=true
[tool:start] read_latest_accessibility_snapshot
[tool:done] success=true
[tool:start] accessibility_rule_lookup
[tool:done] success=true
...
# Accessibility review
## Finding 1: ...
- Evidence: ...
- WCAG criterion: ...
- Recommended remediation: ...
## Review limits
...
streamResponse prints tool start/done lines and streams the assistant text to stdout.
:::
:::language python Your transcript will vary, but it should have this shape:
[tool:start] browser_navigate
[tool:done] success=True
[tool:start] read_latest_accessibility_snapshot
[tool:done] success=True
[tool:start] accessibility_rule_lookup
[tool:done] success=True
...
# Accessibility review
## Finding 1: ...
- Evidence: ...
- WCAG criterion: ...
- Recommended remediation: ...
## Review limits
...
main.py launches report.main, which waits on session.idle after streaming deltas.
:::
:::language go Your transcript will vary, but it should have this shape:
# Accessibility review
## Finding 1: ...
- Evidence: ...
- WCAG criterion: ...
- Recommended remediation: ...
## Review limits
...
Explain that Client owns the Copilot CLI lifecycle, the Session owns one conversation, and the
permission handler gates external navigation. The expected report is evidence-bound.
:::
:::language rust Your transcript will vary, but it should have this shape:
# Accessibility review
## Finding 1: ...
- Evidence: ...
- WCAG criterion: ...
- Recommended remediation: ...
## Review limits
...
Explain that Client manages the runtime, Session dispatches events, typed tools are app-owned,
and the permission handler trusts only exact navigation.
:::
:::language java Your transcript will vary, but it should have this shape:
# Accessibility review
## Finding 1: ...
- Evidence: ...
- WCAG criterion: ...
- Recommended remediation: ...
## Review limits
...
Explain that Maven compiles the Java 17 application, CopilotClient manages the runtime, and tools
remain scoped. By default the permission callback accepts only the canonical URL; with the explicit
local-demo flag it is limited to the configured mcp kind but cannot verify that URL.
:::
The controlled target intentionally includes browser-observable issues: a missing text alternative,
no main landmark, an illogical heading sequence, and a textbox without an accessible name.
Compare the report with the
published target HTML;
do not accept a finding that is absent from both the snapshot and source.
Troubleshooting the complete run
| Symptom | Fix |
|---|---|
| A known issue is omitted | Agent output can vary. Rerun once, but require evidence rather than forcing a predetermined answer. |
| A reported issue is not in the page | Reject it as ungrounded; the prompt requires specific browser evidence. |
| A tool is denied | Check that browser_navigate uses the exact entered target. |
| The reader finds no snapshot | Keep the prompt order: navigate before calling read_latest_accessibility_snapshot. |
| The runtime cannot start | Re-authenticate with copilot login, confirm the CLI is on PATH, and retry the run command for your language. |
This step is complete when: the report is grounded, the tool names are visible, and you can answer the architecture questions below without reading the code.
- What state belongs to the session?
- Why is the WCAG catalog local?
- Why is Playwright external?
- Where are permissions enforced?
- What changes when another MCP server is added?
Compare your explanation
- The session owns one conversation's messages, model response, and tool results.
- The application owns the catalog data and deterministic lookup, so the function stays local.
- Playwright is a reusable browser capability with its own Node.js process and dependencies.
- The MCP tool allowlist exposes only navigation, and the permission handler approves only the exact target. The trusted local reader accepts no path and reads only a new generated snapshot; the catalog is also read-only. Those application-owned tools skip permission.
- Add the server configuration, expose only needed tools, define its trust policy, and keep observing its calls through the same session event stream.
Continue to Step 8: Select a model, then turn your findings into an interactive HTML report in Step 9.
The workshop application runs on your machine. These pages cover what changes when the same design moves somewhere else.
- Backend services: running the SDK server-side against a headless CLI instead of a local one.
- Scaling and multi-tenancy: horizontal scaling and the isolation patterns that keep one user's session out of another's.
- OpenTelemetry instrumentation: tracing tool calls and turns once the agent runs where you cannot watch the terminal.
- Microsoft Agent Framework integration: placing a Copilot session inside a larger multi-agent workflow.