# Agents: how to use michaelchurley.com

Source: https://www.michaelchurley.com/agents.md

This is the personal site of Michael C. Hurley (20 Years of SEO, Now AEO/GEO · Technical Web · AI Agent Tooling). It holds his resume, portfolio, blog, and a public daily stand-up log. Everything below is read-only and free to use for search, answers, and user-requested tasks. Training use is opted out (see Policy).

## Machine-readable endpoints

| Path | What it returns |
| --- | --- |
| `/llms.txt` | This file: Michael's resume in llms.txt format |
| `/llms-full.txt` | The whole site as Markdown: resume, every page, portfolio, and every blog post |
| `/agents.md` | Usage guide for AI agents: endpoints, .md routes, MCP and WebMCP tools, booking, contact |
| `/resume.json` | Resume in JSON Resume (jsonresume.org) format |
| `/portfolio.json` | Portfolio pieces with titles, categories, media, and live links |
| `/blog.json` | Blog posts with title, slug, URL, date, description, tags, and Markdown link |
| `/resume.md` | Resume as Markdown |
| `/feed.xml` | Blog as RSS 2.0 |
| `/feed.json` | Blog as JSON Feed 1.1 |
| `/openapi.json` | OpenAPI 3.1 description of the JSON endpoints, Markdown routes, booking API, and MCP endpoint |
| `/mcp` | Remote MCP server (Streamable HTTP, POST JSON-RPC); Server Card at /mcp/server-card |
| `/aeo` | How this site is built for AI search and agents (case study with live links) |
| `/robots.txt` | Crawler policy with Content-Signal |
| `/sitemap.xml` | Sitemap of public pages with lastmod |
| `/.well-known/ai-catalog.json` | AI Catalog pointing to the MCP Server Card |

All `.md` and `.txt` endpoints return `text/markdown` or `text/plain` (UTF-8). JSON endpoints return `application/json`.

## Markdown for any page

Append `.md` to any page path to get that page's content as Markdown, generated from the same source data as the HTML page:

- `/` -> `/index.md`
- `/resume.md` (resume only)
- `/blog` -> `/blog.md`, `/blog/<slug>` -> `/blog/<slug>.md`
- `/portfolio` -> `/portfolio.md`
- `/book` -> `/book.md`, `/vizible` -> `/vizible.md`, `/omadesign` -> `/omadesign.md`
- `/aeo` -> `/aeo.md`
- `/nightly` -> `/nightly.md`, `/nightly/report/<YYYY-MM-DD>` -> `/nightly/report/<YYYY-MM-DD>.md`

Or request any page URL with `Accept: text/markdown`. HTML pages advertise their Markdown twin with a `Link: <...md>; rel="alternate"; type="text/markdown"` header and a matching `<link rel="alternate">` tag. Every page footer also has a "Markdown" link to its `.md` version. Unknown paths return 404.

## Remote MCP server

`POST https://www.michaelchurley.com/mcp`: a Model Context Protocol server over Streamable HTTP. It is stateless and answers with `application/json` (no SSE stream; `GET /mcp` returns 405). No authentication.

- Protocol versions: `2026-07-28` (per-request `_meta`, `server/discover`, `MCP-Protocol-Version` / `Mcp-Method` / `Mcp-Name` headers validated) and the legacy `initialize` handshake for `2025-11-25`, `2025-06-18`, and `2025-03-26`.
- Methods: `server/discover`, `initialize`, `ping`, `tools/list`, `tools/call`.
- Results: `content` (text) plus `structuredContent` for JSON results; tool errors come back with `isError: true`.
- Discovery: Server Card at `/mcp/server-card` (also `/.well-known/mcp/server-card.json` and `/.well-known/mcp.json`), AI Catalog at `/.well-known/ai-catalog.json`.

Example (legacy handshake, then a call):

```bash
curl -s https://www.michaelchurley.com/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2025-11-25' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_booking_options","arguments":{}}}'
```

