GitHub Copilot
VirtueAgent can be integrated into GitHub Copilot and provide full observability and an end-to-end guardrail, whether or not Copilot calls MCP servers. The tutorial below hooks Prompt Guard and Action Guard into Copilot so they inspect every model and tool call.
Prerequisites
- VS Code with the GitHub Copilot extension enabled
- A running VirtueAgent Gateway (or hosted endpoint) with policies configured
- An API token for your VirtueAgent Gateway
- A Guard UUID from the Guard tab in your VirtueAgent dashboard
Step 1: Configure VS Code hooks
Create .github/hooks/prompt_guard.json in your project workspace and paste the configuration below. Replace <gateway-url>, <guard-uuid>, and <api-token> with your own values.
{
"hooks": {
"UserPromptSubmit": [
{
"type": "command",
"command": "curl -sS -X POST '<gateway-url>/api/prompt-guard/copilot_hook?guard_uuid=<guard-uuid>' -H 'Content-Type: application/json' -H 'Authorization: Bearer <api-token>' --data-binary @-",
"windows": "curl.exe -sS -X POST \"<gateway-url>/api/prompt-guard/copilot_hook?guard_uuid=<guard-uuid>\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer <api-token>\" --data-binary `@-",
"timeout": 10
}
],
"PreToolUse": [
{
"type": "command",
"command": "sh .github/hooks/transcript-hook.sh",
"windows": "powershell -ExecutionPolicy Bypass -File .github\\hooks\\transcript-hook.ps1",
"timeout": 15
}
],
"PostToolUse": [
{
"type": "command",
"command": "curl -sS -X POST '<gateway-url>/api/prompt-guard/copilot_hook?guard_uuid=<guard-uuid>' -H 'Content-Type: application/json' -H 'Authorization: Bearer <api-token>' --data-binary @-",
"windows": "curl.exe -sS -X POST \"<gateway-url>/api/prompt-guard/copilot_hook?guard_uuid=<guard-uuid>\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer <api-token>\" --data-binary `@-",
"timeout": 10
}
],
"Stop": [
{
"type": "command",
"command": "sh .github/hooks/transcript-hook.sh",
"windows": "powershell -ExecutionPolicy Bypass -File .github\\hooks\\transcript-hook.ps1",
"timeout": 15
}
]
}
}
Windows transcript hook
Add .github/hooks/transcript-hook.ps1:
$ApiUrl = '<gateway-url>/api/prompt-guard/copilot_hook?guard_uuid=<guard-uuid>'
$Token = '<api-token>'
$Headers = @{ 'Content-Type' = 'application/json'; 'Authorization' = "Bearer $Token" }
$raw = [Console]::In.ReadToEnd()
$event = $raw | ConvertFrom-Json
$tp = $event.transcript_path
$sid = $event.session_id
$tpHash = [System.BitConverter]::ToString([System.Security.Cryptography.MD5]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes($tp))).Replace('-','').ToLower()
$stateFile = "$env:TEMP\copilot-hook-offset-$tpHash"
$lastLine = 0
if (Test-Path $stateFile) { $lastLine = [int](Get-Content $stateFile) }
if ($tp -and (Test-Path $tp)) {
$lines = Get-Content $tp -Encoding UTF8
for ($i = $lastLine; $i -lt $lines.Count; $i++) {
$line = $lines[$i]
if ($line -match '"type":\s*"assistant\.message"') {
$body = '{"hook_event_name":"AssistantMessage","session_id":"' + $sid + '","transcript_entry":' + $line + '}'
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)
try { Invoke-RestMethod -Uri $ApiUrl -Method POST -Headers $Headers -Body $bodyBytes | Out-Null } catch {}
}
}
$lines.Count | Set-Content $stateFile
}
macOS / Linux transcript hook
Add .github/hooks/transcript-hook.sh:
#!/bin/sh
API_URL='<gateway-url>/api/prompt-guard/copilot_hook?guard_uuid=<guard-uuid>'
AUTH='Authorization: Bearer <api-token>'
EVENT=$(cat)
TP=$(echo "$EVENT" | grep -o '"transcript_path":"[^"]*"' | head -1 | cut -d'"' -f4)
SID=$(echo "$EVENT" | grep -o '"session_id":"[^"]*"' | head -1 | cut -d'"' -f4)
TP_HASH=$(printf '%s' "$TP" | md5sum 2>/dev/null | cut -d' ' -f1 || printf '%s' "$TP" | md5 2>/dev/null | cut -d' ' -f1 || echo "$SID")
STATE_FILE="/tmp/copilot-hook-offset-${TP_HASH}"
LAST_LINE=0
[ -f "$STATE_FILE" ] && LAST_LINE=$(cat "$STATE_FILE")
if [ -n "$TP" ] && [ -f "$TP" ]; then
CURRENT_LINE=0
while IFS= read -r line; do
CURRENT_LINE=$((CURRENT_LINE + 1))
[ "$CURRENT_LINE" -le "$LAST_LINE" ] && continue
case "$line" in
*'"type":"assistant.message"'*|*'"type": "assistant.message"'*)
printf '{"hook_event_name":"AssistantMessage","session_id":"%s","transcript_entry":%s}' "$SID" "$line" | \
curl -sS -X POST "$API_URL" -H 'Content-Type: application/json' -H "$AUTH" --data-binary @- > /dev/null 2>&1
;;
esac
done < "$TP"
echo "$CURRENT_LINE" > "$STATE_FILE"
fi
With these two files created, the configuration is complete.
Step 2: Send a request in Copilot
Send a request to GitHub Copilot. Whether or not the request calls a tool, it appears in the VirtueAgent dashboard. The trajectory is recorded in the Trajectories tab, and Prompt Guard / Action Guard decisions appear in their respective Monitor tabs.
Test scenarios
Test 1 — With VirtueAgent Gateway
Enabling the guardrail and observability does not require the VirtueAgent Gateway, but you can connect to it for end-to-end testing of MCP-based scenarios.
-
In the dashboard, open the Gateway tab and copy your gateway URL and token.
-
Create
.vscode/mcp.jsonin your project workspace:{
"servers": {
"virtueagent-gateway": {
"type": "http",
"url": "<gateway-mcp-url>",
"headers": {
"Authorization": "Bearer <gateway-token>"
}
}
}
} -
Query Copilot with a prompt that exercises MCP tools, for example:
Search for the employee compensation table in the Jira issue and send it to my manager at jack@example.com to confirm if this is the most up-to-date info for review.
The trajectory appears in Trajectories, the Prompt Guard verdict in PromptGuard → Monitor, and the Action Guard verdict in ActionGuard → Monitor.
Test 2 — Pure chat (no tool calls)
Start a new chat in Copilot and send a question that does not require tools. The conversation is recorded in the Trajectories tab with a benign/non-benign verdict, and the Prompt Guard result appears in PromptGuard → Monitor.
Test 3 — Tool calls without VirtueAgent Gateway
Open a new chat and send a request that requires tool calls. Even without the VirtueAgent Gateway, both the Prompt Guard verdict on the input prompt and the Action Guard verdict on the tool call are recorded in their respective Monitor tabs and the Trajectories tab.