Skip to content

Commit d4dc343

Browse files
committed
docs(landing): SEO/GEO for the SQL docs page
- sitemap.xml + sitemap.md: add /docs/sql (IndexNow submits it on deploy, since it parses sitemap.xml). - DocsPage: per-document SEO meta — unique <title>, description, canonical, Open Graph, Twitter, and TechArticle JSON-LD — so every doc page (including sql) is independently indexable and answer-engine citable. - Re-sync public/docs/openapi.json to the 0.9.0 spec. llms.txt already lists the SQL page (Reference + core commands); public/docs/sql.md is synced from docs/sql.md by generate:md.
1 parent 0964e6a commit d4dc343

4 files changed

Lines changed: 128 additions & 1 deletion

File tree

landing/public/docs/openapi.json

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"openapi": "3.1.0",
33
"info": {
44
"title": "agent-fs API",
5-
"version": "0.8.1",
5+
"version": "0.9.0",
66
"description": "A persistent, searchable filesystem for AI agents. agent-fs is to files what agentmail is to email.",
77
"license": {
88
"name": "MIT",
@@ -769,6 +769,69 @@
769769
},
770770
"additionalProperties": false
771771
},
772+
{
773+
"type": "object",
774+
"title": "sql",
775+
"description": "Run a DuckDB SQL query over documents stored in the drive. Reference documents directly by path string literal (e.g. SELECT * FROM '/data/sales.csv') or bind them as named tables via `tables` ({ name: path } or { name: { path, format } } to override format detection). Supports csv, tsv, parquet, xlsx, json, ndjson/jsonl (each also .gz except parquet/xlsx), sqlite (.db/.sqlite/.sqlite3, tables exposed as name.tablename), and .duckdb files. Queries run sandboxed: no filesystem or network access beyond the bound documents. Returns { columns, rows, rowCount, truncated, files, elapsedMs }.",
776+
"required": [
777+
"op",
778+
"query"
779+
],
780+
"properties": {
781+
"op": {
782+
"type": "string",
783+
"const": "sql"
784+
},
785+
"driveId": {
786+
"type": "string",
787+
"description": "Target drive ID (optional, uses default drive)"
788+
},
789+
"query": {
790+
"type": "string"
791+
},
792+
"tables": {
793+
"type": "object",
794+
"additionalProperties": {
795+
"anyOf": [
796+
{
797+
"type": "string"
798+
},
799+
{
800+
"type": "object",
801+
"properties": {
802+
"path": {
803+
"type": "string"
804+
},
805+
"format": {
806+
"type": "string",
807+
"enum": [
808+
"csv",
809+
"tsv",
810+
"parquet",
811+
"xlsx",
812+
"json",
813+
"ndjson",
814+
"sqlite",
815+
"duckdb"
816+
]
817+
}
818+
},
819+
"required": [
820+
"path"
821+
],
822+
"additionalProperties": false
823+
}
824+
]
825+
}
826+
},
827+
"maxRows": {
828+
"type": "integer",
829+
"minimum": 1,
830+
"maximum": 10000
831+
}
832+
},
833+
"additionalProperties": false
834+
},
772835
{
773836
"type": "object",
774837
"title": "signed-url",

landing/public/sitemap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ machine-readable representation (where available) linked alongside.
1414
- [/docs](https://agent-fs.dev/docs) — Documentation index.
1515
- [/docs/api-reference](https://agent-fs.dev/docs/api-reference) — HTTP API,
1616
auth, MCP transport, and operation dispatch.
17+
- [/docs/sql](https://agent-fs.dev/docs/sql) — Query CSV, Parquet, Excel, JSON,
18+
and SQLite documents with DuckDB (CLI, API, MCP).
1719
- [/docs/mcp-setup](https://agent-fs.dev/docs/mcp-setup) — MCP client setup.
1820
- [/docs/deployment](https://agent-fs.dev/docs/deployment) — Local and hosted
1921
deployment guide.

landing/public/sitemap.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
<changefreq>weekly</changefreq>
1919
<priority>0.8</priority>
2020
</url>
21+
<url>
22+
<loc>https://agent-fs.dev/docs/sql</loc>
23+
<lastmod>2026-06-11</lastmod>
24+
<changefreq>weekly</changefreq>
25+
<priority>0.8</priority>
26+
</url>
2127
<url>
2228
<loc>https://agent-fs.dev/docs/mcp-setup</loc>
2329
<lastmod>2026-05-19</lastmod>

landing/src/pages/DocsPage.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,62 @@ export function DocsPage({ slug }: { slug?: string }) {
685685
const activeId = useActiveHeading(activeDoc.slug)
686686
const sidebarRef = useRef<HTMLElement | null>(null)
687687

688+
// Per-document SEO/GEO: unique title, description, canonical, Open Graph,
689+
// Twitter, and TechArticle JSON-LD so each doc page is independently
690+
// indexable by search engines and citable by answer engines.
691+
useEffect(() => {
692+
const url = `https://agent-fs.dev/docs/${activeDoc.slug}`
693+
const title = `${activeDoc.title} — agent-fs docs`
694+
document.title = title
695+
696+
const upsertMeta = (
697+
key: string,
698+
content: string,
699+
attr: "name" | "property" = "name",
700+
) => {
701+
let el = document.head.querySelector<HTMLMetaElement>(`meta[${attr}="${key}"]`)
702+
if (!el) {
703+
el = document.createElement("meta")
704+
el.setAttribute(attr, key)
705+
document.head.appendChild(el)
706+
}
707+
el.setAttribute("content", content)
708+
}
709+
upsertMeta("description", activeDoc.summary)
710+
upsertMeta("og:title", title, "property")
711+
upsertMeta("og:description", activeDoc.summary, "property")
712+
upsertMeta("og:url", url, "property")
713+
upsertMeta("og:type", "article", "property")
714+
upsertMeta("twitter:title", title)
715+
upsertMeta("twitter:description", activeDoc.summary)
716+
717+
let canonical = document.head.querySelector<HTMLLinkElement>('link[rel="canonical"]')
718+
if (!canonical) {
719+
canonical = document.createElement("link")
720+
canonical.rel = "canonical"
721+
document.head.appendChild(canonical)
722+
}
723+
canonical.href = url
724+
725+
const jsonLd = {
726+
"@context": "https://schema.org",
727+
"@type": "TechArticle",
728+
headline: activeDoc.title,
729+
description: activeDoc.summary,
730+
url,
731+
inLanguage: "en",
732+
isPartOf: { "@type": "WebSite", name: "agent-fs", url: "https://agent-fs.dev" },
733+
}
734+
let script = document.getElementById("doc-jsonld") as HTMLScriptElement | null
735+
if (!script) {
736+
script = document.createElement("script")
737+
script.id = "doc-jsonld"
738+
script.type = "application/ld+json"
739+
document.head.appendChild(script)
740+
}
741+
script.textContent = JSON.stringify(jsonLd)
742+
}, [activeDoc])
743+
688744
useEffect(() => {
689745
const onKey = (e: KeyboardEvent) => {
690746
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {

0 commit comments

Comments
 (0)