Add OG social cards, Mermaid diagrams, and robots.txt
- OG images: auto-generated per page via astro-og-canvas with maritime teal theme, wired through Starlight route data middleware - Mermaid: request flow and parallel fetch diagrams on architecture page - robots.txt: sitemap and llms.txt references for crawlers
This commit is contained in:
parent
e519c100ed
commit
a5009941ab
@ -1,4 +1,5 @@
|
||||
import { defineConfig } from "astro/config";
|
||||
import mermaid from "astro-mermaid";
|
||||
import starlight from "@astrojs/starlight";
|
||||
import starlightLlmsTxt from "starlight-llms-txt";
|
||||
|
||||
@ -10,10 +11,12 @@ export default defineConfig({
|
||||
telemetry: false,
|
||||
devToolbar: { enabled: false },
|
||||
integrations: [
|
||||
mermaid(),
|
||||
starlight({
|
||||
title: "mcnoaa-tides",
|
||||
description:
|
||||
"FastMCP server for NOAA CO-OPS tide predictions, water levels, and marine conditions.",
|
||||
routeMiddleware: "./src/routeData.ts",
|
||||
plugins: [
|
||||
starlightLlmsTxt({
|
||||
projectName: "mcnoaa-tides",
|
||||
|
||||
1314
docs/package-lock.json
generated
1314
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,8 @@
|
||||
"@iconify-json/lucide": "^1.2.91",
|
||||
"astro": "^5.6.1",
|
||||
"astro-icon": "^1.1.5",
|
||||
"astro-mermaid": "^1.3.1",
|
||||
"astro-og-canvas": "^0.10.1",
|
||||
"sharp": "^0.34.2",
|
||||
"starlight-llms-txt": "^0.7.0"
|
||||
}
|
||||
|
||||
7
docs/public/robots.txt
Normal file
7
docs/public/robots.txt
Normal file
@ -0,0 +1,7 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://mcnoaa-tides.warehack.ing/sitemap-index.xml
|
||||
|
||||
# LLM documentation — https://llmstxt.org/
|
||||
LLMs-Txt: https://mcnoaa-tides.warehack.ing/llms.txt
|
||||
@ -9,6 +9,35 @@ import { Aside, Card, CardGrid, Steps, FileTree, Badge } from "@astrojs/starligh
|
||||
|
||||
mcnoaa-tides is a FastMCP server that wraps the NOAA CO-OPS Tides and Currents API. This page describes how the pieces fit together -- the server lifecycle, caching strategy, parallel fetch patterns, and module organization.
|
||||
|
||||
## Request Flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
User([User])
|
||||
LLM[LLM Client]
|
||||
MCP[MCP Transport<br/>stdio / HTTP]
|
||||
Server[mcnoaa-tides<br/>FastMCP Server]
|
||||
Cache[(Station<br/>Cache)]
|
||||
DataAPI[NOAA Data API<br/>Predictions &<br/>Observations]
|
||||
MetaAPI[NOAA Metadata API<br/>Station Catalog]
|
||||
|
||||
User -->|natural language| LLM
|
||||
LLM -->|tool call| MCP
|
||||
MCP --> Server
|
||||
Server -->|cache hit| Cache
|
||||
Server -->|parallel fetch| DataAPI
|
||||
Server -->|catalog refresh| MetaAPI
|
||||
Cache -.->|24h TTL| MetaAPI
|
||||
|
||||
style User fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style LLM fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style MCP fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style Server fill:#1a8a8f,stroke:#5ec4c8,color:#0a1517
|
||||
style Cache fill:#0d3b3e,stroke:#5ec4c8,color:#e8f0f0
|
||||
style DataAPI fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style MetaAPI fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
```
|
||||
|
||||
## Server Lifecycle
|
||||
|
||||
The server uses FastMCP's lifespan context manager to own the `NOAAClient` instance. Every tool call receives the same client through the lifespan context, so there is exactly one HTTP connection pool and one station cache for the entire server process.
|
||||
@ -86,9 +115,28 @@ Several tools fire multiple API calls simultaneously using `asyncio.gather`. Thi
|
||||
|
||||
Fetches 6 products in parallel:
|
||||
|
||||
```
|
||||
predictions (hilo) | water_level | water_temperature
|
||||
air_temperature | wind | air_pressure
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Tool[marine_conditions<br/>_snapshot]
|
||||
P[predictions<br/>hilo]
|
||||
WL[water_level]
|
||||
WT[water_temperature]
|
||||
AT[air_temperature]
|
||||
W[wind]
|
||||
AP[air_pressure]
|
||||
R[Combined<br/>Response]
|
||||
|
||||
Tool --> P & WL & WT & AT & W & AP
|
||||
P & WL & WT & AT & W & AP --> R
|
||||
|
||||
style Tool fill:#1a8a8f,stroke:#5ec4c8,color:#0a1517
|
||||
style R fill:#1a8a8f,stroke:#5ec4c8,color:#0a1517
|
||||
style P fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style WL fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style WT fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style AT fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style W fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
style AP fill:#0d3b3e,stroke:#1a8a8f,color:#e8f0f0
|
||||
```
|
||||
|
||||
Each product is fetched independently. If a product fails (sensor not available at the station, temporary API error), it is recorded under an `"unavailable"` key in the response rather than failing the entire request.
|
||||
|
||||
40
docs/src/pages/og/[...slug].ts
Normal file
40
docs/src/pages/og/[...slug].ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { getCollection } from "astro:content";
|
||||
import { OGImageRoute } from "astro-og-canvas";
|
||||
|
||||
const entries = await getCollection("docs");
|
||||
const pages = Object.fromEntries(
|
||||
entries.map(({ data, id }) => [id, { data }]),
|
||||
);
|
||||
|
||||
export const { getStaticPaths, GET } = await OGImageRoute({
|
||||
pages,
|
||||
param: "slug",
|
||||
getImageOptions: (_id, page: (typeof pages)[number]) => ({
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
bgGradient: [[10, 21, 23]],
|
||||
border: { color: [26, 138, 143], width: 20 },
|
||||
padding: 120,
|
||||
font: {
|
||||
title: {
|
||||
size: 64,
|
||||
weight: "Bold",
|
||||
color: [232, 240, 240],
|
||||
families: ["Inter"],
|
||||
},
|
||||
description: {
|
||||
size: 28,
|
||||
color: [196, 212, 214],
|
||||
families: ["Inter"],
|
||||
},
|
||||
},
|
||||
fonts: [
|
||||
"https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-600-normal.woff2",
|
||||
"https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.woff2",
|
||||
],
|
||||
logo: {
|
||||
path: "./public/favicon.svg",
|
||||
size: [60],
|
||||
},
|
||||
}),
|
||||
});
|
||||
27
docs/src/routeData.ts
Normal file
27
docs/src/routeData.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { defineRouteMiddleware } from "@astrojs/starlight/route-data";
|
||||
|
||||
export const onRequest = defineRouteMiddleware((context) => {
|
||||
const ogImageUrl = new URL(
|
||||
`/og/${context.locals.starlightRoute.id || "index"}.png`,
|
||||
context.site,
|
||||
);
|
||||
|
||||
const { head } = context.locals.starlightRoute;
|
||||
|
||||
head.push({
|
||||
tag: "meta",
|
||||
attrs: { property: "og:image", content: ogImageUrl.href },
|
||||
});
|
||||
head.push({
|
||||
tag: "meta",
|
||||
attrs: { property: "og:image:width", content: "1200" },
|
||||
});
|
||||
head.push({
|
||||
tag: "meta",
|
||||
attrs: { property: "og:image:height", content: "630" },
|
||||
});
|
||||
head.push({
|
||||
tag: "meta",
|
||||
attrs: { name: "twitter:image", content: ogImageUrl.href },
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user