llms.txt and Service JSON-LD on my site, then two AI agents tested it

I made my site readable for AI agents, then sent two. Both got the prices right. One added other people's images; the other missed my project form.

#4
5 min
Nuxt, schema.org JSON-LD, llms.txt, ChatGPT, Claude Code, Playwright

The problem

AI agents now visit sites on people's behalf. They read the page, compare prices and fill in forms (Profound). Until this change, an agent had little to work with on my site: the three services and their "from" prices were on the page but not in structured data, /llms.txt returned a 404, and /lab had no JSON-LD.

What I built

The What I do section already reads its services from one data file, shared/data/homepage.ts. I made three more things read from the same file, so the page, the structured data and llms.txt can't drift apart.

Service JSON-LD on the homepage. One Service per service, each with an Offer holding its "from" price as minPrice, the currency and the pricing model. A service with no confirmed price gets no Offer. This is the AI App Rescue block as the live site serves it:

{
  "@context": "https://schema.org",
  "@type": "Service",
  "name": "AI App Rescue",
  "description": "A 48-hour audit of the app's security, auth, database rules and performance, with a prioritized fix plan. Then, when you want it, a rescue sprint that takes the app from fragile demo to production.",
  "provider": {
    "@type": "Person",
    "name": "Ahmed Saad",
    "url": "https://amdsaad.com/"
  },
  "offers": {
    "@type": "Offer",
    "description": "fixed price, 48-hour audit",
    "priceCurrency": "USD",
    "priceSpecification": {
      "@type": "PriceSpecification",
      "minPrice": 299,
      "priceCurrency": "USD"
    }
  }
}

/llms.txt, generated, not written by hand. It has my short bio, the services with "from" prices, the booking link, the email, the Fiverr rating and the published Lab entries. It's cached like the sitemap (Cache-Control: s-maxage=60, stale-while-revalidate). One section of the live file:

## Work with me

- [Book a 20-min call](https://cal.com/ahmed-saad-soliman-axzyti/20-min-meeting)
- Email: hello@amdsaad.com
- [Fiverr](https://www.fiverr.com/amdsaad): Rated 5.0 from 52 reviews

Fiverr and Bluesky in the Person sameAs. Bluesky waited until its profile URL was confirmed in my brand facts file. No fact goes on the site unconfirmed, even an obvious URL.

CollectionPage JSON-LD on /lab, listing the published entries as BlogPostings.

Then I sent two agents

Test 1: ChatGPT with search

I asked ChatGPT, with search on, what Ahmed Saad offers. The text was accurate: all three services, the "from" prices ($2,500, $1,500 and $299), the 4 care hours, the first reply within 1 business day, the three model providers, and 2–4 weeks for one AI feature.

But it put a picture next to each service, and none of them is on my site. They came from its own image search. Two I traced: a Dribbble shot (cdn.dribbble.com) and an image from a Webflow site (cdn.prod.website-files.com). A reader could take them for my work.

ChatGPT's answer listing my three services and their prices, with the three pictures it added blurred

Test 2: Claude Code driving a browser

I gave Claude Code (Sonnet 5.5) a task written as a client:

I have an AI-built app that's slow and might be leaking data. Go to amdsaad.com, find the right service, tell me what it costs and what's included, then open the project form and fill it in for me. Don't send it until I say.

Claude in Chrome wasn't connected, so it drove a separate browser through Playwright.

It picked AI App Rescue and got the price, what's included and what isn't right. It also caught a real gap: the page gives no price for the rescue sprint. My brand facts have rescue sprints from $1,500, but the card doesn't show it.

Then it said: "AI App Rescue is the right service, but the site has no project form to fill in."

That's wrong, and it's the most useful finding. Every "Tell me about your project" trigger is a mailto link in the server HTML, so the site works without JavaScript. From the live page, with the class list removed:

<a href="mailto:hello@amdsaad.com" rel="noopener noreferrer">Tell me about your project (AI App Rescue)</a>

Once the JavaScript loads, a click opens the form dialog. The agent read the mailto:, didn't click, and offered an email draft or a cal.com booking instead. What works for people and for visitors without JavaScript hid the form from an agent.

One caveat: the agent ran inside my repo folder and read a source file, so it wasn't a clean outside visitor.

What I learned

  1. An agent can read your facts correctly and still fill the gaps with other people's images. Two of my services have no image yet. That empty space didn't stay empty.
  2. Agents act on what the HTML says, not on what the JavaScript will do. A link that turns into a dialog after the page loads is, to an agent, just a link.
  3. One data file for the page, the JSON-LD and llms.txt held up. Every price both agents quoted matched the page.

What happened next

I built the fixes and ran the test again: Rerunning the AI agent test: a real button, and a hidden copy of it.

Built your app with an AI tool and not sure it's safe to run? Tell me what you're working with.

Privacy

One email per entry. Unsubscribe any time.