On September 17, 2026, GitHub added Copilot CLI customization activity to its usage metrics API. Administrators can now examine activity for skills, custom agents, MCP servers, slash commands, and plugins. Security teams gain a useful investigation lead, but an MCP connection count cannot establish which tools ran or what they changed.
Use the new report to choose where to look. Identify the relevant configuration, then collect execution evidence for the question under review. A dashboard that drops these distinctions can give an auditor false confidence about what the agent did.
This guide combines GitHub's documented reporting behavior with our recommended evidence-handling procedure. General Analysis has not tested the integration against a Copilot enterprise tenant. For the surrounding rollout controls, see securing coding agents.
Collect a report with a defined scope#
Start with an enterprise or organization and a complete reporting day. Keep that scope attached to every exported record. A team investigation and an enterprise-wide review need different populations; a total without its population is difficult to reconcile later.
GitHub's September 17 announcement puts the fields in aggregate and per-user one-day reports, per-user 28-day reports, and the day_totals records of aggregate 28-day reports. The Copilot usage metrics policy must be enabled. The release does not promise that every tenant immediately receives every field.
For the enterprise aggregate endpoint, GitHub's REST reference lists enterprise owners, billing managers, and authorized metrics readers. Classic tokens need manage_billing:copilot or read:enterprise; supported GitHub App tokens use the read-level Enterprise Copilot metrics permission. Use your approved reporting identity.
This read-only request assumes an authenticated GitHub CLI. Replace the enterprise slug and date with your reporting scope.
Code source: GitHub's enterprise daily-report endpoint, adapted for GitHub CLI.
gh api --method GET \
-H 'Accept: application/vnd.github+json' \
-H 'X-GitHub-Api-Version: 2026-03-10' \
'/enterprises/ENTERPRISE/copilot/metrics/reports/enterprise-1-day' \
-f day='2026-09-17' > report-links.json
The response contains expiring download_links. Retrieve every returned file through your approved downloader and retain the NDJSON privately. Record the returned reporting day, collection time, scope, and file hashes; keep signed URLs out of tickets and public logs. Record a failed request or missing download as a collection failure.
See how your AI systems hold up under real attacks
General Analysis maps AI applications and agents, red teams prompts, retrieval, tools, MCP servers, browser actions, permissions, and business workflows, then turns findings into evidence your team can reproduce and retest.
Preserve the difference between missing and empty#
The field reference defines a limited view. Each customization array contains at most five entries. Customer-defined names become other, except custom slash commands, which use custom. Distinct counts retain identifiers beyond those visible entries. Null or absent fields mean unavailable data; present empty arrays and zero distinct counts mean no matching activity was recorded.
Keep the raw values. Do not replace every missing array with [] or every missing count with 0 before assessing coverage.
This illustrative jq projection reads already-downloaded one-day NDJSON records and labels MCP coverage. GitHub's example schema places these fields at the record's top level. The labels below are our own, not GitHub API fields.
Code source: illustrative.
jq -c '{
day, enterprise_id, organization_id, user_id,
totals_by_mcp, distinct_mcp_use_count,
review_state: (
if .totals_by_mcp == null or .distinct_mcp_use_count == null
then "coverage_unavailable"
elif .totals_by_mcp == [] and .distinct_mcp_use_count == 0
then "no_matching_activity_recorded"
else "needs_corroboration"
end
)
}' report.ndjson
Keep separate input files for different scopes and report types. This example does not unpack a 28-day day_totals wrapper, validate the complete schema, or classify a device as safe. Retain unexpected combinations for investigation instead of coercing them into a clean result. If no rows arrive, the projection emits nothing; the collection job must detect that separately.
Choose the evidence the count cannot supply#
GitHub's interpretation guide makes two counting rules explicit: MCP activity counts successful and failed connection attempts, including reconnects; plugin activity counts plugin-associated skill invocations and already contributes to skill totals. Adding plugin and skill counts double-counts overlapping activity.
Use those definitions to choose a follow-up, rather than assigning one generic risk score to every interaction:
| Report observation | Question to investigate | Evidence to request |
|---|---|---|
| MCP connection count rises | Reconnection trouble, a rollout, or newly configured access? | Configuration revision and connection errors for the same period |
MCP activity appears as other | Which configured servers could account for it? | Approved catalog and device configuration, with collection dates |
| A plugin appears in the report | Which installed artifact supplied the invoked skill? | Plugin source, approved revision, and installed-copy evidence |
| An expected entry is absent | Is it outside the displayed entries, or is collection incomplete? | Raw fields, reporting scope, and collection status |
An other entry may represent approved internal tooling. Its appearance alone is insufficient reason to block a developer. Keep the case open for identification, with the catalog or device owner responsible for supplying the missing evidence.
Distinct counts also require care: an organization-level identifier counts once even when several users interact with it. Do not sum per-user counts to reconstruct the organization total or sum daily distinct counts into a unique monthly inventory. Keep the source aggregate for its own scope and time period. GitHub documents the aggregation distinction.
Work an unknown-server case through to a decision#
Consider a hypothetical review where a team's daily report shows MCP activity under other after a rollout. The approved change added an internal issue-tracker server. That is a plausible explanation; the report does not identify it.
Ask the rollout owner for the approved configuration and the device inventory from that period. Compare the intended server with what was configured. If they match, record that the configuration is accounted for. The snapshot establishes available authority; execution still needs its own evidence.
If the task is to establish whether the agent changed an issue, inspect the relevant trace and the issue tracker's own history. A connection attempt cannot answer that question. If the task is instead to establish plugin provenance, follow the installed-artifact procedure; a usage count cannot verify a checkout.
Add runtime evidence without assuming an identity join#
Copilot CLI's OpenTelemetry reference documents execute_tool spans with a tool name and call identifier. Agent spans carry session context and can include a pseudonymous enduser.pseudo.id. Tool arguments and results require content capture.
The usage report's user_id and a trace's pseudonymous identifier are different fields. The cited documentation does not establish that they are interchangeable. Confirm your own authorized identity mapping before joining them. Preserve session and device provenance in the collector where available; a shared timestamp alone is a weak attribution.
GitHub's OTel deployment guidance supports managed telemetry configuration across supported clients. It also warns that optional content capture can include sensitive prompts and tool inputs. Start with the metadata needed for the investigation, restrict access to the collector, and define retention before enabling broader content collection.
For a reported tool execution, check the downstream effect separately. A trace can help identify the attempted call; the destination's record answers whether the expected write occurred. If either source was never collected, mark that part of the case unresolved. Newly enabling telemetry does not supply missing historical observations.
Close only the question the evidence answers#
Keep the reporting decision separate from enforcement. Use managed Copilot permissions for supported operation restrictions and the MCP security review for server authority and tool exposure.
Before closing a review, attach the reporting scope and day, raw-field coverage, configuration provenance, and any execution or destination records used in the decision. State what those records establish. Assign an owner to missing evidence instead of converting an unknown server or an uncollected trace into a clean audit result.
Frequently asked questions
- Can Copilot CLI usage metrics identify every MCP server?
No. The arrays show up to five entries, and customer-defined server names are grouped under other. Distinct counts include identifiers outside the displayed entries. Use approved configuration and endpoint records to identify the actual servers.
- Does a missing MCP entry mean a server was unused?
No. It may fall outside the top five, and unavailable fields must remain unknown. A present empty array with a zero distinct count means no matching activity was recorded, which still does not prove that every device reported.

