Pages

03 November, 2026

Sitecore.MarkdownForAI: An Ask AI Button for Sitecore MVC Pages

More and more readers do not read an article to the end anymore. They copy it into ChatGPT or Claude and ask their own questions. I wanted to make that easy on Sitecore sites, so I built a small open-source module: Sitecore.MarkdownForAI. It adds an Ask AI button to your pages, and gives the AI assistant a clean copy of the page to read.

It works on Sitecore 10.1 to 10.5 with MVC renderings.

The Problem: AI Reads Your Footer Too

When an AI assistant opens an article page, it gets the full HTML: the mega menu, the search box, the cookie banner, the footer with forty links and kilobytes of script. The article is in there somewhere. The assistant has to dig it out, spends its context on the noise, and sometimes answers from the footer instead of the article.

What the Module Does

Two things:

  • An Ask AI button. Readers pick ChatGPT, Claude or Perplexity from a dropdown, and the assistant opens with the page already in the prompt.
  • A Markdown version of every page. Add .md to any URL and you get the same page as Markdown, without the header, menus or footer. This is the URL the button hands to the assistant.
https://www.example.com/articles/dental-care-for-dogs       the page, with the Ask AI button
https://www.example.com/articles/dental-care-for-dogs.md    the same page as Markdown

Here is a sample article page on Sitecore MVC, with a header, menu and search box, and the Ask AI button under the summary:

Sample article page on Sitecore MVC with the Ask AI button under the summary

The Ask AI Button

Ask AI dropdown with Open in ChatGPT, Open in Claude, Open in Copilot, Copy page as Markdown and View as Markdown

The module ships with ChatGPT, Claude and Perplexity. On my test site I added Copilot and turned Perplexity off, just by editing items, which is what you see above. The dropdown has one entry per assistant, plus two more:

  • Open in ChatGPT / Claude / Perplexity: opens the assistant in a new tab with a prompt like "Read this page so I can ask you questions about it: https://www.example.com/articles/dental-care-for-dogs.md".
  • Copy page as Markdown: copies the clean page to the clipboard, for any other AI tool.
  • View as Markdown: opens the .md version in a new tab.

