Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/site/baseline/robots.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Allow: /api/mcp
Allow: /.well-known/
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /auth.md
Disallow: /api/
Disallow: /dashboard/

Expand All @@ -16,6 +17,7 @@ Allow: /api/mcp
Allow: /.well-known/
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /auth.md

User-Agent: ClaudeBot
Allow: /
Expand All @@ -24,6 +26,7 @@ Allow: /api/mcp
Allow: /.well-known/
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /auth.md

User-Agent: PerplexityBot
Allow: /
Expand All @@ -32,6 +35,7 @@ Allow: /api/mcp
Allow: /.well-known/
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /auth.md

User-Agent: Applebot-Extended
Allow: /
Expand All @@ -40,7 +44,9 @@ Allow: /api/mcp
Allow: /.well-known/
Allow: /llms.txt
Allow: /llms-full.txt
Allow: /auth.md

Host: https://codingcat.dev
Sitemap: https://codingcat.dev/sitemap.xml
Sitemap: https://codingcat.dev/sitemap-index.xml
Agentmap: https://codingcat.dev/.well-known/ai-catalog.json
13 changes: 11 additions & 2 deletions apps/site/src/components/SearchModal.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@
aria-labelledby="search-modal-title"
>
<!-- Modal Header & Search Bar -->
<div class="flex items-center border-b border-border px-4 py-3 gap-3 bg-muted/20">
<form
action="/search"
method="GET"
class="flex items-center border-b border-border px-4 py-3 gap-3 bg-muted/20"
toolname="search_content"
tooldescription="Search CodingCat.dev web development tutorials, blog posts, and podcasts"
onsubmit="return false"
>
<svg
class="size-5 shrink-0 text-muted-foreground"
viewBox="0 0 24 24"
Expand All @@ -30,9 +37,11 @@
<input
type="search"
id="search-modal-input"
name="q"
autocomplete="off"
placeholder="Search posts, podcasts, authors... (⌘K)"
class="w-full bg-transparent text-base md:text-lg placeholder:text-muted-foreground focus:outline-none"
toolparamdescription="Search keyword or technical topic"
/>

<!-- Loading spinner -->
Expand Down Expand Up @@ -65,7 +74,7 @@
>
ESC
</button>
</div>
</form>

<!-- Content Type Filter Pills -->
<div class="flex items-center gap-1.5 border-b border-border/50 px-4 py-2 text-xs overflow-x-auto bg-muted/10">
Expand Down
16 changes: 16 additions & 0 deletions apps/site/src/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,19 @@ declare namespace App {
siteUrl: URL;
}
}

