Шість практичних кроків, щоб сайт одночасно служив людям і AI-агентам, з готовим чек-лістом
Останні чотири дні ми з Claude Code перетворювали smarts.md із сайту документації смартконтрактів для людей на сайт для AI-агентів.
Звучить наче одне й те саме. Ні, різне. Сайт для людей оптимізує читабельність, пошук і швидкість завантаження. Сайт для AI-агентів має відповідати на інші питання:
robots.txt зачиняє перед ними двері?Ось шість речей, які я попросив Claude побудувати. Кожна корисна сама по собі, її можна перенести на твій сайт окремо.
robots.txtБільшість сайтів успадковують robots.txt із якогось старого шаблону, що неявно блокує будь-який незнайомий User-Agent. Але після 2025 року багато AI-краулерів — це нові User-Agent: GPTBot, ClaudeBot, PerplexityBot, Applebot-Extended, Google-Extended, CCBot, meta-externalagent тощо. Щоб пустити їх усередину, треба явно прописати Allow.
robots.txt на smarts починається з нотатки про політику, а далі перелічує User-agent + Allow: / для 12 краулерів — OpenAI, Anthropic, Perplexity, Google Gemini, Apple, Common Crawl, Meta, Cohere. Позиція пряма: цей сайт існує, щоб його читали AI.
llms.txt — меню для LLMllms.txt — конвенція, запропонована llmstxt.org: кладеш у корінь сайту короткий Markdown, який повідомляє LLM, що це за сайт, як його використовувати та де основні точки входу. Такий собі elevator pitch сайту.
llms.txt на smarts — близько 60 рядків:
/{chain}/{address} vs короткий slug)Суть: LLM платить токенами за читання твоєї документації, тож не краулитиме весь сайт. Дай йому дистильований покажчик — і попадання в ціль різко зросте.
.well-known/mcp.json — ставка завчасноПротокол MCP ще не стандартизував конвенцію автовиявлення штибу /.well-known/openid-configuration. Сьогодні жоден публічний клієнт не обходить фіксований well-known-шлях.
Але опублікувати маніфест — це 30 рядків:
def well_known_mcp
response.set_header("Cache-Control", "public, max-age=3600")
response.set_header("Access-Control-Allow-Origin", "*")
render json: {
name: "smarts",
version: "0.1.0",
description: "...",
protocol_version: "2024-11-05",
transports: [{ type: "sse", endpoint: "https://smarts.md/mcp/sse" }],
capabilities: { tools: true, resources: false, prompts: false },
tools: MCP_TOOLS.map { |t| { name: t[:name], description: t[:blurb] } }
}
end
Коли конвенція з'явиться — не змінюю жодного рядка. І це самодокументація для розробників — хто цікавиться моїм MCP, просто curl smarts.md/.well-known/mcp.json.
Ключ: масив tools виводиться з константи MCP_TOOLS — єдине джерело правди. Наявний структурний тест вимагає, щоб кожен app/tools/*_tool.rb з'являвся в цій константі, тож маніфест безкоштовно успадковує гарантію «новий інструмент не забудеться».
.md: WebFetch-friendly дистилят кожної сторінкиМоє улюблене. Інструмент WebFetch у Claude Code має сам чистити й видобувати контент із довільного HTML — це дорого по токенах і ненадійно. А що, якщо віддавати одразу .md?
respond_to у Rails підтримує це нативно:
# config/routes.rb
get ":slug(.:format)", to: "contracts#show",
constraints: { slug: ContractSlugs::ROUTE_PATTERN, format: /html|md/ }
https://smarts.md/usdc-eth повертає HTML; https://smarts.md/usdc-eth.md повертає 40 рядків Markdown — адресу, мережу, класифікацію, MCP endpoint, як запитувати з AI-агента, посилання на джерело.
# USD Coin on Ethereum
- **Address:** `0xa0b8...`
- **Chain:** Ethereum
- **Classification:** ERC-20 Token
## Query via AI agent
- **MCP endpoint:** `https://smarts.md/mcp/sse`
- **Reference:** `usdc-eth`
- **Sample prompt:** "Tell me the current state of usdc-eth"
Один fetch — уся структура, яку хоче агент, усередині, ніяких DOM-парсерів. Claude виконав це швидко — один контролер, два шаблони .md.erb, (.:format) у routes, готово.
Це зворотне виявлення: частина моїх відвідувачів уже користується Claude Code / Cursor / Windsurf. Потрапляють на сторінку контракту, і наступний імпульс — «хай мій AI гляне на це».
Тому на кожній сторінці контракту рендериться картка з:
usdc-eth) або chain/address, із кнопкою «Копіювати»mcp.smarts.md (setup в один рядок)Одне клацання тертя між «я дивлюся на цей контракт» і «я запитую свій AI». Картка використовує card із DaisyUI та загальний Stimulus-контролер copy — 50 рядків erb. Регресійний тест фіксує атрибути data-copy-text-value, щоб майбутній refactor не вставив користувачам у буфер обміну тиху помилку.
OpenGraph, Twitter Card, JSON-LD — традиційний шар пошуку та соціальних карток. Варто робити, бо AI-краулери теж читають звідси.
Цікавий вибір — як JSON-LD має описати смартконтракт. Claude обрав WebPage, що обгортає SoftwareApplication:
{
"@type": "WebPage",
"about": {
"@type": "SoftwareApplication",
"name": "USD Coin",
"applicationCategory": "SmartContract",
"operatingSystem": "Ethereum",
"identifier": "0xa0b8..."
}
}
До operatingSystem — ім'я мережі, до identifier — адреса, до additionalType — класифікація (наприклад, «Uniswap V3 Pool»). У schema.org немає окремого типу SmartContract, але SoftwareApplication плюс ці поля достатньо, щоб LLM зрозумів «це контракт на Ethereum з конкретною адресою».
OG-зображення — summary_large_image 1200×630; Twitter Card, хлібні крихти, softwareVersion і license довершують картину.
Шість пунктів вище — це сім PR за чотири дні:
| PR | Файлів | Розмір |
|---|---|---|
feat/ai-crawler-discovery |
2 | ~126 loc |
feat/well-known-mcp-manifest |
4 | ~129 loc |
feat/markdown-contract-pages |
6 | ~298 loc |
feat/contract-mcp-card |
3 | ~126 loc |
feat/seo-meta-tags |
8 | ~292 loc |
feat/seo-enrichments |
8 | ~189 loc |
feat/og-card |
— | — |
Кожен — самостійний PR із власними тестами та commit-повідомленням. Цей ритм дрібного нарізання — найцінніше в роботі з Claude: кожен PR достатньо малий, щоб переглянути diff одним поглядом, але складені докупи — на сайті виросла повноцінна AI-поверхня.
Якщо у тебе контентний сайт і ти хочеш зробити його AI-дружнім, роби в такому порядку:
robots.txt/llms.txt (дистильоване меню).md для основних контентних сторінок/.well-known/mcp.jsonПерші п'ять тобі не розповість більшість SEO-інженерів — їх немає в традиційному playbook. Але за чотири дні даних у мене крива AI-трафіку і крива людського трафіку — це дві повністю незалежні серії. Треба обслуговувати обидві.