What did your coding assistant actually do?
A coding assistant might become a problem when it calls tools
I gave the real GitHub Copilot CLI - unmodified, straight from GitHub this prompt, which references an MCP (Model Context Protocol) tool server:
This is a bounded product integration proof. Do exactly this and nothing else:
1. Call release_status from the release-gateway MCP server with this exact run_nonce: <run-nonce>
2. Call release_prepare from the release-gateway MCP server with the same exact run_nonce: <run-nonce>
3. Stop immediately after those two calls and briefly report their outcomes.
Do not run shell commands, read or modify files, call release_publish, retry, delegate,
use subagents, or inspect anything else.
(`<run-nonce>` is a fresh random value minted for each run — more on why below.)
Copilot could see a release-management API exposing release operations over MCP. Between Copilot and everything else stood Talon, an open-source control plane: one self-hosted Go binary serving an OpenAI-compatible model endpoint and an MCP proxy for tool calls. Copilot kept its native protocol on both.
What happened: three model turns on gpt-4o-mini, two tool calls, and a stop — exactly as instructed. The run finished inside its 90-second timeout and left this record (summarized from the signed session):
Use case: coding-assistant
Client: GitHub Copilot CLI
Provider: OpenAI
Model: gpt-4o-mini
Actions: release_status, release_prepare
Session cost: $0.003538
Evidence: 5 valid, 0 invalid recordsThe point of the exercise was never whether Copilot can call tools. It was whether, afterward, the company operating it could answer the questions that arrive with every incident, audit, and invoice:
Which AI use case did the work, and on which model?
What data was detected and changed before it reached the provider?
Which actions reached the release system — and did anything reach it that shouldn’t have?
What did the session cost?
Can the record be verified after the fact, against a published specification?
One session record answered all five. The rest of this post shows how the experiment worked, what it proves, and — just as deliberately — what it does not.
What GitHub already gives you — and where it stops
Any honest pitch starts by steel manning the native controls, because they are real. GitHub ships enterprise administration for Copilot CLI, MCP server allowlisting policies, an agent control plane with agent-attributed audit events, and a genuinely useful client-side permission ladder - --allow-tool, --deny-too, trusted directories. My own experiment uses that ladder.
But look at where each control enforces. The org policies toggle features; the permission flags live in client configuration on each laptop; the audit events tell you an agent acted, in GitHub’s products , and GitHub’s own changelog notes that session-activity coverage for the CLI is still on the roadmap. GitHub’s own documentation is candid about the controls that do not apply to the CLI. What none of these can do, as of this writing, is stand in the request path: inspect the model call that carries your data to the provider, or the tool call that carries an action to your release system. A practitioner audit of the Copilot extension surfaces found the same pattern from the other side: most surfaces ship with no org-level allowlist, no central gate, and no audit trail.
Client-side controls govern what a well-behaved client asks for. Nothing in that stack governs what actually crosses the wire.
Why the gap is dangerous, not just untidy
An agentic coding assistant holds all three ingredients of Simon Willison’s lethal trifecta: access to private data 🔒, exposure to untrusted content ⚠️, and the ability to communicate externally 📡.
The tool channel itself is an attack surface - Invariant Labs demonstrated MCP tool poisoning where the human approves a harmless-looking summary while the model reads a poisoned payload. And asking the model to police itself does not hold: Microsoft’s own measurement found prompt-only guardrails still leaked a 26.67% policy-violation rate, which is why their conclusion matches ours — enforcement has to be deterministic and sit outside the model.
The operational version of the same gap is quieter but just as real. The model provider has one set of logs, the MCP server another, the client a transcript, finance an account-level invoice, and the data-handling rules live in a policy document. When something goes wrong, there is no single answer to
what did this coding assistant actually do?
and the surface is growing: Anthropic counts more than 10,000 active public MCP servers an assistant can be pointed at.
The experiment, step by step
Everything below is reproducible from the public talon-full-demo repository; the Copilot CLI governance guide walks it end to end.
Five local components:
GitHub Copilot CLI - the real client, unmodified, run non-interactively.
Session shim (:8079) ~55-line reverse proxy that stamps two headers — session ID and client label
Talon gateway (:8080) - authenticates the use case, applies the effective policy, calls OpenAI, signs a record per model call.
Talon MCP proxy (:8081) - filters tool discovery, enforces tool policy, forwards allowed calls, signs a record per tool call.
Synthetic release server (:8090) - stand-in release API with release_status, release_prepare, release_publish; performs no real release.
The interesting part was not whether Copilot could use the tools.
Copilot’s model traffic is redirected with its own standard configuration — the same custom-endpoint interception pattern now common for governing coding agents. This uses Copilot CLI’s custom-provider (bring-your-own-key) mode; seat-licensed subscription traffic billed through GitHub’s own OAuth never transits an interceptable endpoint — more on that boundary below.
export COPILOT_PROVIDER_TYPE=openai
export COPILOT_PROVIDER_BASE_URL=http://127.0.0.1:8079/v1/proxy/openai/v1
export COPILOT_PROVIDER_API_KEY="$TALON_CODING_ASSISTANT_KEY"
export COPILOT_MODEL=gpt-4o-miniNote the API key: Copilot receives a Talon agent key representing the coding-assistant use case - never the real OpenAI credential. The gateway resolves that key to the use-case identity, computes the effective policy, retrieves the vaulted OpenAI credential itself, and only then calls the provider. A provider key answers which account may call OpenAI; a Talon agent key answers which company AI use case is making this request, and which policy applies.
Copilot’s tool traffic gets the mirror treatment — its MCP config points at Talon’s proxy, not at the release server:
{
"mcpServers": {
"release-gateway": {
"type": "http",
"url": "http://127.0.0.1:8081/mcp/proxy",
"headers": {
"Authorization": "Bearer <coding-assistant Talon key>",
"X-Talon-Session-ID": "copilot-<run-id>",
"X-Talon-Client": "github-copilot-cli-full-demo"
}
}
}
}Both planes carry the same session ID — supplied by the shim on the model plane and the MCP config on the action plane, and recorded as client-asserted attribution: metadata the client supplies under its authenticated agent key, not proof of process identity (more in the boundary section). That is how five records from two different interception points land in one session:
gateway gpt-4o-mini allow
proxy_tool_call release_status allow
gateway gpt-4o-mini allow
proxy_tool_call release_prepare allow
gateway gpt-4o-mini allowOne more design detail does a lot of work: the synthetic release server appends a receipt for every call that actually reaches it, and every call must carry the run’s fresh random nonce. Those nonce-stamped receipts are ground truth for upstream effect, independent of anything Copilot — or Talon — claims about itself. A stale receipt from yesterday’s run cannot fake a pass.
One record, five answers
Which use case, which model? Every record — model and tool alike — carries agent_id: coding-assistant, the session copilot-20260724T132616Z-5a5ba0, and the client label. A request log shows five API calls; this session record shows one company AI use case using three model turns to perform two release actions.
What happened to the data? The tested run’s records show email and person entities detected in the input and redacted before the request reached OpenAI. That happened because the request physically transited the control plane - data handling enforced in the path, not described in a policy document. (Honest scope: this covers the input path of intercepted traffic, nothing more.)
Which actions? Exactly release_status and release_prepare, each with a signed proxy record and a nonce-matched upstream receipt. No release_publish receipt exists for the run.
What did it cost?
Input: 7,031 tokens
Cache read: 32,128 tokens
Output: 123 tokens
Cost: $0.003538Not commercially meaningful in itself - the structure is the point. An account-level bill says OpenAI cost $20,000 last month; it cannot say which use cases consumed it, which sessions a spike maps to, or whether those sessions also touched internal systems. And there is a difference in kind, not degree, between reporting and enforcement: a cost dashboard tells you on Tuesday that a session overspent on Monday; a gateway budget denies the *next* model request before provider dispatch, a soft cap that preserves completed work while bounding the overrun at request granularity, demonstrated in a separate n8n scenario in the same demo repository.
Can it be verified? That deserves its own section - see it, right below.
The evidence is checked
The presentation/render is not the proof. The signed records are the proof, and the presentation is a readable projection of them.
After the run, the session is exported and verified:
talon audit export --format signed-json --session "$SESSION" --output evidence.json
talon audit verify --file evidence.json
Total records: 5
Valid records: 5
Invalid records: 0
Missing signature: 0
Could not parse: 0
Unsupported: 0The demo’s presenter re-runs the whole chain before rendering anything human-readable: signatures verified, every record confirmed to belong to this session and this use case, the MCP records asserted to contain exactly the two expected tools, the upstream receipts nonce-checked. It fails closed on any mismatch. Records are signed with HMAC-SHA256 and independently verifiable against Talon’s published integrity specification. HMAC is symmetric, so be precise about what that buys: anyone holding the signing key can re-verify every record against the spec alone, which makes the records tamper-evident under the operator’s key custody - not immutable, and not third-party non-repudiation. Reproduce the run yourself and verify your own records; the pass criteria are public.
The same signed records feed several audiences from one source — developers inspect individual records, platform engineering reviews the timeline, FinOps reads spend, security reads data handling and actions — and they feed Talon’s compliance report generators (GDPR Article 30 and EU AI Act Annex IV packs) as supporting evidence for reviews, not a compliance determination.
Where the boundary ends
A control plane must be explicit about what it cannot see. This experiment proves control and evidence for model requests routed through Talon, MCP calls routed through Talon, and tool schemas carried in intercepted model requests. It does not prove control over:
local shell commands and file edits (the demo restricts these with Copilot’s own --deny-tool flags - client-side safeguards, not Talon enforcement)
browser actions
direct APIs that bypass Talon
what a governed tool did after the call was forwarded - Talon evidences the call that crossed the boundary, not the tool’s downstream execution
the correctness of Copilot’s generated answer
the identity of the operating process independently of its presented credentials
The record describes client provenance as client_asserted: operational attribution, not process attestation. One prerequisite is equally explicit: this pattern requires a client that can point at a custom endpoint with a company-held provider key, which Talon injects from its vault. Seat-licensed subscription traffic, billed through the vendor’s own OAuth, does not transit the gateway and cannot be governed this way.
These are not wording details — they determine which additional controls you still need: endpoint security, repository permissions, sandboxing, workload identity (SPIFFE-style), network restrictions. Treat any vendor’s non-interception list as an evaluation criterion; a control plane that won’t show you its boundary is asking you to assume it has none.
Rollout: observe, attribute, enforce
The way in is not “govern every AI system in the enterprise.” It is one existing workflow, three reversible steps:
Observe. 👁️ Point one coding assistant’s endpoint and MCP config at Talon and change nothing else. Developers keep their client; you get the session timeline you didn’t have yesterday.
Attribute. 🏷️ Turn on per-use-case identity and session cost. FinOps gets spend per use case and session; security gets data-handling visibility.
Enforce. 🛡️ Scope the tool surface and set session budgets. Actions outside the approved path are denied at the boundary — with signed denial evidence.
Pilot success criteria, from the run above: developers keep the existing client; model and MCP traffic share one identity and one session; data handling is visible in the record; cost is attributed to the session; every intercepted action is verifiable; no out-of-scope action reaches upstream. First-time setup is about twenty minutes with the guide; budget an afternoon if you include evaluation.
The deliverable that matters is not the first governed workflow - it is that the second one onboards as a config and policy change under an already-reviewed pattern. Security stops approving integrations and starts approving use cases: the review happens once, at the boundary, instead of once per tool.
Run it, then tell us what broke
You have always been responsible for what your assistant does with your credentials. Now you can hold the record of it.
⚡ 60 seconds, no API key: quickstart demo
🧭 20 minutes, the full Copilot scenario: governance guide
🧪 The complete experiment: talon-full-demo )
🦅 Talon (Apache 2.0)
And an open invitation: run this scenario against your own workflow - your assistant, your MCP integration, your policy, and co-author the writeup with us, backed by your own signed records. The strongest post in this category won’t be written by a vendor. It will be written by the first platform team that publishes its evidence.