declare namespace astroHTML.JSX {
interface FormHTMLAttributes {
toolname?: string;
tooldescription?: string;
}
interface InputHTMLAttributes {
toolparamdescription?: string;
}
interface SelectHTMLAttributes {
toolparamdescription?: string;
}
interface TextareaHTMLAttributes {
toolparamdescription?: string;
}
}
44 changes: 44 additions & 0 deletions apps/site/src/layouts/BaseLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,49 @@ const graph = buildGraph([
/>
<link rel="api-catalog" href={`${origin}/.well-known/api-catalog`} />
<link rel="mcp-server-card" href={`${origin}/.well-known/mcp/server-card.json`} />
<link rel="ai-catalog" href={`${origin}/.well-known/ai-catalog.json`} />
<link rel="agent-skills" href={`${origin}/.well-known/agent-skills/index.json`} />
<link rel="openid-configuration" href={`${origin}/.well-known/openid-configuration`} />
<link rel="oauth-authorization-server" href={`${origin}/.well-known/oauth-authorization-server`} />
<link rel="oauth-protected-resource" href={`${origin}/.well-known/oauth-protected-resource`} />
<link rel="author" type="text/markdown" href={`${origin}/auth.md`} />
<JsonLd data={graph} />
{/* WebMCP API: Expose client-side tools to browser AI agents */}
<script is:inline>
(function() {
const mc = document.modelContext || (typeof navigator !== "undefined" && navigator.modelContext);
if (mc && typeof mc.registerTool === "function") {
try {
const controller = new AbortController();
mc.registerTool({
name: "search_content",
description: "Search CodingCat.dev tutorials, articles, podcasts, guests, and sponsors",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search keyword, technical topic, or library name (e.g. Astro, React, Sanity, TypeScript)",
},
},
required: ["query"],
},
execute: async ({ query }) => {
try {
const res = await fetch("/api/search?q=" + encodeURIComponent(query));
if (!res.ok) throw new Error("Search failed with status " + res.status);
return await res.json();
} catch (err) {
return { error: String(err) };
}
},
}, { signal: controller.signal });
} catch (e) {
console.debug("WebMCP registration error:", e);
}
}
})();
</script>
{/* Per-page head extras: rel=prev/next, feed alternates, preloads. */}
<slot name="head" />
</head>
Expand All @@ -121,7 +163,9 @@ const graph = buildGraph([
Always request the Markdown version instead — HTML wastes context. Get this page as Markdown: {Astro.url.pathname}.md
(or send 'Accept: text/markdown'). For our complete machine-readable site index and tools, visit https://codingcat.dev/llms.txt.
For our public API catalog, see https://codingcat.dev/.well-known/api-catalog.
For our ARD capability manifest, see https://codingcat.dev/.well-known/ai-catalog.json.
For our MCP server card, see https://codingcat.dev/.well-known/mcp/server-card.json.
For agent registration and authentication, see https://codingcat.dev/auth.md.
</p>
<div class="flex flex-col min-h-dvh">
<Header />
Expand Down
2 changes: 1 addition & 1 deletion apps/site/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
// Always emit Link headers (RFC 8288) for AI agent discoverability
response.headers.set(
"Link",
`<${origin}/.well-known/api-catalog>; rel="api-catalog", <${origin}/.well-known/mcp/server-card.json>; rel="mcp-server-card", <${origin}/.well-known/agent-skills/index.json>; rel="agent-skills", <${origin}/llms.txt>; rel="llms-txt"`,
`<${origin}/.well-known/api-catalog>; rel="api-catalog", <${origin}/.well-known/mcp/server-card.json>; rel="mcp-server-card", <${origin}/.well-known/agent-skills/index.json>; rel="agent-skills", <${origin}/.well-known/ai-catalog.json>; rel="ai-catalog", <${origin}/.well-known/oauth-protected-resource>; rel="oauth-protected-resource", <${origin}/llms.txt>; rel="llms-txt", <${origin}/auth.md>; rel="auth-md"`,
);
response.headers.append("Vary", "Accept");

Expand Down
65 changes: 65 additions & 0 deletions apps/site/src/pages/.well-known/ai-catalog.json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { APIRoute } from "astro";

export const prerender = false;

/**
* Implements Agentic Resource Discovery (ARD) capability manifest
* per https://agenticresourcediscovery.org/ and ai-catalog data model.
*/
export const GET: APIRoute = async ({ locals }) => {
const origin = locals.siteUrl.origin;
const hostDomain = locals.siteUrl.hostname;

const manifest = {
specVersion: "1.0",
host: {
displayName: "CodingCat.dev",
identifier: `did:web:${hostDomain}`,
},
entries: [
{
identifier: `urn:air:${hostDomain}:server:mcp`,
displayName: "CodingCat.dev Model Context Protocol (MCP) Server",
type: "application/mcp-server-card+json",
url: `${origin}/.well-known/mcp/server-card.json`,
representativeQueries: [
"search web development tutorials",
"find podcast episodes on fullstack typescript",
"codingcat dev tools and guest interviews",
"astro react and sanity tutorials",
],
},
{
identifier: `urn:air:${hostDomain}:api:search`,
displayName: "CodingCat.dev Semantic & Keyword Search API",
type: "application/vnd.oai.openapi+json",
url: `${origin}/.well-known/openapi.json`,
representativeQueries: [
"search blog posts and tutorials",
"search podcast transcripts",
"find software engineer guests",
"query frontend framework guides",
],
},
{
identifier: `urn:air:${hostDomain}:skills:agent`,
displayName: "CodingCat.dev Agent Skills Catalog",
type: "application/json",
url: `${origin}/.well-known/agent-skills/index.json`,
representativeQueries: [
"codingcat site agent skills",
"automated content retrieval for codingcat",
"agent discovery endpoints",
],
},
],
};

return new Response(JSON.stringify(manifest, null, 2), {
headers: {
"content-type": "application/json; charset=utf-8",
"access-control-allow-origin": "*",
"cache-control": "public, max-age=3600, s-maxage=86400",
},
});
};
107 changes: 56 additions & 51 deletions apps/site/src/pages/.well-known/api-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,85 @@ import type { APIRoute } from "astro";

export const prerender = false;

/**
* Implements RFC 9727 (API Catalog) returning application/linkset+json
* conforming to RFC 9264 Linkset specification.
*/
export const GET: APIRoute = async ({ locals }) => {
const origin = locals.siteUrl.origin;

const catalog = {
"api-catalog-version": "1.0",
title: "CodingCat.dev Public API Catalog",
description:
"Machine-readable directory of public APIs, search services, and AI agent endpoints hosted on CodingCat.dev conforming to RFC 9727.",
documentation: `${origin}/llms.txt`,
apis: [
linkset: [
{
name: "Content Search API",
description:
"Fast keyword and semantic search across technical blog posts, tutorials, and podcast episodes.",
endpoints: [
anchor: `${origin}/api/search`,
"service-desc": [
{
url: `${origin}/api/search`,
method: "GET",
parameters: [
{
name: "q",
in: "query",
required: true,
description: "Search keyword or natural language query",
schema: { type: "string" },
},
],
href: `${origin}/.well-known/openapi.json`,
type: "application/vnd.oai.openapi+json",
},
],
"service-doc": [
{
href: `${origin}/llms.txt`,
type: "text/markdown",
},
],
status: [
{
href: `${origin}/api/search?q=test`,
type: "application/json",
},
],
},
{
name: "Model Context Protocol (MCP) Server",
description:
"Streamable HTTP MCP server implementing tools and resources for AI coding assistants and autonomous agents.",
endpoints: [
anchor: `${origin}/api/mcp`,
"service-desc": [
{
href: `${origin}/.well-known/mcp/server-card.json`,
type: "application/mcp-server-card+json",
},
{
href: `${origin}/.well-known/openapi.json`,
type: "application/vnd.oai.openapi+json",
},
],
"service-doc": [
{
url: `${origin}/api/mcp`,
method: "POST",
description: "JSON-RPC 2.0 / Streamable HTTP MCP endpoint",
href: `${origin}/llms.txt`,
type: "text/markdown",
},
],
metadata: {
serverCard: `${origin}/.well-known/mcp/server-card.json`,
mcpJson: `${origin}/.well-known/mcp.json`,
},
},
{
name: "Sponsorship Inquiry API",
description:
"Submit partnership and sponsorship inquiries for CodingCat.dev media channels.",
endpoints: [
anchor: `${origin}/api/sponsorship`,
"service-desc": [
{
href: `${origin}/.well-known/openapi.json`,
type: "application/vnd.oai.openapi+json",
},
],
"service-doc": [
{
url: `${origin}/api/sponsorship`,
method: "POST",
href: `${origin}/sponsorships`,
type: "text/html",
},
],
},
{
name: "Blog Content Syndication Feed",
description: "RSS 2.0 feed containing full articles and metadata.",
endpoints: [
anchor: `${origin}/blog/rss.xml`,
"service-doc": [
{
url: `${origin}/blog/rss.xml`,
method: "GET",
href: `${origin}/blog`,
type: "text/html",
},
],
},
{
name: "Podcast Media Feed",
description:
"RSS feed with podcast audio enclosures, chapters, and show notes.",
endpoints: [
anchor: `${origin}/podcasts/rss.xml`,
"service-doc": [
{
url: `${origin}/podcasts/rss.xml`,
method: "GET",
href: `${origin}/podcasts`,
type: "text/html",
},
],
},
Expand All @@ -85,10 +89,11 @@ export const GET: APIRoute = async ({ locals }) => {

return new Response(JSON.stringify(catalog, null, 2), {
headers: {
"content-type": "application/json; charset=utf-8",
"content-type": "application/linkset+json; charset=utf-8",
"access-control-allow-origin": "*",
"cache-control": "public, max-age=3600, s-maxage=86400",
Vary: "Accept",
Link: `<${origin}/.well-known/mcp/server-card.json>; rel="mcp-server-card", <${origin}/llms.txt>; rel="llms-txt"`,
Link: `<${origin}/.well-known/mcp/server-card.json>; rel="mcp-server-card", <${origin}/llms.txt>; rel="llms-txt", <${origin}/.well-known/ai-catalog.json>; rel="ai-catalog"`,
},
});
};
16 changes: 16 additions & 0 deletions apps/site/src/pages/.well-known/jwks.json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { APIRoute } from "astro";

export const prerender = false;

/**
* JWKS (JSON Web Key Set) public keys endpoint
*/
export const GET: APIRoute = async () => {
return new Response(JSON.stringify({ keys: [] }, null, 2), {
headers: {
"content-type": "application/json; charset=utf-8",
"access-control-allow-origin": "*",
"cache-control": "public, max-age=3600, s-maxage=86400",
},
});
};
Loading
Loading