Nothing about the button is hard-coded. New assistants show up every few months, and the prefill links they accept (such as https://chatgpt.com/?q=) are not documented public APIs, so they can change. Everything is a Sitecore item under /sitecore/system/Modules/Markdown For AI/Ask AI:

ItemFieldValues
Ask AI (settings)Button LabelButton text, for example "Ask AI about this page"
PromptText sent to the assistant. {url} = Markdown URL, {page} = page URL, {title} = page title
Show Copy MarkdownChecked: show Copy page as Markdown
Show View MarkdownChecked: show View as Markdown
Max Link LengthLongest assistant link before the page is copied to the clipboard instead (default 6000)
ChatGPT, Claude, Perplexity or any assistant you addAssistant NameFor example ChatGPT
Link PatternAn https link where {prompt} is replaced with the prompt, for example https://claude.ai/new?q={prompt}. Without {prompt}, the page is copied to the clipboard and the assistant opens, ready to paste
Menu LabelMenu text (empty: "Open in" and the name)
HintSecond line of the menu entry
PromptA prompt for this assistant only (empty: the Ask AI prompt)
IconA small image from the Media Library
EnabledUnchecked: hidden from the dropdown

To add an assistant, an editor inserts one item and publishes. No code, no deploy. The dropdown follows the content tree order. For a different set per site, create another Ask AI settings item and pick it as the rendering's data source. Upgrades never overwrite these items.

The button is a rendering called Ask AI. You can add it in three ways:

  • Once for every article: add it to the article template's Standard Values.
  • Per page: put an ask-ai placeholder near the article title, and editors can add the button in the Experience Editor.
  • In code: call @Html.AskAi() in a view.

The Markdown Version

For the sample article page shown earlier, this is what /articles/dental-care-for-dogs.md returns:

---
title: "Dental Care for Dogs: A Simple Home Routine"
description: "Why dental disease is so common in dogs, the signs to look for, and a five-minute daily routine."
url: "https://www.example.com/articles/dental-care-for-dogs"
language: "en"
last_updated: "2026-10-29"
---

# Dental Care for Dogs: A Simple Home Routine

## Why it matters

Most dogs show some sign of dental disease by the age of three. ...

## What else helps

| Option | How it helps | Notes |
| --- | --- | --- |
| Dental chews | Scrape plaque mechanically | Pick a size that cannot be swallowed whole |

No header, no menu, no newsletter, no footer. Headings, lists, tables and links stay. Every Markdown response also sends Link: rel="canonical" pointing to the HTML page and X-Robots-Tag: noindex, so search engines keep indexing the HTML page and not the copy.

How it decides what goes in:

  • The layout is never rendered, only its placeholders. Anything the layout file writes directly (a header partial, scripts) is gone automatically.
  • Excluded placeholders: renderings in the placeholders you list (by default header|footer|navigation|nav|breadcrumb) are skipped at any depth, before their controller or view runs. Dynamic placeholders such as footer-{GUID}-0 match too.
  • data-md-ignore on any element removes it, for example a share bar inside the article body.

The .md suffix is removed before Sitecore resolves the item, so wildcard items, custom item resolvers and language prefixes resolve the same item as for the HTML page. Only Request.RawUrl still shows the .md URL, so a custom resolver should read Request.Path instead.

Caching

Rendering a page and converting it costs CPU, so the finished Markdown is cached in memory and served early in httpRequestBegin, before Sitecore resolves the layout. On my test instance, the first request for an article took about 130 ms and the cached ones about 15 ms.

  • Cleared on publish, on every server.
  • Only successful pages are cached. Not-found and error pages are never stored.
  • Never cached: logged-in visitors, and content from the master database (local development and preview).
  • Your HTML rendering cache is not touched. A Markdown request does not read or write Sitecore's rendering cache.

How to Install

  1. Install Sitecore.MarkdownForAI-<version>.zip with the Installation Wizard. If you deploy through CI, there is also a NuGet package and the Sitecore CLI serialization.
  2. Rename App_Config/Include/MarkdownForAI/z.MarkdownForAI.Site.config.example to z.MarkdownForAI.Site.config and set your site name. Your settings live only in this file, so upgrades never touch them.
  3. Publish the Markdown For AI items.
  4. Add the Ask AI rendering to your page.

The .md pages are served on ContentDelivery and Standalone servers. All items live under Modules folders, so a Helix solution that serializes its own Foundation, Feature and Project folders never deletes them.

Settings

Most sites only change Sites and ExcludedPlaceholders. Every setting goes in z.MarkdownForAI.Site.config with the MarkdownForAI. prefix:

<setting name="MarkdownForAI.Sites" set:value="mysite" />
SettingDefaultPossible values
Enabledtruefalse turns off the .md pages
SiteswebsiteSite names, pipe separated, or * for every site except Sitecore's own. On other sites the button sends the normal page URL
Extension.mdThe suffix that asks for Markdown
ExcludedPathPrefixes/sitecore/|/-/|/api/|...Paths never treated as Markdown requests
ExcludedPlaceholdersheader|footer|navigation|nav|breadcrumbPlaceholder keys left out, at any depth
PlaceholdersemptyTop-level placeholders to render, in this order. Empty: all on the page
SkipTagsscript|style|form|nav|...HTML elements always removed
IgnoreClassesemptyCSS classes whose elements are removed
IncludeImagestruefalse drops images
TitleFieldsTitle|Page Title|HeadingFields for the title; the first non-empty one wins, then the display name
DescriptionFieldsMeta Description|...|AbstractFields for the description; the first non-empty one wins
FrontMatterFieldstitle|description|url|language|last_updatedFront matter lines to write, or none
CacheMinutes30Minutes to keep a page in memory. 0 turns the cache off
CacheSizeMB100Memory limit of the cache
CacheVaryByQueryStringfalsetrue only if components change their output based on the query string
NoCacheDatabasesmasterDatabases whose content is never cached
NoIndexHeadertruefalse stops sending X-Robots-Tag: noindex
Schemeemptyhttps to force it in generated URLs, for example behind an SSL offloader
AskAi.ScriptUrl/scripts/markdownforai/ask-ai.jsWhere the button's script is served from. Empty: load it yourself

What Is Not in This Version

I kept the first release small on purpose. These are not included:

  • WebForms pages: only MVC layouts get a .md version. On other layouts the button still works with the normal page URL.
  • llms.txt, field mappings, per-component rules and discovery headers. They are written up in the repository's docs/extending.md, so they can come back when there is a need.
  • Local and intranet sites: the assistant must be able to reach the .md URL. On a local host, use Copy page as Markdown and paste it.

Wrapping Up

  • Readers ask ChatGPT, Claude or Perplexity about your article with one click.
  • The assistant reads a clean Markdown copy of the page, not your menus and footer.
  • The assistants and the prompt are Sitecore items, so adding the next one is an editor task, not a deploy.
  • Install the package, set your site name, publish, and add the rendering.

If you are working on something similar, I would love to hear how you approached it. If you find the module useful, a star on GitHub helps with visibility.

Links

09 October, 2026

Exploring SitecoreAI Agentic Studio - And an Apply Changes Gotcha

I have been exploring SitecoreAI Agentic Studio recently. If you have not tried it yet, it is the AI workspace inside SitecoreAI where you can chat with pre-built agents, build your own, and chain them into multi-step workflows called Flows.

What Is Agentic Studio

When you open it, you see five sections: Actions (chat history), Agents (pre-built and custom agents), Flows (multi-step workflows), Signals (AI-driven trends and research), and Spaces (organized view of conversations and artifacts).

What I Tried

I started by opening a Chat and asking it to write a short blog post. That created a text artifact inside Studio. Then I created a Space from that conversation, which grouped everything together but did not change the artifact type.

Next I ran the Quote Extractor workflow against the blog content. That produced an extract artifact. Then I ran the Blog Writing Agent, which generated a content_item artifact with its originItemId pointing back to the Quote Extractor output.

The chain: Chat (text) > Space > Quote Extractor (extract) > Blog Writing Agent (content_item). None of these are real Sitecore CMS items.

The Apply Changes Issue

The Blog Writing Agent's output showed an "Apply Changes" button. That button implies you can push the content into a Sitecore page. But there is no Sitecore page behind this artifact. The originItemId points to the Quote Extractor output, which is an internal Studio artifact. The componentMap is null. When I clicked the button, it did not work.

The UI should check whether the origin is a real Sitecore item before showing the button. If the origin is another Studio artifact, "Apply Changes" should be hidden or disabled.

Wrapping Up

Agentic Studio has a lot of potential. The chat, space, and workflow pipeline feels natural. I am planning to dig deeper into custom agents and Flows, and will share more as I go.




For some reason, I was not able to submit the feedback to Sitecore. I will find another way to send it.



07 October, 2026

What Is Structured Data? A Sitecore Developer's Guide to JSON-LD and Schema.org

Recently I worked on adding structured data to a corporate homepage on a Sitecore XP project. The site belongs to a brand with one corporate site and a long list of location sites, in this case hospitals. The homepage was the easy part. The real question was how to do this well across hundreds of locations.

While working on it, I realized structured data is one of those topics everyone has heard of but few teams fully understand. So I am starting a short series. I will keep it simple, start from the basics, and build up to a real Sitecore implementation.

Here is the plan for the series:

  1. What is structured data? (this post)
  2. Modelling a multi-location brand: corporate site and location sites
  3. Implementing structured data on the corporate homepage in Sitecore
  4. Describing a membership program with MemberProgram
  5. Generating structured data for hundreds of location pages
  6. Validating, deploying and monitoring your schema
  7. Measuring the impact: what you can and cannot expect

What is structured data?

Structured data is a small block of code on your page that tells search engines exactly what the page is about. It does not change what visitors see. It is written for machines.

Think of it this way. A person reading your homepage can tell that "Shine Hospitals" is a company, that the number in the footer is a phone number, and that the logo is a logo. A search engine has to guess. Structured data removes the guessing by labelling each piece of information.

Three terms you will see again and again:

  • Schema.org is the shared vocabulary. It defines types like Organization, LocalBusiness and Event, and properties like name, address and telephone. Google, Bing and others all use it.
  • JSON-LD is the format Google recommends for writing it. It sits in a <script type="application/ld+json"> tag, separate from your HTML markup. That makes it much easier to manage from a CMS like Sitecore.
  • Microdata and RDFa are older formats that add attributes directly to your HTML. They still work, but I will use JSON-LD throughout this series.

A simple example

Here is a simplified version of what a corporate homepage schema looks like in practice. I have used a made-up brand, Shine Hospitals, but the structure mirrors a real production implementation.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "WebSite",
      "@id": "https://www.shinehospitals.com/#website",
      "url": "https://www.shinehospitals.com/",
      "name": "Shine Hospitals",
      "publisher": { "@id": "https://www.shinehospitals.com/#organization" }
    },
    {
      "@type": "WebPage",
      "@id": "https://www.shinehospitals.com/#webpage",
      "url": "https://www.shinehospitals.com/",
      "name": "Animal Hospitals & Emergency Vets | Shine Hospitals",
      "isPartOf": { "@id": "https://www.shinehospitals.com/#website" },
      "mainEntity": { "@id": "https://www.shinehospitals.com/#organization" }
    },
    {
      "@type": "Corporation",
      "@id": "https://www.shinehospitals.com/#organization",
      "name": "Shine Hospitals",
      "url": "https://www.shinehospitals.com/",
      "logo": "https://www.shinehospitals.com/images/logo.png",
      "contactPoint": {
        "@type": "ContactPoint",
        "telephone": "+1-800-555-0100",
        "contactType": "customer service"
      },
      "sameAs": [
        "https://www.facebook.com/shinehospitals",
        "https://www.linkedin.com/company/shinehospitals"
      ],
      "hasMemberProgram": {
        "@type": "MemberProgram",
        "@id": "https://www.shinehospitals.com/careplus#memberprogram",
        "name": "Shine CarePlus",
        "url": "https://www.shinehospitals.com/careplus"
      }
    }
  ]
}
</script>

