Detection
How the Agentronics SDK spots agents on the page — self-declared, browser automation, vision models, WebMCP and crawlers.
Detection
Detection labels traffic; it never blocks it. The SDK uses five sources, from strongest to weakest:
| Source | Class | How | Confidence |
|---|---|---|---|
| Self-declaration | any | The agent names itself (presentIdentity(), declareAgent(), an X-Agent header) | trust: 'declared' — a claim |
| WebMCP activity | webmcp | An agent activates or calls one of your tools through document.modelContext | Exact — confidence: 1 |
| Vision behaviour | screenshot | How the visitor clicks and types (see below) | Scored, 0.6–1 |
| DOM / headless | dom | Automation fingerprints — navigator.webdriver, ChromeDriver / Playwright / Puppeteer globals, headless UA | Scored, 0.4–1 |
| Crawler User-Agent | crawler | navigator.userAgent matches a known bot | 0.9 — spoofable; verify on the server |
init() runs detection automatically (autoDetect, on by default) and keeps the result in client.agentContext, so every tool call is attributed without you passing identity around. Call client.detect() to re-run it.
WebMCP agents
document.modelContext exists for every visitor of a WebMCP-capable browser (Chrome and Edge origin trials, ChatGPT Desktop, …), so its presence alone says nothing about an agent. The SDK reports a WebMCP agent once one acts: a toolactivated event, or a call to a tool you published with publishToWebMcp(). From then on the session is attributed to that agent (unless a stronger, declared or verified identity is already in place).
webMcpSupport() tells you whether the page can serve tools at all: 'document', 'navigator' (the old alias, removed in Chrome 152), 'legacy' (old polyfills) or null.
Vision-model agents
Computer-use agents (ChatGPT agent, Claude computer use, Gemini computer use, …) look at screenshots and drive a real browser with synthetic mouse and keyboard input. They often pass DOM checks, but they interact differently from people:
- teleport clicks — a click with no pointer movement before it;
- centre clicks — clicks within a pixel or two of an element's exact centre, again and again;
- metronome typing — key intervals with almost no variance, or text inserted with no key presses;
- no hover, no wheel — clicks and scroll jumps with no mousemove or wheel events between;
- VM-shaped viewport — exact sandbox sizes such as 1024×768 or 1280×800 (a weak signal on its own).
The monitor scores these over a short window and reports a screenshot agent only when several agree after at least three interactions. It reads event timing and coordinates — never key values or text — and ignores synthetic (isTrusted: false) events, which are DOM automation instead.
DOM / headless
A weighted bag of signals: navigator.webdriver, ChromeDriver's cdc_* globals, Playwright / Puppeteer / Selenium / PhantomJS / Nightmare globals, a headless User-Agent, empty navigator.languages or plugins on desktop, a Chrome UA without window.chrome. Raise dom.threshold (default 0.4) to be stricter.
Crawlers
detectCrawler() matches navigator.userAgent against AI crawlers (GPTBot, OAI-SearchBot, ChatGPT-User, Claude-User, Claude-SearchBot, ClaudeBot, PerplexityBot, Perplexity-User, Google-Extended, Applebot-Extended, Amazonbot, Bytespider, CCBot, Meta-ExternalAgent, cohere-ai) and search crawlers (Googlebot, Bingbot, DuckDuckBot, YandexBot, Baiduspider, Applebot). In the browser it only sees crawlers that run JavaScript and can't tell a real GPTBot from a spoofed one — verify crawlers on the server with their published IP ranges.
Tuning
What we deliberately don't do
- No canvas, audio or font fingerprinting, and no reading what visitors type.
- No retries to defeat anti-detection tooling. An agent that hides well browses as a human — and is still welcome.
- Detection never blocks. Use authorization rules to decide what each kind of agent may call.