## WebMCP tools

When the browser supports WebMCP, every page registers the same tools with `document.modelContext.registerTool()` (falling back to `navigator.modelContext` in older previews). Browsers without WebMCP are unaffected. WebMCP results are strings: Markdown, or JSON text.

The MCP server and WebMCP share one implementation (`lib/agent-tools.ts`), so names, inputs, and outputs are identical.

### Read-only tools

| Tool | Input | Returns |
| --- | --- | --- |
| `get_resume` | none | JSON Resume text (same as `/resume.json`) |
| `list_portfolio` | `{ category?: "sites" \| "interfaces" \| "marks" }` | JSON: `{ items: [{ id, title, category, media, url? }] }` |
| `list_blog_posts` | `{ tag?: string, limit?: number }` | JSON: `{ posts: [{ title, slug, url, markdown, date, description, tags }] }` |
| `get_blog_post` | `{ slug: string }` | Markdown of the post |
| `get_page_markdown` | `{ path: string }` (e.g. "/", "/portfolio") | Markdown of that page |
| `search_site` | `{ query: string }` | JSON: `{ query, results: [{ type, title, url, markdown?, snippet }] }` |
| `get_booking_options` | none | JSON: meeting types, duration, host time zone, hours, required fields, limits |
| `get_availability` | `{ date_from?: "YYYY-MM-DD", date_to?: "YYYY-MM-DD", timezone?: IANA zone, meeting_type?: "intro-call" }` | JSON: `{ meeting_type, duration_minutes, timezone, days: [{ date, slots: [{ slot_start, eastern, local }] }] }` |

### Booking tool (writes)

| Tool | Input | Returns |
| --- | --- | --- |
| `book_meeting` | `{ slot_start: ISO 8601 (from get_availability), name: string, email: string, phone?: string, notes?: string, timezone?: IANA zone, meeting_type?: "intro-call", dry_run?: boolean }` | JSON: `{ status: "confirmed", booking: { meeting_type, duration_minutes, slot_start, eastern, local, name, email }, message }`, or `{ status: "valid", dry_run: true, booking }` for a dry run |

### Booking flow

1. `get_booking_options`: one meeting type today, `intro-call` (30 minutes). Host time zone is America/New_York. Slots run Monday to Friday, 07:30 to 20:30 Eastern, every 30 minutes; same-day slots must be at least 30 minutes out.
2. `get_availability` with a date range (and the visitor's `timezone` for readable `local` labels). Up to 62 days per call.
3. `book_meeting` with a `slot_start` from step 2 plus the visitor's name and email. No extra confirmation step is required; the agent can book directly. The booking goes through the same path as the form on /book: it is saved, Michael is notified, and the visitor receives a confirmation email with a calendar invite. The same validation (name, valid email, open slot) and the same limit (5 booking requests per hour per IP) apply. Use `dry_run: true` to validate without booking.

Without WebMCP, send the visitor to https://www.michaelchurley.com/book: pick a day and time (Eastern), enter name, email, and optional phone, then press Book.

## Policy

- robots.txt allows search engines, answer engines, and user-triggered agents (OAI-SearchBot, ChatGPT-User, GPTBot, PerplexityBot, ClaudeBot, Google-Extended, and others) with `Content-Signal: search=yes, ai-input=yes, ai-train=no`.
- CCBot is blocked. Private paths (`/api/`, `/manage/`, and `/nightly`) are disallowed for crawlers.
- Please cite the canonical page URL (without `.md`) when quoting.

## Contact or book Michael

- Book a 30-minute call: the booking tools above, or https://www.michaelchurley.com/book
- Email: michaelmonetized@gmail.com
- Call or text: 828-593-1935
- LinkedIn: https://www.linkedin.com/in/michaelchurley
- GitHub: https://github.com/michaelmonetized
- X: https://x.com/michaelh_rley
