TRUE GEO ENGINE

Inspiration

Search stopped being the only front door. People now ask ChatGPT, Gemini, Perplexity, and Claude what to buy, who to trust, and which vendor to shortlist — and a brand either appears in that answer or it doesn't. Marketing teams have twenty years of tooling for the blue-links era and almost nothing for this one.

The tools that do exist have a credibility problem. Most GEO dashboards burn API budget hammering four models with the same prompt, then present whatever came back as if it were a measurement. Run it twice and the numbers move. That isn't visibility analytics — it's a screenshot of a coin flip.

We wanted the opposite: an evidence workspace where the same input always produces the same output, every synthetic value is labeled as synthetic, and no .env file, API key, or paid inference is needed to see exactly how the reasoning works. If a claim can't be traced to a rule and a row, it shouldn't appear on the screen.

What it does

TRUE GEO ENGINE is a brand- and industry-agnostic GEO/AEO workspace with two independent workflows that never share evidence.

AI Visibility takes one public page URL as a deterministic seed and generates a synthetic raw-HTML versus post-hydration comparison — without fetching the target page. The post-hydration state supplies one ordered master list of h1–h6, p, li, figcaption, blockquote, and td blocks. Both columns render that identical list in identical order, and each block is marked Fully extractable or Not visible to AI by signature membership:

$$\text{status}(b) = \begin{cases} \text{Fully extractable} & \text{if } \sigma(b) \in \Sigma_{\text{raw}} \ \text{Not visible to AI} & \text{otherwise} \end{cases}$$

where $\sigma(b)$ is the normalized signature of block $b$ and $\Sigma_{\text{raw}}$ is the signature set of the synthetic raw HTML. Metadata, Schema.org JSON-LD types, and FAQ schema live in a separate machine-readable-signals section so structural signals never inflate the content score. Four deterministic crawler perspectives vary the evidence feeding answer-engine scores. Private-network URLs are rejected.

Run a Prompt replays a selected prompt from a bundled fictional benchmark — no LLM call, no API key, no page URL, no external request. The catalog holds eight scenarios across automotive, B2B technology, financial services, healthcare, travel, telecommunications, consumer products, and education. Each carries one high-intent prompt and measured brand, a benchmark answer, three competitors, four evidence sources, four answer-engine interpretations, and six sentiment topics. At runtime it expands to

$$8 \text{ prompts} \times 4 \text{ engines} \times 6 \text{ dimensions} = 192 \text{ deterministic evidence rows}$$

from which the pipeline computes visibility, coverage and citation rate, sentiment and recommendation quality, competitor leaders, ranked answer gaps, and next steps.

Both workflows populate the same five views — AI visibility, answer engine scores, content/answer gaps, next steps, and agent activity — while results stay isolated, so URL evidence is never relabeled as prompt evidence.

Eight WebMCP tools are registered imperatively in the browser, letting an agent drive the workspace directly:

Tool Purpose
geo_ai_visibility_audit Generate the synthetic URL-scoped page analysis
geo_analyze_prompt Replay one exact prompt from the curated benchmark
geo_visibility_overview Read the active score and evidence components
geo_list_tools Export the browser WebMCP catalog
geo_content_gaps Explain one ranked gap from active evidence
geo_generate_next_steps Open page- or prompt-specific next steps
geo_download_package Download the active evidence package
geo_export_action_plan Export the active action plan

Each workflow exports a 16-file ZIP — disclosure, consolidated llms.txt, FAQs, JSON-LD and schema, preview HTML, the block audit or benchmark answers, scores, gaps, action plan, and WebMCP manifests.

How we built it

Next.js and TypeScript, with Drizzle for schema and a Vite-driven test setup. The whole app runs from npm run dev with no .env, no key, and no network dependency for either challenge workflow.

Determinism layer:

  • lib/synthetic-page-scan.ts — URL-derived raw and hydrated fixtures plus the four simulated crawler perspectives
  • lib/page-audit.ts — the ordered block classifier and structural-signal extractor
  • lib/visibility-scanner.ts — URL-scoped prompt and score analysis fed by those fixtures

Benchmark layer:

  • components/true-geo/demo-prompts.json — the curated benchmark, single source of truth
  • components/true-geo/demo-prompts.ts — deterministic expansion into the 192 evidence rows

Interface layer:

  • components/true-geo/TrueGeoApp.tsx — isolated workflow state and all tab wiring
  • components/true-geo/PageVisibilityAudit.tsx — the aligned Human vs AI block view
  • components/true-geo/PromptBar.tsx — mode pills, prompt selector, URL input
  • components/true-geo/page-audit.ts — the audit data model and client normalization