This looks like a lot, but it is just three things described together:

  • @graph holds several entities in one script block instead of writing three separate scripts.
  • WebSite describes the site as a whole.
  • WebPage describes this specific page, and says its main topic is the organization.
  • Corporation (a more specific type of Organization) describes the company: name, logo, contact details and official social profiles in sameAs.
  • hasMemberProgram describes the brand's membership or wellness plan program using the MemberProgram type. More on this in its own post.

The important part is @id. Each entity gets a unique identifier, and the entities refer to each other by it instead of repeating details. The website is published by the organization, the page is part of the website, and the page is about the organization. Later, every location page will point back to the same organization @id.

No visitor ever sees this. But a search engine now knows exactly who the company is, what the site is, and how they relate.

Why does it matter?

Structured data helps search engines understand your content with confidence. That understanding shows up in a few ways:

  • Rich results. Some schema types can make your listing stand out in search, for example with ratings, hours or breadcrumbs.
  • Knowledge panel and brand identity. Organization schema helps Google connect your logo, website and social profiles as one entity.
  • Local search. For businesses with physical locations, accurate address, hours and phone details support map and local results.
  • AI-powered search. Search features and AI assistants that answer questions directly rely on clear, machine-readable facts. Structured data is one of the cleanest ways to provide them.

It is also important to be honest about what it does not do:

  • It is not a direct ranking boost. Adding schema will not move you from page five to page one.
  • Valid markup does not guarantee a rich result. Google decides when to show them.
  • Google has reduced or retired some rich result types over the years, like FAQ and HowTo. Always check Google's current list of supported types before promising a specific result to stakeholders.

