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 { defineConfig } from "astro/config";
|
||||||
|
import mermaid from "astro-mermaid";
|
||||||
import starlight from "@astrojs/starlight";
|
import starlight from "@astrojs/starlight";
|
||||||
import starlightLlmsTxt from "starlight-llms-txt";
|
import starlightLlmsTxt from "starlight-llms-txt";
|
||||||
|
|
||||||
@ -10,10 +11,12 @@ export default defineConfig({
|
|||||||
telemetry: false,
|
telemetry: false,
|
||||||
devToolbar: { enabled: false },
|
devToolbar: { enabled: false },
|
||||||
integrations: [
|
integrations: [
|
||||||
|
mermaid(),
|
||||||
starlight({
|
starlight({
|
||||||
title: "mcnoaa-tides",
|
title: "mcnoaa-tides",
|
||||||
description:
|
description:
|
||||||
"FastMCP server for NOAA CO-OPS tide predictions, water levels, and marine conditions.",
|
"FastMCP server for NOAA CO-OPS tide predictions, water levels, and marine conditions.",
|
||||||
|
routeMiddleware: "./src/routeData.ts",
|
||||||
plugins: [
|
plugins: [
|
||||||
starlightLlmsTxt({
|
starlightLlmsTxt({
|
||||||
projectName: "mcnoaa-tides",
|
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",
|
"@iconify-json/lucide": "^1.2.91",
|
||||||
"astro": "^5.6.1",
|
"astro": "^5.6.1",
|
||||||
"astro-icon": "^1.1.5",
|
"astro-icon": "^1.1.5",
|
||||||
|
"astro-mermaid": "^1.3.1",
|
||||||
|
"astro-og-canvas": "^0.10.1",
|
||||||
"sharp": "^0.34.2",
|
"sharp": "^0.34.2",
|
||||||
"starlight-llms-txt": "^0.7.0"
|
"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.
|
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
|
## 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.
|
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:
|
Fetches 6 products in parallel:
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
predictions (hilo) | water_level | water_temperature
|
flowchart LR
|
||||||
air_temperature | wind | air_pressure
|
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.
|
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