Agents.md metafield exposure

Hi, I’ve been trying to add shop metafields to my agents.md.liquid file but only the agents ones are rendering despite the docs saying that request and shop should work. I had a full report drafted below:

Area: Online Store › Themes › agents.md.liquid template

Docs referenced: agents.md.liquid — “the template exposes an agents object alongside the standard global Liquid objects (such as request and shop).”

Summary

The agents.md.liquid template exposes the base shop global object as documented, but shop.metafields.<namespace>.<key> resolves to empty — even when the metafield has a definition and Storefront API read access. The dedicated agents object and request hydrate correctly, and on a live (non-password) store shop.name renders, so the base shop object is present; only the metafields association appears not to be loaded by this template’s rendering pipeline (which behaves like the robots.txt.liquid class of templates).

Environment

  • Online Store 2.0 theme, file templates/agents.md.liquid
  • Metafield: merchant-owned, non-reserved namespace, with a definition, type single_line_text_field, Storefront API access enabled (PUBLIC_READ)
  • Tested via the live /agents.md route (page type agents_md, served by the active theme — confirmed in server-timing: ... pageType;desc="agents_md")

Steps to reproduce

  1. In Settings › Custom data › Shop, create a metafield definition: namespace/key custom.agents_render_test, type single-line text, Storefront API access ON. Set its value to RENDER-OK.

  2. Add templates/agents.md.liquid with:

    SHOPNAME={{ shop.name }}
    

    MF_VALUE={{ shop.metafields.custom.agents_render_test.value }}

    MF_OBJ={{ shop.metafields.custom.agents_render_test }}

    AGENTS_STORE={{ agents.store_name }}

    AGENTS_MCP={{ agents.mcp_endpoint_url }}

    REQHOST={{ request.host }}

  3. Save (this busts the AgentsMdController page cache) and request GET /agents.md.

Expected

MF_VALUE renders RENDER-OK (and MF_OBJ renders the metafield), consistent with shop being exposed and the metafield having a storefront-readable definition.

Actual

SHOPNAME=DylanDevelopment        ← base shop object present (live store)

MF_VALUE= ← EMPTY

MF_OBJ= ← EMPTY

AGENTS_STORE=DylanDevelopment ← agents object works

AGENTS_MCP=https://<shop>/api/ucp/mcp ← agents object works

REQHOST=<shop>.myshopify.com ← request works

shop.metafields.* is empty while every other global resolves. We ruled out the usual suspects:

  • Metafield type / definition: failed with a properly typed, defined metafield.
  • Access controls: failed with Storefront API PUBLIC_READ enabled. (We also confirmed separately that for a non-reserved merchant-owned namespace, access.admin must be PUBLIC_READ_WRITE or omitted — neither changes the Liquid result.)
  • Caching: /agents.md is cf-cache-status: DYNAMIC; saving the template busts the Shopify page_cache:...:AgentsMdController etag. A fresh render still shows empty.
  • Password wall: ruled out — request.host and the full agents object render, so the request context is live, not a degraded password render. (On a password-protected store the base shop object is additionally suppressed, but shop.metafields is empty on both live and password stores.)

The same gap was reproduced independently on two stores.

Impact / the ask

Apps that want to populate a store’s /agents.md (and /llms.txt//llms-full.txt) with brand-specific, always-current content currently have only two options: (a) ask the merchant to paste the full generated file into the template by hand, which then goes stale, or (b) obtain a write_themes exemption to upsert the file via the Theme Files API.

If agents.md.liquid hydrated shop.metafields (the base shop object is already exposed, so this looks like a small gap rather than a design decision), an app could write its content to a shop metafield and have the template stay in sync with a tiny merchant-pasted connector — no theme-write scope required. That’s a materially better integration story for the whole ecosystem of agentic-commerce apps building on agents.md.

Question for Shopify: Is shop.metafields intentionally excluded from the agents.md.liquid rendering context, or is this a gap? If intentional, could the docs state it explicitly (right now they list shop as available without noting the metafields exclusion)? If a gap, please consider hydrating storefront-readable metafields in this template.

Hey @Dylan_Hunt! Thanks for the thorough write-up and patience in waiting to hear back from us. Your instinct is right. agents.md.liquid renders in the same restricted context as robots.txt.liquid, and shop.metafields isn’t hydrated there. The base shop object is available (which is why shop.name works), but its metafields association isn’t loaded on this render path, so the lookup resolves empty.

As you suspected, this has nothing to do with Storefront API access. Liquid shop.metafields doesn’t go through the Storefront API at all, it’s loaded server side as the page renders, so the definition, the PUBLIC_READ toggle, and caching were all correctly ruled out.

I want to give you a definitive answer on the intentional-vs-gap question rather than guess, so I’ve raised this with the team that owns this template, whether the exclusion is by design, and whether the docs should call it out (they currently list shop as an available global with no note that its metafields aren’t populated here). I’ll follow up here as soon as I hear back.

In the meantime, the dynamic surface that is wired up for this template is the agents object (UCP/MCP endpoints, store URL, currency, sitemap), so anything you can express through that stays in sync automatically.