So I see structured data as hygiene: low effort, low risk, and it makes everything else in SEO work a little better.

Corporate site vs location sites

This is where it gets interesting for multi-location brands. Think of a veterinary or healthcare network: one corporate site, plus a page or microsite for every hospital.

Each level describes a different thing:

  • The corporate homepage describes the brand itself, using Organization.
  • Each location page describes one physical place people visit, using LocalBusiness or a more specific type like VeterinaryCare or MedicalClinic. It carries the address, opening hours, phone number and map coordinates for that location.

The key is to link them. Each location points back to the brand using parentOrganization and the brand's @id. Search engines then see one company with many branches, not hundreds of unrelated businesses.

Corporate site and location sites — one brand, many locations

The corporate schema is written once. The location schema has to work for hundreds of pages, so it should come from content rather than being typed by hand. That difference shapes the whole Sitecore implementation, which I will cover in parts 3 and 5.

Wrapping up

To summarize:

  • Structured data labels your content so search engines don't have to guess.
  • Use schema.org types, written as JSON-LD.
  • It supports rich results, brand identity, local search and AI-powered answers, but it is not a ranking shortcut.
  • Multi-location brands need two levels: Organization for the brand, and LocalBusiness (or a subtype) for each location, linked together.

In the next post, I will go deeper into modelling a multi-location brand: which properties matter for the corporate site and for each location, and how to connect them properly.

If you are working on something similar, I would love to hear how you approached it. Feel free to reach out.



blockquote { margin: 0; } blockquote p { padding: 15px; background: #eee; border-radius: 5px; } blockquote p::before { content: '\201C'; } blockquote p::after { content: '\201D'; }