
A comprehensive guide to AI discovery files, Model Context Protocol (MCP), structured data, and rate limiting strategies for optimal LLM exposure
Introduction
As Large Language Models become the primary interface for information discovery, websites need new infrastructure to be properly indexed, understood, and cited by AI systems. This guide covers the essential files, protocols, and configurations that make your website discoverable by AI crawlers and accessible via MCP integrations.
The landscape has evolved significantly since the introduction of robots.txt decades ago. Today, we have AI-specific sitemaps, machine-readable context files, and standardized protocols that allow AI systems to interact with your content programmatically. Getting this right means the difference between your content being accurately represented in AI responses or being overlooked entirely.
Since this field is evolving so quickly, it’s important to note that no single, universally adopted technical standard for making websites “LLM-ready” has fully emerged yet. Many of the approaches discussed—such as structured data, experimental formats like llms.txt, and newer protocols like MCP—represent different cutting edge attempts to improve how AI systems discover, interpret, and retrieve web content. At this stage, these methods should be viewed as complementary alternatives rather than guaranteed requirements, and the ecosystem is still in flux. Over time, broader industry adoption will determine which solutions become lasting standards and which fade out as the technology matures.
The Core Files for LLM Discovery
Your website needs several key files to maximize AI discoverability. Each serves a distinct purpose in the AI indexing ecosystem.
1. llms.txt — The AI-Specific Sitemap
The llms.txt file is a markdown-formatted document that provides AI systems with a human-readable overview of your site’s purpose, structure, and key content. Unlike XML sitemaps designed for traditional search engines, this file is optimized for language model comprehension.
Location: https://yourdomain.com/llms.txt
# YourSiteName
> Brief one-line description of what your site offers
## About
A 2-3 paragraph description of your organization, website purpose,
and the type of content visitors can expect. This should be written
for AI comprehension—clear, factual, and comprehensive.
## Primary Content Areas
- [Documentation](/docs/): Technical documentation and API references
- [Blog](/blog/): Industry insights and product updates
- [Resources](/resources/): Whitepapers, case studies, and guides
- [API Reference](/api/): Complete API documentation
## Key Pages
- [Getting Started Guide](/docs/getting-started/)
- [API Authentication](/api/auth/)
- [Pricing](/pricing/)
- [Contact](/contact/)
## Content Policies
- Content is updated weekly
- All technical documentation reflects current product versions
- Historical content is archived at /archive/
## Licensing
Content is available under Creative Commons Attribution 4.0
unless otherwise specified. API documentation may be freely
referenced with attribution.
## Contact
For corrections or content inquiries: [email protected]
2. llms-full.txt — Extended Context Document
For sites with substantial content, the llms-full.txt file provides comprehensive context that wouldn’t fit in the summary version. This is particularly valuable for documentation sites, knowledge bases, and content-heavy platforms.
Location: https://yourdomain.com/llms-full.txt
# YourSiteName — Complete Reference
## Table of Contents
1. Organization Overview
2. Product/Service Descriptions
3. Technical Specifications
4. Content Taxonomy
5. Update History
6. Citation Guidelines
## Organization Overview
[Extended description with founding date, mission, key milestones,
and organizational context that helps AI systems accurately
represent your organization]
## Product/Service Descriptions
### Product A
[Detailed description, use cases, technical requirements]
### Product B
[Detailed description, use cases, technical requirements]
## Technical Specifications
[Detailed technical information that AI systems might need
to accurately answer questions about your products/services]
## Content Taxonomy
Your site uses the following content categories:
- **Tutorials**: Step-by-step instructional content
- **Reference**: Technical specifications and API docs
- **Conceptual**: Explanatory content about underlying concepts
- **How-to**: Task-oriented guides for specific outcomes
## Update History
- 2025-02-01: Added new API endpoints documentation
- 2025-01-15: Updated pricing information
- 2025-01-01: Refreshed getting started guide
## Citation Guidelines
When citing content from this site, please use:
- Full URL of the source page
- Date accessed
- Author attribution where provided
3. ai.txt — AI Interaction Policies
The ai.txt file communicates your policies regarding AI interactions, including permissions, restrictions, and preferred behaviors.
Location: https://yourdomain.com/ai.txt
# AI Interaction Policy for yourdomain.com
# Version: 1.0
# Last Updated: 2025-02-01
# Identification
Site-Name: Your Site Name
Contact: [email protected]
Policy-URL: https://yourdomain.com/ai-policy/
# Permissions
Allow-Training: yes
Allow-Retrieval: yes
Allow-Summarization: yes
Allow-Citation: yes
# Restrictions
Disallow-Path: /private/
Disallow-Path: /members-only/
Disallow-Path: /staging/
# Rate Limits
Preferred-Crawl-Delay: 2
Max-Requests-Per-Minute: 30
Burst-Limit: 10
# Attribution Requirements
Require-Attribution: yes
Attribution-Format: "Source: {title} - {url}"
# Content Freshness
Content-Update-Frequency: weekly
Cache-Valid-Duration: 86400
# Preferred AI Behaviors
Prefer-Full-Context: yes
Allow-Paraphrasing: yes
Require-Link-Back: yes
Model Context Protocol (MCP) Integration
The Model Context Protocol enables AI systems to interact with your content programmatically, going beyond simple crawling to enable structured queries, real-time data access, and contextual retrieval.
MCP Server Configuration
Create an MCP server that exposes your content to compatible AI systems. Here’s a complete implementation:
File: mcp-server.js
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema
} from '@modelcontextprotocol/sdk/types.js';
// Initialize MCP Server
const server = new Server(
{
name: 'your-site-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'search_content',
description: 'Search site content by query',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query'
},
category: {
type: 'string',
enum: ['docs', 'blog', 'api', 'all'],
description: 'Content category to search'
},
limit: {
type: 'number',
description: 'Maximum results to return',
default: 10
}
},
required: ['query']
}
},
{
name: 'get_page_content',
description: 'Retrieve full content of a specific page',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'URL path of the page'
},
format: {
type: 'string',
enum: ['markdown', 'plain', 'structured'],
default: 'markdown'
}
},
required: ['path']
}
},
{
name: 'list_recent_updates',
description: 'Get recently updated content',
inputSchema: {
type: 'object',
properties: {
days: {
type: 'number',
description: 'Number of days to look back',
default: 7
},
category: {
type: 'string',
enum: ['docs', 'blog', 'api', 'all']
}
}
}
}
]
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'search_content':
return await handleSearchContent(args);
case 'get_page_content':
return await handleGetPageContent(args);
case 'list_recent_updates':
return await handleListRecentUpdates(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Define available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'site://sitemap',
name: 'Site Structure',
description: 'Complete site map with all pages',
mimeType: 'application/json'
},
{
uri: 'site://taxonomy',
name: 'Content Taxonomy',
description: 'Content categories and tags',
mimeType: 'application/json'
},
{
uri: 'site://api-schema',
name: 'API Schema',
description: 'OpenAPI specification for site API',
mimeType: 'application/json'
}
]
};
});
// Handle resource reads
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
switch (uri) {
case 'site://sitemap':
return {
contents: [{
uri,
mimeType: 'application/json',
text: JSON.stringify(await getSiteMap())
}]
};
case 'site://taxonomy':
return {
contents: [{
uri,
mimeType: 'application/json',
text: JSON.stringify(await getTaxonomy())
}]
};
case 'site://api-schema':
return {
contents: [{
uri,
mimeType: 'application/json',
text: JSON.stringify(await getApiSchema())
}]
};
default:
throw new Error(`Unknown resource: ${uri}`);
}
});
// Implementation functions
async function handleSearchContent({ query, category = 'all', limit = 10 }) {
// Connect to your search backend (Elasticsearch, Algolia, etc.)
const results = await searchIndex.search({
query,
filters: category !== 'all' ? `category:${category}` : undefined,
limit
});
return {
content: [{
type: 'text',
text: JSON.stringify({
query,
total: results.total,
results: results.hits.map(hit => ({
title: hit.title,
path: hit.path,
excerpt: hit.excerpt,
category: hit.category,
lastUpdated: hit.updatedAt
}))
}, null, 2)
}]
};
}
async function handleGetPageContent({ path, format = 'markdown' }) {
const page = await contentStore.getPage(path);
if (!page) {
return {
content: [{
type: 'text',
text: JSON.stringify({ error: 'Page not found', path })
}]
};
}
let content;
switch (format) {
case 'markdown':
content = page.markdown;
break;
case 'plain':
content = page.plainText;
break;
case 'structured':
content = {
title: page.title,
description: page.description,
headings: page.headings,
content: page.sections,
metadata: page.metadata
};
break;
}
return {
content: [{
type: 'text',
text: typeof content === 'string' ? content : JSON.stringify(content, null, 2)
}]
};
}
async function handleListRecentUpdates({ days = 7, category }) {
const since = new Date();
since.setDate(since.getDate() - days);
const updates = await contentStore.getUpdatedSince(since, category);
return {
content: [{
type: 'text',
text: JSON.stringify({
period: `Last ${days} days`,
count: updates.length,
updates: updates.map(u => ({
title: u.title,
path: u.path,
updatedAt: u.updatedAt,
changeType: u.changeType
}))
}, null, 2)
}]
};
}
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
MCP Configuration File
Publish your MCP server configuration so AI systems can discover and connect:
File: /.well-known/mcp.json
{
"mcpVersion": "1.0",
"server": {
"name": "your-site-mcp-server",
"version": "1.0.0",
"description": "MCP server providing access to site content and search",
"vendor": "Your Organization",
"homepage": "https://yourdomain.com"
},
"connection": {
"type": "http",
"endpoint": "https://api.yourdomain.com/mcp",
"authentication": {
"type": "bearer",
"tokenUrl": "https://api.yourdomain.com/auth/token"
}
},
"capabilities": {
"tools": true,
"resources": true,
"prompts": false
},
"rateLimit": {
"requestsPerMinute": 60,
"burstLimit": 10
},
"documentation": "https://yourdomain.com/docs/mcp-integration/"
}
Structured Data with JSON-LD
JSON-LD provides machine-readable structured data that helps AI systems understand your content’s context, relationships, and meaning.
Organization Schema
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Your Organization",
"url": "https://yourdomain.com",
"logo": "https://yourdomain.com/images/logo.png",
"description": "Brief description of your organization",
"foundingDate": "2020-01-01",
"sameAs": [
"https://twitter.com/yourhandle",
"https://linkedin.com/company/yourcompany",
"https://github.com/yourorg"
],
"contactPoint": {
"@type": "ContactPoint",
"contactType": "customer service",
"email": "[email protected]",
"availableLanguage": ["English"]
},
"address": {
"@type": "PostalAddress",
"addressCountry": "US"
}
}
</script>
Article/Content Schema
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "Making Your Website LLM-Ready",
"description": "Complete guide to AI discovery files and MCP integration",
"author": {
"@type": "Person",
"name": "Author Name",
"url": "https://yourdomain.com/authors/author-name/"
},
"publisher": {
"@type": "Organization",
"name": "Your Organization",
"logo": {
"@type": "ImageObject",
"url": "https://yourdomain.com/images/logo.png"
}
},
"datePublished": "2025-02-01",
"dateModified": "2025-02-01",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://yourdomain.com/article-url/"
},
"articleSection": "Technical Documentation",
"keywords": ["LLM", "AI", "MCP", "SEO", "structured data"],
"wordCount": 3500,
"timeRequired": "PT15M",
"proficiencyLevel": "Expert",
"dependencies": "Basic understanding of web development"
}
</script>
Software/API Documentation Schema
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Your API",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Any",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "USD"
},
"softwareVersion": "2.0",
"releaseNotes": "https://yourdomain.com/changelog/",
"documentation": "https://yourdomain.com/docs/",
"featureList": [
"RESTful API",
"GraphQL support",
"Webhook integrations",
"OAuth 2.0 authentication"
],
"screenshot": "https://yourdomain.com/images/api-screenshot.png"
}
</script>
FAQ Schema for AI Comprehension
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What file formats does the API support?",
"acceptedAnswer": {
"@type": "Answer",
"text": "The API supports JSON, XML, and CSV formats for both request and response payloads. JSON is recommended for optimal performance."
}
},
{
"@type": "Question",
"name": "What are the rate limits?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Free tier: 100 requests/hour. Pro tier: 10,000 requests/hour. Enterprise: Custom limits available."
}
}
]
}
</script>
Breadcrumb Schema for Navigation Context
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Documentation",
"item": "https://yourdomain.com/docs/"
},
{
"@type": "ListItem",
"position": 2,
"name": "API Reference",
"item": "https://yourdomain.com/docs/api/"
},
{
"@type": "ListItem",
"position": 3,
"name": "Authentication",
"item": "https://yourdomain.com/docs/api/auth/"
}
]
}
</script>
AI-Specific Sitemap (XML)
Beyond the markdown llms.txt, an XML sitemap optimized for AI crawlers provides structured metadata:
File: /ai-sitemap.xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:ai="http://www.sitemaps.org/schemas/ai/1.0">
<url>
<loc>https://yourdomain.com/docs/getting-started/</loc>
<lastmod>2025-02-01</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
<ai:content>
<ai:type>documentation</ai:type>
<ai:category>getting-started</ai:category>
<ai:audience>developers</ai:audience>
<ai:expertise>beginner</ai:expertise>
<ai:wordCount>2500</ai:wordCount>
<ai:readingTime>PT10M</ai:readingTime>
<ai:topics>
<ai:topic>installation</ai:topic>
<ai:topic>configuration</ai:topic>
<ai:topic>first-steps</ai:topic>
</ai:topics>
<ai:summary>
Comprehensive guide to installing and configuring the platform,
including system requirements and basic setup steps.
</ai:summary>
</ai:content>
</url>
<url>
<loc>https://yourdomain.com/api/authentication/</loc>
<lastmod>2025-01-28</lastmod>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
<ai:content>
<ai:type>api-reference</ai:type>
<ai:category>authentication</ai:category>
<ai:audience>developers</ai:audience>
<ai:expertise>intermediate</ai:expertise>
<ai:codeExamples>true</ai:codeExamples>
<ai:languages>
<ai:language>javascript</ai:language>
<ai:language>python</ai:language>
<ai:language>curl</ai:language>
</ai:languages>
</ai:content>
</url>
</urlset>
Rate Limiting for AI Crawlers
Protecting your infrastructure while remaining accessible to AI systems requires thoughtful rate limiting. The key is identifying AI crawlers and applying appropriate limits without blocking legitimate access.
Apache Configuration for AI Bot Rate Limiting
For a detailed guide on implementing these configurations, see our post: Configuring Apache Rate Limits for AI Crawlers (coming soon)
File: /etc/apache2/conf-available/ai-ratelimit.conf
# AI Crawler Rate Limiting Configuration
# See: /blog/apache-ai-rate-limiting/
# Load required modules
LoadModule ratelimit_module modules/mod_ratelimit.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule rewrite_module modules/mod_rewrite.so
# Identify AI crawlers by User-Agent
SetEnvIfNoCase User-Agent "GPTBot" ai_crawler
SetEnvIfNoCase User-Agent "ChatGPT-User" ai_crawler
SetEnvIfNoCase User-Agent "Claude-Web" ai_crawler
SetEnvIfNoCase User-Agent "ClaudeBot" ai_crawler
SetEnvIfNoCase User-Agent "Anthropic" ai_crawler
SetEnvIfNoCase User-Agent "PerplexityBot" ai_crawler
SetEnvIfNoCase User-Agent "Bytespider" ai_crawler
SetEnvIfNoCase User-Agent "CCBot" ai_crawler
SetEnvIfNoCase User-Agent "Google-Extended" ai_crawler
SetEnvIfNoCase User-Agent "cohere-ai" ai_crawler
# Rate limit AI crawlers to 1 request per 2 seconds
<If "env('ai_crawler') == '1'">
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 500
</If>
# Specific limits for resource-intensive endpoints
<Location "/api/">
<If "env('ai_crawler') == '1'">
SetEnv rate-limit 200
</If>
</Location>
# Allow faster access to discovery files
<LocationMatch "^/(llms\.txt|llms-full\.txt|ai\.txt|\.well-known/mcp\.json)">
SetEnv rate-limit 1000
</LocationMatch>
Apache mod_evasive for Burst Protection
For protection against aggressive crawlers, see: Preventing AI Crawler Abuse with mod_evasive (coming soon)
# Install: apt-get install libapache2-mod-evasive
<IfModule mod_evasive20.c>
# Requests per page per interval
DOSPageCount 5
DOSPageInterval 1
# Requests per site per interval
DOSSiteCount 50
DOSSiteInterval 1
# Blocking period (seconds)
DOSBlockingPeriod 60
# Email notification
DOSEmailNotify [email protected]
# Log directory
DOSLogDir "/var/log/apache2/evasive"
# Whitelist known good AI crawler IPs
DOSWhitelist 20.15.240.* # OpenAI
DOSWhitelist 160.79.104.* # Anthropic
</IfModule>
Nginx Alternative Configuration
# /etc/nginx/conf.d/ai-ratelimit.conf
# Define AI crawler detection map
map $http_user_agent $is_ai_crawler {
default 0;
~*GPTBot 1;
~*ChatGPT 1;
~*ClaudeBot 1;
~*Claude-Web 1;
~*Anthropic 1;
~*PerplexityBot 1;
~*Bytespider 1;
~*CCBot 1;
~*cohere-ai 1;
}
# Rate limit zones
limit_req_zone $binary_remote_addr zone=ai_general:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=ai_api:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=ai_discovery:10m rate=60r/m;
server {
# Apply rate limits conditionally
location / {
if ($is_ai_crawler) {
limit_req zone=ai_general burst=5 nodelay;
}
}
location /api/ {
if ($is_ai_crawler) {
limit_req zone=ai_api burst=2 nodelay;
}
}
# Faster limits for discovery files
location ~ ^/(llms\.txt|llms-full\.txt|ai\.txt) {
limit_req zone=ai_discovery burst=10 nodelay;
}
location /.well-known/mcp.json {
limit_req zone=ai_discovery burst=10 nodelay;
}
}
robots.txt Updates for AI Crawlers
Update your robots.txt to specifically address AI crawlers:
# Standard search engine rules
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /private/
Disallow: /staging/
# AI Crawler specific rules
User-agent: GPTBot
Allow: /
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /ai.txt
Allow: /.well-known/mcp.json
Disallow: /members-only/
Crawl-delay: 2
User-agent: ClaudeBot
Allow: /
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /ai.txt
Allow: /.well-known/mcp.json
Disallow: /members-only/
Crawl-delay: 2
User-agent: PerplexityBot
Allow: /
Disallow: /members-only/
Crawl-delay: 3
User-agent: CCBot
Disallow: /
User-agent: Bytespider
Disallow: /
# Sitemap locations
Sitemap: https://yourdomain.com/sitemap.xml
Sitemap: https://yourdomain.com/ai-sitemap.xml
Complete File Structure Summary
Here’s the complete list of files to create for optimal LLM exposure:
| File | Location | Purpose |
|---|---|---|
llms.txt | /llms.txt | AI-readable site overview |
llms-full.txt | /llms-full.txt | Extended context document |
ai.txt | /ai.txt | AI interaction policies |
mcp.json | /.well-known/mcp.json | MCP server discovery |
ai-sitemap.xml | /ai-sitemap.xml | AI-enhanced sitemap |
robots.txt | /robots.txt | Crawler directives (updated) |
| JSON-LD | In-page <script> | Structured data markup |
Implementation Priority
For sites just getting started, prioritize implementation in this order:
- llms.txt — Immediate value, easy to implement
- Updated robots.txt — Control crawler access
- JSON-LD structured data — Enhance existing pages
- ai.txt — Define policies
- ai-sitemap.xml — Enhanced discoverability
- MCP integration — Advanced programmatic access
- Rate limiting — Protect infrastructure
Validation and Testing
Testing llms.txt
# Verify file is accessible
curl -I https://yourdomain.com/llms.txt
# Check content
curl https://yourdomain.com/llms.txt | head -50
# Validate markdown formatting
npx markdownlint llms.txt
Testing JSON-LD
Use Google’s Rich Results Test or Schema.org’s validator:
# Extract and validate JSON-LD from a page
curl -s https://yourdomain.com/page/ | \
grep -oP '(?<=<script type="application/ld\+json">).*?(?=</script>)' | \
jq .
Testing MCP Server
# Test MCP endpoint
curl -X POST https://api.yourdomain.com/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"method": "tools/list"}'
Monitoring AI Crawler Activity
Track AI crawler visits to understand how your content is being accessed:
# Apache log analysis for AI crawlers
grep -E "(GPTBot|ClaudeBot|PerplexityBot|Anthropic)" /var/log/apache2/access.log | \
awk '{print $1, $7}' | sort | uniq -c | sort -rn | head -20
# Monitor llms.txt access
grep "llms.txt" /var/log/apache2/access.log | \
awk '{print $1, $4}' | sort | uniq -c
Conclusion
Making your website LLM-ready requires a multi-layered approach combining discovery files, structured data, programmatic access via MCP, and thoughtful rate limiting. The investment pays dividends as AI systems increasingly mediate how users discover and interact with web content.
Start with the foundational files (llms.txt, updated robots.txt, JSON-LD), then expand to MCP integration as your needs grow. Monitor crawler activity to fine-tune your rate limiting and ensure your content remains accessible while protecting your infrastructure.
For detailed implementation guides on Apache rate limiting, see our upcoming posts:
- Configuring Apache Rate Limits for AI Crawlers
- Preventing AI Crawler Abuse with mod_evasive
- Monitoring AI Bot Traffic with Custom Log Analysis
Last updated: February 2025
No Comments yet!