ByJoel GoldfootUpdated
Implementation Plan
A phased, week-by-week rollout guide for implementing all five Defense in Depth layers of BiModal Design — from basic FR-1 compliance to full agent protocol support.
Overview
BiModal Design is implemented in six sequential phases. Each phase builds on the previous and unlocks a new category of agent access. You can stop at any phase — partial implementation still delivers measurable value.
All content in initial HTTP response. Enables curl/HTTP agents.
HTML5 landmarks, ARIA, heading hierarchy. Enables structured parsing.
Schema.org microdata and JSON-LD. Enables knowledge graph agents.
OpenAPI spec, API discovery link. Enables API-capable agents.
MCP server, Agent Card. Enables MCP/A2A protocol agents.
Per-layer automated tests, monitoring, and regression prevention.
Phase 1: FR-1 Compliance
Week 1–2Foundational Requirement 1: every piece of content agents need must be present in the raw HTML returned by the initial HTTP response. No client-side rendering for primary content.
Verification test:
# Test FR-1 compliance: all content must be in initial HTTP response
curl -s https://yoursite.com/products \
| grep -c "data-agent-component"
# Must return > 0 — if it returns 0, content is client-rendered (FR-1 FAIL)
# Verify specific content is present
curl -s https://yoursite.com/products \
| grep -o 'data-agent-page="[^"]*"'
# Expected: data-agent-page="product-listing"Next.js implementation
// app/products/page.tsx — Server Component (default in Next.js 13+)
// Content rendered on server = FR-1 compliant
async function getProducts() {
const res = await fetch('https://api.yoursite.com/products', {
next: { revalidate: 3600 }, // ISR: revalidate every hour
});
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<main
data-agent-page="product-listing"
data-agent-intent="browse"
data-agent-content-type="listing"
>
<h1 data-agent-content="page-title">Our Products</h1>
<ul data-agent-component="product-list">
{products.map((product) => (
<li
key={product.id}
data-agent-component="product-card"
data-agent-content-type="product"
>
<h2 data-agent-content="product-name">{product.name}</h2>
<p data-agent-content="product-price">${product.price}</p>
<a
href={`/products/${product.id}`}
data-agent-action="view-product-details"
>
View Details
</a>
</li>
))}
</ul>
</main>
);
}Framework note
React/Next.js App Router Server Components are FR-1 compliant by default. Pure client components ('use client' with useEffect data fetching) are not. Move data fetching to the server.
Phase 2: Semantic Structure
Week 2–3Add HTML5 landmarks, ARIA roles, a logical heading hierarchy, and data-agent-* attributes so agents can navigate and understand page structure without executing JavaScript.
<!-- Layer 2: Semantic HTML with ARIA landmarks -->
<!DOCTYPE html>
<html lang="en" data-agent-framework="nextjs" data-agent-mode="ssg">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Product Catalog — YourSite</title>
<meta name="description" content="Browse our full product catalog">
</head>
<body>
<!-- Skip link for keyboard/agent navigation -->
<a
href="#main-content"
class="sr-only focus:not-sr-only"
data-agent-action="skip-to-content"
>
Skip to main content
</a>
<header role="banner">
<nav
aria-label="Main navigation"
data-agent-component="navigation"
>
<a href="/" data-agent-action="go-home">Home</a>
<a href="/products" data-agent-action="view-products">Products</a>
<a href="/support" data-agent-action="get-support">Support</a>
</nav>
</header>
<main id="main-content" role="main"
data-agent-page="product-listing"
data-agent-intent="browse"
>
<!-- Content here -->
</main>
<footer role="contentinfo">
<!-- Footer content -->
</footer>
</body>
</html>Required HTML5 landmarks
<header role="banner">— site header<nav aria-label="...">— navigation regions<main role="main">— primary content (one per page)<footer role="contentinfo">— site footer<aside role="complementary">— supplementary content
Heading hierarchy rules
- One
h1per page — the page title - Don't skip levels (h1 → h3 is invalid)
- Use
data-agent-content="page-title"on the h1 - Use
data-agent-content="section-title"on h2/h3
Phase 3: Structured Data
Week 3–4Add Schema.org structured data using both inline microdata (for zero-JS fallback) and JSON-LD (for rich extraction). Both formats in the initial HTTP response.
<!-- Layer 3: Structured Data — inline microdata + JSON-LD -->
<!-- Microdata (inline, works without JS) -->
<article
itemscope
itemtype="https://schema.org/Product"
data-agent-component="product-card"
>
<h2 itemprop="name" data-agent-content="product-name">
Wireless Headphones Pro
</h2>
<p itemprop="description" data-agent-content="product-description">
Premium noise-cancelling audio
</p>
<div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
<span itemprop="price" data-agent-content="product-price">$299</span>
<meta itemprop="priceCurrency" content="USD">
<link itemprop="availability" href="https://schema.org/InStock">
</div>
</article>
<!-- JSON-LD (in <head> or at bottom of <body>) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Wireless Headphones Pro",
"description": "Premium noise-cancelling audio",
"offers": {
"@type": "Offer",
"price": "299",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock"
}
}
</script>Schema types by page type
Phase 4: API Surface
Week 4–5Expose a machine-readable API with an OpenAPI specification and make it discoverable via <link rel="api"> in your page head.
OpenAPI specification
# Layer 4: OpenAPI specification
# Place at /api/openapi.json or /api/openapi.yaml
openapi: "3.0.3"
info:
title: YourSite API
version: "1.0.0"
description: |
RESTful API for YourSite product catalog and ordering.
Designed for both human-facing integrations and AI agent access.
contact:
name: API Support
url: https://yoursite.com/support
servers:
- url: https://yoursite.com/api/v1
description: Production
paths:
/products:
get:
operationId: listProducts
summary: List all products
description: Returns paginated product catalog with full agent-readable data
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
"200":
description: Product list
content:
application/json:
schema:
type: object
properties:
products:
type: array
items:
$ref: "#/components/schemas/Product"
components:
schemas:
Product:
type: object
required: [id, name, price]
properties:
id:
type: string
name:
type: string
price:
type: number
currency:
type: string
default: USDAPI discovery links
<!-- Add to <head> for API discoverability by agents -->
<link rel="api" href="/api/openapi.json" type="application/json">
<link rel="api" href="/api/openapi.yaml" type="application/yaml">
<!-- Layer 5: MCP Server Discovery -->
<link rel="alternate" type="application/mcp+json" href="/mcp-server" />
<!-- Also add sitemap and robots.txt references -->
<link rel="sitemap" href="/sitemap.xml" type="application/xml">Phase 5: Agent Protocols
Week 5–6Implement the Model Context Protocol (MCP) for agentic tool use, and publish an Agent Card at /.well-known/agent.json for A2A protocol discovery.
MCP server
// Layer 5: MCP Server (Model Context Protocol)
// Install: npm install @modelcontextprotocol/sdk
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
const server = new Server(
{ name: 'yoursite-mcp', version: '1.0.0' },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'search_products',
description: 'Search the product catalog',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
maxResults: { type: 'number', default: 10 },
},
required: ['query'],
},
},
{
name: 'get_product',
description: 'Get full details for a specific product',
inputSchema: {
type: 'object',
properties: {
productId: { type: 'string' },
},
required: ['productId'],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'search_products') {
const results = await fetch(
`https://yoursite.com/api/v1/products?q=${encodeURIComponent(args.query)}`
).then((r) => r.json());
return {
content: [
{
type: 'text',
text: JSON.stringify(results.products.slice(0, args.maxResults ?? 10)),
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);Agent Card
// Place at /.well-known/agent.json
// This is the Agent Card — discoverable by A2A protocol clients
{
"schemaVersion": "1.0",
"name": "YourSite Agent",
"description": "AI agent interface for YourSite product catalog and ordering",
"version": "1.0.0",
"url": "https://yoursite.com",
"capabilities": {
"streaming": false,
"pushNotifications": false,
"stateTransitionHistory": false
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["text/plain", "application/json"],
"skills": [
{
"id": "product-search",
"name": "Product Search",
"description": "Search and browse the product catalog",
"tags": ["products", "catalog", "search"],
"examples": [
"Find wireless headphones under $200",
"Show me all laptops in stock"
]
},
{
"id": "order-assistance",
"name": "Order Assistance",
"description": "Help with placing and tracking orders",
"tags": ["orders", "checkout", "tracking"]
}
]
}Phase 6: Testing & Validation
OngoingRun per-layer validation tests after each phase. Automate these in CI to prevent regressions when content or code changes.
# Phase 6: Validate all 5 layers
echo "=== Layer 1: FR-1 Check ==="
CONTENT_COUNT=$(curl -s https://yoursite.com/products | grep -c "data-agent-component")
echo "Agent components found: $CONTENT_COUNT"
[ $CONTENT_COUNT -gt 0 ] && echo "PASS" || echo "FAIL: Content not in initial response"
echo ""
echo "=== Layer 2: Semantic Structure ==="
LANDMARKS=$(curl -s https://yoursite.com | grep -E '<(main|nav|header|footer|aside)' | wc -l)
echo "HTML5 landmarks found: $LANDMARKS"
[ $LANDMARKS -ge 3 ] && echo "PASS" || echo "WARN: Add more HTML5 landmarks"
echo ""
echo "=== Layer 3: Structured Data ==="
curl -s https://yoursite.com/products/sample | python3 -c "
import sys, json, re
html = sys.stdin.read()
ld = re.findall(r'<script type=.application/ld+json.>(.*?)</script>', html, re.DOTALL)
print(f'JSON-LD blocks found: {len(ld)}')
for block in ld:
try:
data = json.loads(block)
print(f' @type: {data.get(chr(64)+"type", "unknown")}')
except:
print(' [parse error]')
"
echo ""
echo "=== Layer 4: API Discovery ==="
curl -si https://yoursite.com | grep -i 'link.*api'
curl -s https://yoursite.com/api/openapi.json | python3 -c "
import sys, json
data = json.load(sys.stdin)
paths = len(data.get('paths', {}))
print(f'OpenAPI paths: {paths}')
[ paths > 0 ] and print('PASS') or print('FAIL')
" 2>/dev/null || echo "No OpenAPI spec found"
echo ""
echo "=== Layer 5: Agent Protocols ==="
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://yoursite.com/.well-known/agent.json)
echo "Agent Card status: $HTTP_CODE"
[ "$HTTP_CODE" = "200" ] && echo "PASS" || echo "FAIL: Agent Card not found"Implementation Checklist
Layer 1: FR-1 Compliance
- All primary content rendered server-side (SSR/SSG)
- curl test returns > 0 data-agent-component matches
- No critical data loaded in useEffect or client-side fetch
- Progressive enhancement: core functionality works without JS
- noscript fallback for any JS-dependent components
Layer 2: Semantic Structure
- HTML5 landmarks: header, nav, main, footer present
- One h1 per page with data-agent-content="page-title"
- No skipped heading levels
- nav elements have aria-label attributes
- Skip-to-content link with data-agent-action="skip-to-content"
- data-agent-framework and data-agent-mode on <html>
- data-agent-page and data-agent-intent on <main>
- data-agent-component on major UI sections
- data-agent-action on interactive elements
Layer 3: Structured Data
- JSON-LD in <head> for primary page entity
- Schema.org type matches page content type
- Microdata on key content elements (optional but recommended)
- BreadcrumbList on multi-level navigation pages
- Google Rich Results Test passes with no errors
Layer 4: API Surface
- OpenAPI 3.x spec at /api/openapi.json
- <link rel="api"> in page <head>
- All endpoints documented with descriptions
- Authentication documented in securitySchemes
- robots.txt references API spec location
Layer 5: Agent Protocols
- MCP server implemented and deployed
- Agent Card at /.well-known/agent.json returns 200
- Agent Card skills reflect actual capabilities
- robots.txt allows agent crawlers
- llms.txt at /llms.txt (optional, for LLM context)