Skip to content

Step 5: Combine local and MCP tools

Time: 15 minutes

You’ll use one URL to drive an agent turn that gathers browser evidence with Playwright and gets remediation guidance from the local WCAG catalog.

Agent orchestration is the model choosing and sequencing capabilities to complete a goal. Your session exposes two different tools through one interface:

  • Playwright discovers facts about the live page.
  • The C# catalog explains a matching criterion and remediation.

Both tools report through ToolExecutionStartEvent and ToolExecutionCompleteEvent. Your application can observe the work without knowing how either tool is implemented.

Each tool has one job. Playwright supplies browser evidence, while the local catalog supplies the application’s source-of-truth guidance. The answer is grounded in those sources instead of asking the model to infer both.

The flow is now URL -> Playwright evidence -> WCAG catalog lookup -> grounded response.

Remove the command-line argument validation from Step 4. After the banner and before creating the client, insert:

Console.Write("Enter URL to analyze: ");
var urlInput = Console.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(urlInput))
{
Console.Error.WriteLine("Enter a URL to analyze.");
return;
}
if (!urlInput.Contains("://", StringComparison.Ordinal))
{
urlInput = $"https://{urlInput}";
}
if (!Uri.TryCreate(urlInput, UriKind.Absolute, out var targetUri) ||
targetUri.Scheme is not ("http" or "https"))
{
Console.Error.WriteLine("Enter an absolute HTTP or HTTPS URL.");
return;
}

The Step 4 handler receives this validated targetUri, so the URL boundary still applies.

Replace the final prompt:

Console.WriteLine($"\nAnalyzing: {targetUri.AbsoluteUri}\n");
await ResponseStreamer.SendAndPrintAsync(
session,
$"""
Use browser_navigate to open {targetUri.AbsoluteUri}, then call read_latest_accessibility_snapshot.
Identify two high-confidence accessibility issues supported by the snapshot.
For each issue, call accessibility_rule_lookup and return:
- the browser evidence;
- the matching criterion;
- the catalog's recommended remediation.
""");

The prompt assigns evidence and guidance to their correct sources. It leaves the order of the catalog lookups to the agent.

Terminal window
dotnet run --project workshop-app

Paste this URL when prompted:

https://jamesmontemagno.github.io/workshop-accessibility-agent/target-app/

You should see activity from both kinds of tool:

[tool:start] playwright-browser_navigate
[tool:start] read_latest_accessibility_snapshot
[tool:start] accessibility_rule_lookup
- Evidence: The text input has no accessible name ...
- WCAG criterion: 4.1.2 Name, Role, Value
- Recommended remediation: Associate a visible <label> ...

The exact order and wording can vary. The evidence must come from Playwright, and the criterion must match the catalog.

Troubleshooting this run
SymptomFix
Only Playwright tools runKeep the explicit instruction to call accessibility_rule_lookup for each issue.
The catalog tool runs before browser inspectionThis can be valid planning, but reject any final finding that lacks browser evidence.
The URL is rejectedEnter an HTTP or HTTPS URL; a missing scheme is automatically changed to https://.

You’re ready to shape the report when: one run names a Playwright tool and accessibility_rule_lookup, then connects browser evidence to catalog guidance.

Which tool should discover an input without an accessible name, and which tool should explain the associated WCAG criterion?

Check your answer

Playwright finds the input on the live page. The local catalog returns the application’s source-of-truth criterion and remediation.

Complete Step 5 checkpoint

A complete Step 5 project is available at checkpoints/05-combine-tools.

using GitHub.Copilot;
using HelloCopilotSDK.Helpers;
Console.WriteLine("=== Browser evidence plus application guidance ===\n");
Console.Write("Enter URL to analyze: ");
var urlInput = Console.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(urlInput))
{
Console.Error.WriteLine("Enter a URL to analyze.");
return;
}
if (!urlInput.Contains("://", StringComparison.Ordinal))
{
urlInput = $"https://{urlInput}";
}
if (!Uri.TryCreate(urlInput, UriKind.Absolute, out var targetUri) ||
targetUri.Scheme is not ("http" or "https"))
{
Console.Error.WriteLine("Enter an absolute HTTP or HTTPS URL.");
return;
}
await using var client = new CopilotClient();
await client.StartAsync();
var ping = await client.PingAsync("workshop");
Console.WriteLine($"\nConnected to the Copilot runtime: {ping.Message}\n");
var selectedModel = await ModelSelector.SelectAsync(client);
var workingDirectory = Directory.GetCurrentDirectory();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
Model = selectedModel,
Streaming = true,
OnPermissionRequest = WorkshopPermissionHandler.CreateForTarget(targetUri),
Tools =
[
AccessibilityRuleCatalog.CreateLookupTool(),
PlaywrightSnapshotReader.CreateTool(workingDirectory)
],
AvailableTools =
[
"accessibility_rule_lookup",
"read_latest_accessibility_snapshot",
"playwright-browser_navigate"
],
McpServers = new Dictionary<string, McpServerConfig>
{
["playwright"] = new McpStdioServerConfig
{
Command = "npx",
Args = ["-y", "@playwright/mcp@0.0.78", "--browser=msedge"],
WorkingDirectory = workingDirectory,
Tools = ["browser_navigate"]
}
}
});
Console.WriteLine($"Analyzing: {targetUri.AbsoluteUri}\n");
await ResponseStreamer.SendAndPrintAsync(
session,
$"""
Use browser_navigate to open {targetUri.AbsoluteUri}, then call read_latest_accessibility_snapshot.
Identify two high-confidence accessibility issues supported by the snapshot.
For each issue, call accessibility_rule_lookup and return:
- the browser evidence;
- the matching criterion;
- the catalog's recommended remediation.
""");

Continue to Step 6: Produce a structured report.

Reset all workshop progress?

This clears completed lessons and checked task items stored in this browser. This action cannot be undone.