WebMCP layer: components/true-geo/webmcp.ts registers directly against the browser contract, and tool-manifest.ts keeps a portable catalog of the same eight tools so the manifest can travel with an export:

document.modelContext.registerTool({
  name: entry.name,
  title: entry.title,
  description: entry.description,
  inputSchema: entry.inputSchema,
  execute: async (input) => JSON.stringify(await execute(entry.name, input)),
});

API surface: POST /api/page-audit { url }, plus deterministic prompt and visibility endpoints. services/page-renderer/ holds an optional local Playwright reference for future live auditing — deliberately not wired into the public routes.

Challenges we ran into

Keeping two workflows from bleeding into each other. Both feed the same five views, which makes cross-contamination easy and disastrous — a page-derived gap shown under a prompt run is a fabricated claim. We had to make workflow isolation a property of the state model itself rather than something the UI remembers to respect.

Making a schema that can't be misused. geo_analyze_prompt was an obvious injection point: give it a free-text field and an agent will pass an arbitrary prompt the benchmark has no data for, and the pipeline will happily score it. We constrained the field to the eight bundled catalog values and removed any URL or model parameter, so the tool cannot be talked into analyzing something that doesn't exist.

Honest columns are harder than pretty ones. The first Human vs AI view built each column from its own source, which meant the two lists drifted out of alignment and the comparison stopped meaning anything. Deriving both columns from one ordered master list and marking extractability per block was more constrained but is the only version where a row-by-row read is truthful.

Labeling everything without burying the product. Every synthetic value and every downloaded file needed a disclosure, and it would have been easy to end up with a workspace that reads like a legal notice. Getting "verify before publishing" to sit visibly on results and exports without drowning the analysis took more iteration than the scoring math.

Accomplishments that we're proud of

  • Reproducible by construction — the same URL or prompt always yields the same evidence, so two people looking at one run see one set of numbers
  • Zero-key, zero-quota, zero-network for both challenge workflows; a reviewer clones, runs npm run dev, and sees the full product
  • 192 evidence rows from a transparent expansion, not from an opaque model call
  • Eight WebMCP tools registered imperatively, plus a portable manifest that ships inside the exports
  • A schema that refuses out-of-catalog input rather than scoring it anyway
  • SSRF-conscious from the start — private-network URLs are rejected at the boundary
  • Synthetic labeled as synthetic, everywhere, including inside every one of the 32 exported files

What we learned

Determinism is a feature users can feel. We expected reviewers to treat the simulated benchmark as a limitation. In practice, being able to re-run a prompt and get the identical answer made the tool feel more trustworthy than a live-model version would have.

A tool schema is an access-control surface. Once an agent can call your functions, an unconstrained string field is an open door. Enumerating valid values isn't a UX nicety — it's what stops the system from producing confident output about data it doesn't hold.

Separating structural signals from content signals matters. JSON-LD and FAQ schema are real advantages, but folding them into a content-extractability score hides the actual problem: text that only exists after hydration. Keeping the two apart makes the diagnosis actionable.

Optional infrastructure should stay unwired. Keeping the Playwright renderer in the repository but out of the public routes let us keep the live-audit path visible as a direction without letting it become an undeclared runtime dependency.

GEO is fundamentally a content problem. Almost every gap the engine surfaces resolves to the same thing — the answer a model needs is either missing, buried behind JavaScript, or phrased so it can't be lifted into a response.

What's next for TRUE GEO ENGINE

Real page fetching, carefully. Promote services/page-renderer/ into a first-class path with a live raw-versus-hydrated diff of actual pages, behind explicit consent, robots respect, rate limiting, and the same SSRF guards.

Live answer-engine sampling as an opt-in layer. Keep the deterministic benchmark as the default and free tier; add real ChatGPT, Gemini, Perplexity, and Claude sampling as a separate, clearly-labeled mode with its own key management — never mixed into the same view as synthetic evidence.

Tracking over time. A single score is a snapshot. Scheduled re-runs, deltas, and alerts when a competitor overtakes a brand on a tracked prompt are what turn this into a workspace people return to.

Custom prompt sets per brand. Let teams define their own high-intent prompts and competitor sets instead of using the eight bundled scenarios, with the same enumeration guarantee applied to their catalog.

Close the loop on remediation. Today the export is a draft package a human implements. Next is verifying the change actually landed and re-measuring, so the action plan becomes a cycle rather than a handoff.

Publish the WebMCP manifest as a standard. The portable eight-tool catalog is already export-ready; making it a documented contract would let other GEO tools expose the same agent surface.

Share this project:

Updates

Submission history