diff --git a/config.yml b/config.yml
index 17bc68b..4850717 100644
--- a/config.yml
+++ b/config.yml
@@ -151,25 +151,6 @@ repos:
lib: site
api: site
- - name: vet
- source: ../react/vet
- title: Vet
- description: Veterinary clinic demo front-end built on react-openapi — shows the spec-driven admin, theming and routing in one runnable app.
- section: apps
- docs:
- lib: site
- mcp: { bundle: docs/mcp, server: vet, port: 8013 }
-
- - name: vet-app
- source: ../react/vet/server
- title: Vet Server
- description: Veterinary clinic backend API (OpenAPI-first) with JWT auth and dual in-memory/Mongo persistence.
- section: apps
- docs:
- wiki: site
- lib: site
- api: site
-
static:
- kind: wiki
repo: standards
diff --git a/standards/lib/sitemap.xml.gz b/standards/lib/sitemap.xml.gz
index 43b6693ea67df02dd9f5455bc82850339c9ac646..7140084f63cdc7ac094f9b92e2c304376b6ad4a8 100644
GIT binary patch
delta 13
Ucmb=gXP58h;An`wI+48s03EOdf&c&j
delta 13
Ucmb=gXP58h;9%gnGLgLk02vnpumAu6
diff --git a/standards/mcp/modules/wiki_mcp_setup.json b/standards/mcp/modules/wiki_mcp_setup.json
index 5aa3f40..75b46f7 100644
--- a/standards/mcp/modules/wiki_mcp_setup.json
+++ b/standards/mcp/modules/wiki_mcp_setup.json
@@ -2,7 +2,7 @@
"module": "wiki_mcp_setup",
"content": {
"path": "wiki_mcp_setup",
- "docstring": "# 🔌 MCP Setup\n\nEvery repository ships its documentation as a **Model Context Protocol (MCP)**\nserver, and the `standards` registry server ties them together. The `docs`\nportal runs each one as a FastMCP **streamable-http** app bound to\n`0.0.0.0:`, exposing the endpoint at `/mcp` (see `docs/main.py` and\n`docs/config.yml`).\n\nConnect your agent to the **`standards`** server first: it answers\n`find_library`, `list_repos`, and the `start_app` prompt, so the agent reaches\nfor an internal library instead of reimplementing one (see the\n[Library Registry](../lib/13_library_registry/)).\n\n---\n\n## ▶️ Run the servers locally\n\nFrom the `docs` checkout:\n\n```bash\n.venv\\Scripts\\python.exe main.py # every server\n.venv\\Scripts\\python.exe main.py --servers standards # just the registry\n```\n\n`:8000` is the health endpoint (`GET /healthz`), **not** an MCP server.\n\n---\n\n## 🧭 Servers\n\n| Slug | MCP server | Port | localhost | mcp.aetoskia.com |\n|------|------------|------|-----------|------------------|\n| `standards` | `standards` | 8009 | `http://localhost:8009/mcp` | `https://mcp.aetoskia.com/standards/mcp` |\n| `doc-forge` | `docforge` | 8001 | `http://localhost:8001/mcp` | `https://mcp.aetoskia.com/doc-forge/mcp` |\n| `mail-intake` | `mail_intake` | 8002 | `http://localhost:8002/mcp` | `https://mcp.aetoskia.com/mail-intake/mcp` |\n| `omniread` | `omniread` | 8003 | `http://localhost:8003/mcp` | `https://mcp.aetoskia.com/omniread/mcp` |\n| `openapi-first` | `openapi_first` | 8004 | `http://localhost:8004/mcp` | `https://mcp.aetoskia.com/openapi-first/mcp` |\n| `py-jwt` | `jwtlib` | 8005 | `http://localhost:8005/mcp` | `https://mcp.aetoskia.com/py-jwt/mcp` |\n| `dagpipe` | `dagpipe` | 8006 | `http://localhost:8006/mcp` | `https://mcp.aetoskia.com/dagpipe/mcp` |\n| `mongo-ops` | `mongo_ops` | 8007 | `http://localhost:8007/mcp` | `https://mcp.aetoskia.com/mongo-ops/mcp` |\n| `hexa` | `hexa` | 8008 | `http://localhost:8008/mcp` | `https://mcp.aetoskia.com/hexa/mcp` |\n| `doc-weaver` | `doc-weaver` | 8010 | `http://localhost:8010/mcp` | `https://mcp.aetoskia.com/doc-weaver/mcp` |\n| `react-auth` | `react-auth` | 8011 | `http://localhost:8011/mcp` | `https://mcp.aetoskia.com/react-auth/mcp` |\n| `react-openapi` | `react-openapi` | 8012 | `http://localhost:8012/mcp` | `https://mcp.aetoskia.com/react-openapi/mcp` |\n| `vet` | `vet` | 8013 | `http://localhost:8013/mcp` | `https://mcp.aetoskia.com/vet/mcp` |\n\nThe hosted endpoints are served over HTTPS at `https://mcp.aetoskia.com//mcp`.\nThe tabs below are linked (`content.tabs.link`): switching one switches all of\nthem. Every snippet registers **all thirteen** servers, so a single paste is\nenough.\n\n---\n\n## 🤖 Claude Code\n\nProject-scoped `.mcp.json` at the repo root. The `type` field is required — an\nentry with a `url` but no `type` is read as a stdio server and skipped.\n\n=== \"localhost\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"type\": \"http\", \"url\": \"http://localhost:8009/mcp\" },\n \"doc-forge\": { \"type\": \"http\", \"url\": \"http://localhost:8001/mcp\" },\n \"mail-intake\": { \"type\": \"http\", \"url\": \"http://localhost:8002/mcp\" },\n \"omniread\": { \"type\": \"http\", \"url\": \"http://localhost:8003/mcp\" },\n \"openapi-first\": { \"type\": \"http\", \"url\": \"http://localhost:8004/mcp\" },\n \"py-jwt\": { \"type\": \"http\", \"url\": \"http://localhost:8005/mcp\" },\n \"dagpipe\": { \"type\": \"http\", \"url\": \"http://localhost:8006/mcp\" },\n \"mongo-ops\": { \"type\": \"http\", \"url\": \"http://localhost:8007/mcp\" },\n \"hexa\": { \"type\": \"http\", \"url\": \"http://localhost:8008/mcp\" },\n \"doc-weaver\": { \"type\": \"http\", \"url\": \"http://localhost:8010/mcp\" },\n \"react-auth\": { \"type\": \"http\", \"url\": \"http://localhost:8011/mcp\" },\n \"react-openapi\": { \"type\": \"http\", \"url\": \"http://localhost:8012/mcp\" },\n \"vet\": { \"type\": \"http\", \"url\": \"http://localhost:8013/mcp\" }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/standards/mcp\" },\n \"doc-forge\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/doc-forge/mcp\" },\n \"mail-intake\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/mail-intake/mcp\" },\n \"omniread\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/omniread/mcp\" },\n \"openapi-first\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/openapi-first/mcp\" },\n \"py-jwt\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/py-jwt/mcp\" },\n \"dagpipe\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/dagpipe/mcp\" },\n \"mongo-ops\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/mongo-ops/mcp\" },\n \"hexa\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/hexa/mcp\" },\n \"doc-weaver\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/doc-weaver/mcp\" },\n \"react-auth\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/react-auth/mcp\" },\n \"react-openapi\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/react-openapi/mcp\" },\n \"vet\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/vet/mcp\" }\n }\n }\n ```\n\nOr add them with the CLI:\n`claude mcp add --transport http standards http://localhost:8009/mcp`.\n\n---\n\n## 🤖 Codex\n\n`~/.codex/config.toml` (or a project-scoped `.codex/config.toml`).\n\n=== \"localhost\"\n\n ```toml\n [mcp_servers.standards]\n url = \"http://localhost:8009/mcp\"\n\n [mcp_servers.doc-forge]\n url = \"http://localhost:8001/mcp\"\n\n [mcp_servers.mail-intake]\n url = \"http://localhost:8002/mcp\"\n\n [mcp_servers.omniread]\n url = \"http://localhost:8003/mcp\"\n\n [mcp_servers.openapi-first]\n url = \"http://localhost:8004/mcp\"\n\n [mcp_servers.py-jwt]\n url = \"http://localhost:8005/mcp\"\n\n [mcp_servers.dagpipe]\n url = \"http://localhost:8006/mcp\"\n\n [mcp_servers.mongo-ops]\n url = \"http://localhost:8007/mcp\"\n\n [mcp_servers.hexa]\n url = \"http://localhost:8008/mcp\"\n\n [mcp_servers.doc-weaver]\n url = \"http://localhost:8010/mcp\"\n\n [mcp_servers.react-auth]\n url = \"http://localhost:8011/mcp\"\n\n [mcp_servers.react-openapi]\n url = \"http://localhost:8012/mcp\"\n\n [mcp_servers.vet]\n url = \"http://localhost:8013/mcp\"\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```toml\n [mcp_servers.standards]\n url = \"https://mcp.aetoskia.com/standards/mcp\"\n\n [mcp_servers.doc-forge]\n url = \"https://mcp.aetoskia.com/doc-forge/mcp\"\n\n [mcp_servers.mail-intake]\n url = \"https://mcp.aetoskia.com/mail-intake/mcp\"\n\n [mcp_servers.omniread]\n url = \"https://mcp.aetoskia.com/omniread/mcp\"\n\n [mcp_servers.openapi-first]\n url = \"https://mcp.aetoskia.com/openapi-first/mcp\"\n\n [mcp_servers.py-jwt]\n url = \"https://mcp.aetoskia.com/py-jwt/mcp\"\n\n [mcp_servers.dagpipe]\n url = \"https://mcp.aetoskia.com/dagpipe/mcp\"\n\n [mcp_servers.mongo-ops]\n url = \"https://mcp.aetoskia.com/mongo-ops/mcp\"\n\n [mcp_servers.hexa]\n url = \"https://mcp.aetoskia.com/hexa/mcp\"\n\n [mcp_servers.doc-weaver]\n url = \"https://mcp.aetoskia.com/doc-weaver/mcp\"\n\n [mcp_servers.react-auth]\n url = \"https://mcp.aetoskia.com/react-auth/mcp\"\n\n [mcp_servers.react-openapi]\n url = \"https://mcp.aetoskia.com/react-openapi/mcp\"\n\n [mcp_servers.vet]\n url = \"https://mcp.aetoskia.com/vet/mcp\"\n ```\n\nOr add them with the CLI:\n`codex mcp add standards --url http://localhost:8009/mcp`.\n\n---\n\n## 🤖 Antigravity\n\n`~/.gemini/antigravity/mcp_config.json` (IDE) or\n`~/.gemini/config/mcp_config.json` (CLI). Antigravity uses **`serverUrl`**, not\n`url`, for HTTP endpoints.\n\n=== \"localhost\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"serverUrl\": \"http://localhost:8009/mcp\" },\n \"doc-forge\": { \"serverUrl\": \"http://localhost:8001/mcp\" },\n \"mail-intake\": { \"serverUrl\": \"http://localhost:8002/mcp\" },\n \"omniread\": { \"serverUrl\": \"http://localhost:8003/mcp\" },\n \"openapi-first\": { \"serverUrl\": \"http://localhost:8004/mcp\" },\n \"py-jwt\": { \"serverUrl\": \"http://localhost:8005/mcp\" },\n \"dagpipe\": { \"serverUrl\": \"http://localhost:8006/mcp\" },\n \"mongo-ops\": { \"serverUrl\": \"http://localhost:8007/mcp\" },\n \"hexa\": { \"serverUrl\": \"http://localhost:8008/mcp\" },\n \"doc-weaver\": { \"serverUrl\": \"http://localhost:8010/mcp\" },\n \"react-auth\": { \"serverUrl\": \"http://localhost:8011/mcp\" },\n \"react-openapi\": { \"serverUrl\": \"http://localhost:8012/mcp\" },\n \"vet\": { \"serverUrl\": \"http://localhost:8013/mcp\" }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"serverUrl\": \"https://mcp.aetoskia.com/standards/mcp\" },\n \"doc-forge\": { \"serverUrl\": \"https://mcp.aetoskia.com/doc-forge/mcp\" },\n \"mail-intake\": { \"serverUrl\": \"https://mcp.aetoskia.com/mail-intake/mcp\" },\n \"omniread\": { \"serverUrl\": \"https://mcp.aetoskia.com/omniread/mcp\" },\n \"openapi-first\": { \"serverUrl\": \"https://mcp.aetoskia.com/openapi-first/mcp\" },\n \"py-jwt\": { \"serverUrl\": \"https://mcp.aetoskia.com/py-jwt/mcp\" },\n \"dagpipe\": { \"serverUrl\": \"https://mcp.aetoskia.com/dagpipe/mcp\" },\n \"mongo-ops\": { \"serverUrl\": \"https://mcp.aetoskia.com/mongo-ops/mcp\" },\n \"hexa\": { \"serverUrl\": \"https://mcp.aetoskia.com/hexa/mcp\" },\n \"doc-weaver\": { \"serverUrl\": \"https://mcp.aetoskia.com/doc-weaver/mcp\" },\n \"react-auth\": { \"serverUrl\": \"https://mcp.aetoskia.com/react-auth/mcp\" },\n \"react-openapi\": { \"serverUrl\": \"https://mcp.aetoskia.com/react-openapi/mcp\" },\n \"vet\": { \"serverUrl\": \"https://mcp.aetoskia.com/vet/mcp\" }\n }\n }\n ```\n\n---\n\n## 🤖 OpenCode\n\n`opencode.json` (project) or `~/.config/opencode/opencode.json` (global).\n\n=== \"localhost\"\n\n ```json\n {\n \"$schema\": \"https://opencode.ai/config.json\",\n \"mcp\": {\n \"standards\": { \"type\": \"remote\", \"url\": \"http://localhost:8009/mcp\", \"enabled\": true },\n \"doc-forge\": { \"type\": \"remote\", \"url\": \"http://localhost:8001/mcp\", \"enabled\": true },\n \"mail-intake\": { \"type\": \"remote\", \"url\": \"http://localhost:8002/mcp\", \"enabled\": true },\n \"omniread\": { \"type\": \"remote\", \"url\": \"http://localhost:8003/mcp\", \"enabled\": true },\n \"openapi-first\": { \"type\": \"remote\", \"url\": \"http://localhost:8004/mcp\", \"enabled\": true },\n \"py-jwt\": { \"type\": \"remote\", \"url\": \"http://localhost:8005/mcp\", \"enabled\": true },\n \"dagpipe\": { \"type\": \"remote\", \"url\": \"http://localhost:8006/mcp\", \"enabled\": true },\n \"mongo-ops\": { \"type\": \"remote\", \"url\": \"http://localhost:8007/mcp\", \"enabled\": true },\n \"hexa\": { \"type\": \"remote\", \"url\": \"http://localhost:8008/mcp\", \"enabled\": true },\n \"doc-weaver\": { \"type\": \"remote\", \"url\": \"http://localhost:8010/mcp\", \"enabled\": true },\n \"react-auth\": { \"type\": \"remote\", \"url\": \"http://localhost:8011/mcp\", \"enabled\": true },\n \"react-openapi\": { \"type\": \"remote\", \"url\": \"http://localhost:8012/mcp\", \"enabled\": true },\n \"vet\": { \"type\": \"remote\", \"url\": \"http://localhost:8013/mcp\", \"enabled\": true }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"$schema\": \"https://opencode.ai/config.json\",\n \"mcp\": {\n \"standards\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/standards/mcp\", \"enabled\": true },\n \"doc-forge\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/doc-forge/mcp\", \"enabled\": true },\n \"mail-intake\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/mail-intake/mcp\", \"enabled\": true },\n \"omniread\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/omniread/mcp\", \"enabled\": true },\n \"openapi-first\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/openapi-first/mcp\", \"enabled\": true },\n \"py-jwt\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/py-jwt/mcp\", \"enabled\": true },\n \"dagpipe\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/dagpipe/mcp\", \"enabled\": true },\n \"mongo-ops\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/mongo-ops/mcp\", \"enabled\": true },\n \"hexa\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/hexa/mcp\", \"enabled\": true },\n \"doc-weaver\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/doc-weaver/mcp\", \"enabled\": true },\n \"react-auth\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/react-auth/mcp\", \"enabled\": true },\n \"react-openapi\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/react-openapi/mcp\", \"enabled\": true },\n \"vet\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/vet/mcp\", \"enabled\": true }\n }\n }\n ```\n\nOr add them with the CLI:\n`opencode mcp add standards --url http://localhost:8009/mcp --global`.\n\n---\n\n## 🤖 PyCharm (Junie / AI Assistant)\n\n`~/.junie/mcp/mcp.json` (user scope) or `.junie/mcp/mcp.json` (project scope).\nThe same config is reachable from **Settings | Tools | MCP Server → Manual\nClient Configuration → Copy HTTP Stream Config**.\n\n=== \"localhost\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"url\": \"http://localhost:8009/mcp\" },\n \"doc-forge\": { \"url\": \"http://localhost:8001/mcp\" },\n \"mail-intake\": { \"url\": \"http://localhost:8002/mcp\" },\n \"omniread\": { \"url\": \"http://localhost:8003/mcp\" },\n \"openapi-first\": { \"url\": \"http://localhost:8004/mcp\" },\n \"py-jwt\": { \"url\": \"http://localhost:8005/mcp\" },\n \"dagpipe\": { \"url\": \"http://localhost:8006/mcp\" },\n \"mongo-ops\": { \"url\": \"http://localhost:8007/mcp\" },\n \"hexa\": { \"url\": \"http://localhost:8008/mcp\" },\n \"doc-weaver\": { \"url\": \"http://localhost:8010/mcp\" },\n \"react-auth\": { \"url\": \"http://localhost:8011/mcp\" },\n \"react-openapi\": { \"url\": \"http://localhost:8012/mcp\" },\n \"vet\": { \"url\": \"http://localhost:8013/mcp\" }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"url\": \"https://mcp.aetoskia.com/standards/mcp\" },\n \"doc-forge\": { \"url\": \"https://mcp.aetoskia.com/doc-forge/mcp\" },\n \"mail-intake\": { \"url\": \"https://mcp.aetoskia.com/mail-intake/mcp\" },\n \"omniread\": { \"url\": \"https://mcp.aetoskia.com/omniread/mcp\" },\n \"openapi-first\": { \"url\": \"https://mcp.aetoskia.com/openapi-first/mcp\" },\n \"py-jwt\": { \"url\": \"https://mcp.aetoskia.com/py-jwt/mcp\" },\n \"dagpipe\": { \"url\": \"https://mcp.aetoskia.com/dagpipe/mcp\" },\n \"mongo-ops\": { \"url\": \"https://mcp.aetoskia.com/mongo-ops/mcp\" },\n \"hexa\": { \"url\": \"https://mcp.aetoskia.com/hexa/mcp\" },\n \"doc-weaver\": { \"url\": \"https://mcp.aetoskia.com/doc-weaver/mcp\" },\n \"react-auth\": { \"url\": \"https://mcp.aetoskia.com/react-auth/mcp\" },\n \"react-openapi\": { \"url\": \"https://mcp.aetoskia.com/react-openapi/mcp\" },\n \"vet\": { \"url\": \"https://mcp.aetoskia.com/vet/mcp\" }\n }\n }\n ```\n\n---\n\n## ✅ Verify\n\n- Health check: `curl http://localhost:8000/healthz` → `ok`.\n- Claude Code: `claude mcp list` → each server … `✓ Connected`.\n- Codex: `codex mcp list` → each server … `http`.\n- OpenCode: `opencode mcp list` → each server … `connected`.\n- In-session: run `/mcp` to list the connected servers and their tools.\n\n---\n\n## 📚 Reference\n\n- [Library Registry](../lib/13_library_registry/) — the catalog each server\n mirrors, and where `find_library` resolves\n- [Start an App](03_start_an_app.md) — connect `standards`, then invoke\n `start_app`\n- [App Components](02_components.md) — the component model\n\n---\n\n## Related\n\n- [Start an App](03_start_an_app.md) · [Testing & Quality](04_development/01_testing.md) · [Library Registry](../lib/13_library_registry/)\n",
+ "docstring": "# 🔌 MCP Setup\n\nEvery repository ships its documentation as a **Model Context Protocol (MCP)**\nserver, and the `standards` registry server ties them together. The `docs`\nportal runs each one as a FastMCP **streamable-http** app bound to\n`0.0.0.0:`, exposing the endpoint at `/mcp` (see `docs/main.py` and\n`docs/config.yml`).\n\nConnect your agent to the **`standards`** server first: it answers\n`find_library`, `list_repos`, and the `start_app` prompt, so the agent reaches\nfor an internal library instead of reimplementing one (see the\n[Library Registry](../lib/13_library_registry/)).\n\n---\n\n## ▶️ Run the servers locally\n\nFrom the `docs` checkout:\n\n```bash\n.venv\\Scripts\\python.exe main.py # every server\n.venv\\Scripts\\python.exe main.py --servers standards # just the registry\n```\n\n`:8000` is the health endpoint (`GET /healthz`), **not** an MCP server.\n\n---\n\n## 🧭 Servers\n\n| Slug | MCP server | Port | localhost | mcp.aetoskia.com |\n|------|------------|------|-----------|------------------|\n| `standards` | `standards` | 8009 | `http://localhost:8009/mcp` | `https://mcp.aetoskia.com/standards/mcp` |\n| `doc-forge` | `docforge` | 8001 | `http://localhost:8001/mcp` | `https://mcp.aetoskia.com/doc-forge/mcp` |\n| `mail-intake` | `mail_intake` | 8002 | `http://localhost:8002/mcp` | `https://mcp.aetoskia.com/mail-intake/mcp` |\n| `omniread` | `omniread` | 8003 | `http://localhost:8003/mcp` | `https://mcp.aetoskia.com/omniread/mcp` |\n| `openapi-first` | `openapi_first` | 8004 | `http://localhost:8004/mcp` | `https://mcp.aetoskia.com/openapi-first/mcp` |\n| `py-jwt` | `jwtlib` | 8005 | `http://localhost:8005/mcp` | `https://mcp.aetoskia.com/py-jwt/mcp` |\n| `dagpipe` | `dagpipe` | 8006 | `http://localhost:8006/mcp` | `https://mcp.aetoskia.com/dagpipe/mcp` |\n| `mongo-ops` | `mongo_ops` | 8007 | `http://localhost:8007/mcp` | `https://mcp.aetoskia.com/mongo-ops/mcp` |\n| `hexa` | `hexa` | 8008 | `http://localhost:8008/mcp` | `https://mcp.aetoskia.com/hexa/mcp` |\n| `doc-weaver` | `doc-weaver` | 8010 | `http://localhost:8010/mcp` | `https://mcp.aetoskia.com/doc-weaver/mcp` |\n| `react-auth` | `react-auth` | 8011 | `http://localhost:8011/mcp` | `https://mcp.aetoskia.com/react-auth/mcp` |\n| `react-openapi` | `react-openapi` | 8012 | `http://localhost:8012/mcp` | `https://mcp.aetoskia.com/react-openapi/mcp` |\n\nThe hosted endpoints are served over HTTPS at `https://mcp.aetoskia.com//mcp`.\nThe tabs below are linked (`content.tabs.link`): switching one switches all of\nthem. Every snippet registers **all twelve** servers, so a single paste is\nenough.\n\n---\n\n## 🤖 Claude Code\n\nProject-scoped `.mcp.json` at the repo root. The `type` field is required — an\nentry with a `url` but no `type` is read as a stdio server and skipped.\n\n=== \"localhost\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"type\": \"http\", \"url\": \"http://localhost:8009/mcp\" },\n \"doc-forge\": { \"type\": \"http\", \"url\": \"http://localhost:8001/mcp\" },\n \"mail-intake\": { \"type\": \"http\", \"url\": \"http://localhost:8002/mcp\" },\n \"omniread\": { \"type\": \"http\", \"url\": \"http://localhost:8003/mcp\" },\n \"openapi-first\": { \"type\": \"http\", \"url\": \"http://localhost:8004/mcp\" },\n \"py-jwt\": { \"type\": \"http\", \"url\": \"http://localhost:8005/mcp\" },\n \"dagpipe\": { \"type\": \"http\", \"url\": \"http://localhost:8006/mcp\" },\n \"mongo-ops\": { \"type\": \"http\", \"url\": \"http://localhost:8007/mcp\" },\n \"hexa\": { \"type\": \"http\", \"url\": \"http://localhost:8008/mcp\" },\n \"doc-weaver\": { \"type\": \"http\", \"url\": \"http://localhost:8010/mcp\" },\n \"react-auth\": { \"type\": \"http\", \"url\": \"http://localhost:8011/mcp\" },\n \"react-openapi\": { \"type\": \"http\", \"url\": \"http://localhost:8012/mcp\" }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/standards/mcp\" },\n \"doc-forge\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/doc-forge/mcp\" },\n \"mail-intake\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/mail-intake/mcp\" },\n \"omniread\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/omniread/mcp\" },\n \"openapi-first\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/openapi-first/mcp\" },\n \"py-jwt\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/py-jwt/mcp\" },\n \"dagpipe\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/dagpipe/mcp\" },\n \"mongo-ops\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/mongo-ops/mcp\" },\n \"hexa\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/hexa/mcp\" },\n \"doc-weaver\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/doc-weaver/mcp\" },\n \"react-auth\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/react-auth/mcp\" },\n \"react-openapi\": { \"type\": \"http\", \"url\": \"https://mcp.aetoskia.com/react-openapi/mcp\" }\n }\n }\n ```\n\nOr add them with the CLI:\n`claude mcp add --transport http standards http://localhost:8009/mcp`.\n\n---\n\n## 🤖 Codex\n\n`~/.codex/config.toml` (or a project-scoped `.codex/config.toml`).\n\n=== \"localhost\"\n\n ```toml\n [mcp_servers.standards]\n url = \"http://localhost:8009/mcp\"\n\n [mcp_servers.doc-forge]\n url = \"http://localhost:8001/mcp\"\n\n [mcp_servers.mail-intake]\n url = \"http://localhost:8002/mcp\"\n\n [mcp_servers.omniread]\n url = \"http://localhost:8003/mcp\"\n\n [mcp_servers.openapi-first]\n url = \"http://localhost:8004/mcp\"\n\n [mcp_servers.py-jwt]\n url = \"http://localhost:8005/mcp\"\n\n [mcp_servers.dagpipe]\n url = \"http://localhost:8006/mcp\"\n\n [mcp_servers.mongo-ops]\n url = \"http://localhost:8007/mcp\"\n\n [mcp_servers.hexa]\n url = \"http://localhost:8008/mcp\"\n\n [mcp_servers.doc-weaver]\n url = \"http://localhost:8010/mcp\"\n\n [mcp_servers.react-auth]\n url = \"http://localhost:8011/mcp\"\n\n [mcp_servers.react-openapi]\n url = \"http://localhost:8012/mcp\"\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```toml\n [mcp_servers.standards]\n url = \"https://mcp.aetoskia.com/standards/mcp\"\n\n [mcp_servers.doc-forge]\n url = \"https://mcp.aetoskia.com/doc-forge/mcp\"\n\n [mcp_servers.mail-intake]\n url = \"https://mcp.aetoskia.com/mail-intake/mcp\"\n\n [mcp_servers.omniread]\n url = \"https://mcp.aetoskia.com/omniread/mcp\"\n\n [mcp_servers.openapi-first]\n url = \"https://mcp.aetoskia.com/openapi-first/mcp\"\n\n [mcp_servers.py-jwt]\n url = \"https://mcp.aetoskia.com/py-jwt/mcp\"\n\n [mcp_servers.dagpipe]\n url = \"https://mcp.aetoskia.com/dagpipe/mcp\"\n\n [mcp_servers.mongo-ops]\n url = \"https://mcp.aetoskia.com/mongo-ops/mcp\"\n\n [mcp_servers.hexa]\n url = \"https://mcp.aetoskia.com/hexa/mcp\"\n\n [mcp_servers.doc-weaver]\n url = \"https://mcp.aetoskia.com/doc-weaver/mcp\"\n\n [mcp_servers.react-auth]\n url = \"https://mcp.aetoskia.com/react-auth/mcp\"\n\n [mcp_servers.react-openapi]\n url = \"https://mcp.aetoskia.com/react-openapi/mcp\"\n ```\n\nOr add them with the CLI:\n`codex mcp add standards --url http://localhost:8009/mcp`.\n\n---\n\n## 🤖 Antigravity\n\n`~/.gemini/antigravity/mcp_config.json` (IDE) or\n`~/.gemini/config/mcp_config.json` (CLI). Antigravity uses **`serverUrl`**, not\n`url`, for HTTP endpoints.\n\n=== \"localhost\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"serverUrl\": \"http://localhost:8009/mcp\" },\n \"doc-forge\": { \"serverUrl\": \"http://localhost:8001/mcp\" },\n \"mail-intake\": { \"serverUrl\": \"http://localhost:8002/mcp\" },\n \"omniread\": { \"serverUrl\": \"http://localhost:8003/mcp\" },\n \"openapi-first\": { \"serverUrl\": \"http://localhost:8004/mcp\" },\n \"py-jwt\": { \"serverUrl\": \"http://localhost:8005/mcp\" },\n \"dagpipe\": { \"serverUrl\": \"http://localhost:8006/mcp\" },\n \"mongo-ops\": { \"serverUrl\": \"http://localhost:8007/mcp\" },\n \"hexa\": { \"serverUrl\": \"http://localhost:8008/mcp\" },\n \"doc-weaver\": { \"serverUrl\": \"http://localhost:8010/mcp\" },\n \"react-auth\": { \"serverUrl\": \"http://localhost:8011/mcp\" },\n \"react-openapi\": { \"serverUrl\": \"http://localhost:8012/mcp\" }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"serverUrl\": \"https://mcp.aetoskia.com/standards/mcp\" },\n \"doc-forge\": { \"serverUrl\": \"https://mcp.aetoskia.com/doc-forge/mcp\" },\n \"mail-intake\": { \"serverUrl\": \"https://mcp.aetoskia.com/mail-intake/mcp\" },\n \"omniread\": { \"serverUrl\": \"https://mcp.aetoskia.com/omniread/mcp\" },\n \"openapi-first\": { \"serverUrl\": \"https://mcp.aetoskia.com/openapi-first/mcp\" },\n \"py-jwt\": { \"serverUrl\": \"https://mcp.aetoskia.com/py-jwt/mcp\" },\n \"dagpipe\": { \"serverUrl\": \"https://mcp.aetoskia.com/dagpipe/mcp\" },\n \"mongo-ops\": { \"serverUrl\": \"https://mcp.aetoskia.com/mongo-ops/mcp\" },\n \"hexa\": { \"serverUrl\": \"https://mcp.aetoskia.com/hexa/mcp\" },\n \"doc-weaver\": { \"serverUrl\": \"https://mcp.aetoskia.com/doc-weaver/mcp\" },\n \"react-auth\": { \"serverUrl\": \"https://mcp.aetoskia.com/react-auth/mcp\" },\n \"react-openapi\": { \"serverUrl\": \"https://mcp.aetoskia.com/react-openapi/mcp\" }\n }\n }\n ```\n\n---\n\n## 🤖 OpenCode\n\n`opencode.json` (project) or `~/.config/opencode/opencode.json` (global).\n\n=== \"localhost\"\n\n ```json\n {\n \"$schema\": \"https://opencode.ai/config.json\",\n \"mcp\": {\n \"standards\": { \"type\": \"remote\", \"url\": \"http://localhost:8009/mcp\", \"enabled\": true },\n \"doc-forge\": { \"type\": \"remote\", \"url\": \"http://localhost:8001/mcp\", \"enabled\": true },\n \"mail-intake\": { \"type\": \"remote\", \"url\": \"http://localhost:8002/mcp\", \"enabled\": true },\n \"omniread\": { \"type\": \"remote\", \"url\": \"http://localhost:8003/mcp\", \"enabled\": true },\n \"openapi-first\": { \"type\": \"remote\", \"url\": \"http://localhost:8004/mcp\", \"enabled\": true },\n \"py-jwt\": { \"type\": \"remote\", \"url\": \"http://localhost:8005/mcp\", \"enabled\": true },\n \"dagpipe\": { \"type\": \"remote\", \"url\": \"http://localhost:8006/mcp\", \"enabled\": true },\n \"mongo-ops\": { \"type\": \"remote\", \"url\": \"http://localhost:8007/mcp\", \"enabled\": true },\n \"hexa\": { \"type\": \"remote\", \"url\": \"http://localhost:8008/mcp\", \"enabled\": true },\n \"doc-weaver\": { \"type\": \"remote\", \"url\": \"http://localhost:8010/mcp\", \"enabled\": true },\n \"react-auth\": { \"type\": \"remote\", \"url\": \"http://localhost:8011/mcp\", \"enabled\": true },\n \"react-openapi\": { \"type\": \"remote\", \"url\": \"http://localhost:8012/mcp\", \"enabled\": true }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"$schema\": \"https://opencode.ai/config.json\",\n \"mcp\": {\n \"standards\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/standards/mcp\", \"enabled\": true },\n \"doc-forge\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/doc-forge/mcp\", \"enabled\": true },\n \"mail-intake\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/mail-intake/mcp\", \"enabled\": true },\n \"omniread\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/omniread/mcp\", \"enabled\": true },\n \"openapi-first\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/openapi-first/mcp\", \"enabled\": true },\n \"py-jwt\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/py-jwt/mcp\", \"enabled\": true },\n \"dagpipe\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/dagpipe/mcp\", \"enabled\": true },\n \"mongo-ops\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/mongo-ops/mcp\", \"enabled\": true },\n \"hexa\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/hexa/mcp\", \"enabled\": true },\n \"doc-weaver\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/doc-weaver/mcp\", \"enabled\": true },\n \"react-auth\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/react-auth/mcp\", \"enabled\": true },\n \"react-openapi\": { \"type\": \"remote\", \"url\": \"https://mcp.aetoskia.com/react-openapi/mcp\", \"enabled\": true }\n }\n }\n ```\n\nOr add them with the CLI:\n`opencode mcp add standards --url http://localhost:8009/mcp --global`.\n\n---\n\n## 🤖 PyCharm (Junie / AI Assistant)\n\n`~/.junie/mcp/mcp.json` (user scope) or `.junie/mcp/mcp.json` (project scope).\nThe same config is reachable from **Settings | Tools | MCP Server → Manual\nClient Configuration → Copy HTTP Stream Config**.\n\n=== \"localhost\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"url\": \"http://localhost:8009/mcp\" },\n \"doc-forge\": { \"url\": \"http://localhost:8001/mcp\" },\n \"mail-intake\": { \"url\": \"http://localhost:8002/mcp\" },\n \"omniread\": { \"url\": \"http://localhost:8003/mcp\" },\n \"openapi-first\": { \"url\": \"http://localhost:8004/mcp\" },\n \"py-jwt\": { \"url\": \"http://localhost:8005/mcp\" },\n \"dagpipe\": { \"url\": \"http://localhost:8006/mcp\" },\n \"mongo-ops\": { \"url\": \"http://localhost:8007/mcp\" },\n \"hexa\": { \"url\": \"http://localhost:8008/mcp\" },\n \"doc-weaver\": { \"url\": \"http://localhost:8010/mcp\" },\n \"react-auth\": { \"url\": \"http://localhost:8011/mcp\" },\n \"react-openapi\": { \"url\": \"http://localhost:8012/mcp\" }\n }\n }\n ```\n\n=== \"mcp.aetoskia.com\"\n\n ```json\n {\n \"mcpServers\": {\n \"standards\": { \"url\": \"https://mcp.aetoskia.com/standards/mcp\" },\n \"doc-forge\": { \"url\": \"https://mcp.aetoskia.com/doc-forge/mcp\" },\n \"mail-intake\": { \"url\": \"https://mcp.aetoskia.com/mail-intake/mcp\" },\n \"omniread\": { \"url\": \"https://mcp.aetoskia.com/omniread/mcp\" },\n \"openapi-first\": { \"url\": \"https://mcp.aetoskia.com/openapi-first/mcp\" },\n \"py-jwt\": { \"url\": \"https://mcp.aetoskia.com/py-jwt/mcp\" },\n \"dagpipe\": { \"url\": \"https://mcp.aetoskia.com/dagpipe/mcp\" },\n \"mongo-ops\": { \"url\": \"https://mcp.aetoskia.com/mongo-ops/mcp\" },\n \"hexa\": { \"url\": \"https://mcp.aetoskia.com/hexa/mcp\" },\n \"doc-weaver\": { \"url\": \"https://mcp.aetoskia.com/doc-weaver/mcp\" },\n \"react-auth\": { \"url\": \"https://mcp.aetoskia.com/react-auth/mcp\" },\n \"react-openapi\": { \"url\": \"https://mcp.aetoskia.com/react-openapi/mcp\" }\n }\n }\n ```\n\n---\n\n## ✅ Verify\n\n- Health check: `curl http://localhost:8000/healthz` → `ok`.\n- Claude Code: `claude mcp list` → each server … `✓ Connected`.\n- Codex: `codex mcp list` → each server … `http`.\n- OpenCode: `opencode mcp list` → each server … `connected`.\n- In-session: run `/mcp` to list the connected servers and their tools.\n\n---\n\n## 📚 Reference\n\n- [Library Registry](../lib/13_library_registry/) — the catalog each server\n mirrors, and where `find_library` resolves\n- [Start an App](03_start_an_app.md) — connect `standards`, then invoke\n `start_app`\n- [App Components](02_components.md) — the component model\n\n---\n\n## Related\n\n- [Start an App](03_start_an_app.md) · [Testing & Quality](04_development/01_testing.md) · [Library Registry](../lib/13_library_registry/)\n",
"objects": {}
}
}
\ No newline at end of file
diff --git a/standards/src/mkdocs.wiki.yml b/standards/src/mkdocs.wiki.yml
index 5b15b51..39a6e77 100644
--- a/standards/src/mkdocs.wiki.yml
+++ b/standards/src/mkdocs.wiki.yml
@@ -69,9 +69,8 @@ nav:
- Home: index.md
- Overview: 01_overview.md
- App Components: 02_components.md
- - Start an App: 03_start_an_app.md
- - UI + API Reference: 06_ui_api_reference.md
- - MCP:
+ - Start an App: 03_start_an_app.md
+ - MCP:
- Setup: 05_mcp_setup.md
- Testing & Quality: 04_development/01_testing.md
- Releasing & Versioning: 04_development/02_releasing.md
\ No newline at end of file
diff --git a/standards/src/wiki/05_mcp_setup.md b/standards/src/wiki/05_mcp_setup.md
index acb3294..4e50a0c 100644
--- a/standards/src/wiki/05_mcp_setup.md
+++ b/standards/src/wiki/05_mcp_setup.md
@@ -42,11 +42,10 @@ From the `docs` checkout:
| `doc-weaver` | `doc-weaver` | 8010 | `http://localhost:8010/mcp` | `https://mcp.aetoskia.com/doc-weaver/mcp` |
| `react-auth` | `react-auth` | 8011 | `http://localhost:8011/mcp` | `https://mcp.aetoskia.com/react-auth/mcp` |
| `react-openapi` | `react-openapi` | 8012 | `http://localhost:8012/mcp` | `https://mcp.aetoskia.com/react-openapi/mcp` |
-| `vet` | `vet` | 8013 | `http://localhost:8013/mcp` | `https://mcp.aetoskia.com/vet/mcp` |
The hosted endpoints are served over HTTPS at `https://mcp.aetoskia.com//mcp`.
The tabs below are linked (`content.tabs.link`): switching one switches all of
-them. Every snippet registers **all thirteen** servers, so a single paste is
+them. Every snippet registers **all twelve** servers, so a single paste is
enough.
---
@@ -72,8 +71,7 @@ entry with a `url` but no `type` is read as a stdio server and skipped.
"hexa": { "type": "http", "url": "http://localhost:8008/mcp" },
"doc-weaver": { "type": "http", "url": "http://localhost:8010/mcp" },
"react-auth": { "type": "http", "url": "http://localhost:8011/mcp" },
- "react-openapi": { "type": "http", "url": "http://localhost:8012/mcp" },
- "vet": { "type": "http", "url": "http://localhost:8013/mcp" }
+ "react-openapi": { "type": "http", "url": "http://localhost:8012/mcp" }
}
}
```
@@ -94,8 +92,7 @@ entry with a `url` but no `type` is read as a stdio server and skipped.
"hexa": { "type": "http", "url": "https://mcp.aetoskia.com/hexa/mcp" },
"doc-weaver": { "type": "http", "url": "https://mcp.aetoskia.com/doc-weaver/mcp" },
"react-auth": { "type": "http", "url": "https://mcp.aetoskia.com/react-auth/mcp" },
- "react-openapi": { "type": "http", "url": "https://mcp.aetoskia.com/react-openapi/mcp" },
- "vet": { "type": "http", "url": "https://mcp.aetoskia.com/vet/mcp" }
+ "react-openapi": { "type": "http", "url": "https://mcp.aetoskia.com/react-openapi/mcp" }
}
}
```
@@ -147,9 +144,6 @@ Or add them with the CLI:
[mcp_servers.react-openapi]
url = "http://localhost:8012/mcp"
-
- [mcp_servers.vet]
- url = "http://localhost:8013/mcp"
```
=== "mcp.aetoskia.com"
@@ -190,9 +184,6 @@ Or add them with the CLI:
[mcp_servers.react-openapi]
url = "https://mcp.aetoskia.com/react-openapi/mcp"
-
- [mcp_servers.vet]
- url = "https://mcp.aetoskia.com/vet/mcp"
```
Or add them with the CLI:
@@ -222,8 +213,7 @@ Or add them with the CLI:
"hexa": { "serverUrl": "http://localhost:8008/mcp" },
"doc-weaver": { "serverUrl": "http://localhost:8010/mcp" },
"react-auth": { "serverUrl": "http://localhost:8011/mcp" },
- "react-openapi": { "serverUrl": "http://localhost:8012/mcp" },
- "vet": { "serverUrl": "http://localhost:8013/mcp" }
+ "react-openapi": { "serverUrl": "http://localhost:8012/mcp" }
}
}
```
@@ -244,8 +234,7 @@ Or add them with the CLI:
"hexa": { "serverUrl": "https://mcp.aetoskia.com/hexa/mcp" },
"doc-weaver": { "serverUrl": "https://mcp.aetoskia.com/doc-weaver/mcp" },
"react-auth": { "serverUrl": "https://mcp.aetoskia.com/react-auth/mcp" },
- "react-openapi": { "serverUrl": "https://mcp.aetoskia.com/react-openapi/mcp" },
- "vet": { "serverUrl": "https://mcp.aetoskia.com/vet/mcp" }
+ "react-openapi": { "serverUrl": "https://mcp.aetoskia.com/react-openapi/mcp" }
}
}
```
@@ -273,8 +262,7 @@ Or add them with the CLI:
"hexa": { "type": "remote", "url": "http://localhost:8008/mcp", "enabled": true },
"doc-weaver": { "type": "remote", "url": "http://localhost:8010/mcp", "enabled": true },
"react-auth": { "type": "remote", "url": "http://localhost:8011/mcp", "enabled": true },
- "react-openapi": { "type": "remote", "url": "http://localhost:8012/mcp", "enabled": true },
- "vet": { "type": "remote", "url": "http://localhost:8013/mcp", "enabled": true }
+ "react-openapi": { "type": "remote", "url": "http://localhost:8012/mcp", "enabled": true }
}
}
```
@@ -296,8 +284,7 @@ Or add them with the CLI:
"hexa": { "type": "remote", "url": "https://mcp.aetoskia.com/hexa/mcp", "enabled": true },
"doc-weaver": { "type": "remote", "url": "https://mcp.aetoskia.com/doc-weaver/mcp", "enabled": true },
"react-auth": { "type": "remote", "url": "https://mcp.aetoskia.com/react-auth/mcp", "enabled": true },
- "react-openapi": { "type": "remote", "url": "https://mcp.aetoskia.com/react-openapi/mcp", "enabled": true },
- "vet": { "type": "remote", "url": "https://mcp.aetoskia.com/vet/mcp", "enabled": true }
+ "react-openapi": { "type": "remote", "url": "https://mcp.aetoskia.com/react-openapi/mcp", "enabled": true }
}
}
```
@@ -329,8 +316,7 @@ Client Configuration → Copy HTTP Stream Config**.
"hexa": { "url": "http://localhost:8008/mcp" },
"doc-weaver": { "url": "http://localhost:8010/mcp" },
"react-auth": { "url": "http://localhost:8011/mcp" },
- "react-openapi": { "url": "http://localhost:8012/mcp" },
- "vet": { "url": "http://localhost:8013/mcp" }
+ "react-openapi": { "url": "http://localhost:8012/mcp" }
}
}
```
@@ -351,8 +337,7 @@ Client Configuration → Copy HTTP Stream Config**.
"hexa": { "url": "https://mcp.aetoskia.com/hexa/mcp" },
"doc-weaver": { "url": "https://mcp.aetoskia.com/doc-weaver/mcp" },
"react-auth": { "url": "https://mcp.aetoskia.com/react-auth/mcp" },
- "react-openapi": { "url": "https://mcp.aetoskia.com/react-openapi/mcp" },
- "vet": { "url": "https://mcp.aetoskia.com/vet/mcp" }
+ "react-openapi": { "url": "https://mcp.aetoskia.com/react-openapi/mcp" }
}
}
```
diff --git a/standards/src/wiki/06_ui_api_reference.md b/standards/src/wiki/06_ui_api_reference.md
deleted file mode 100644
index f87afce..0000000
--- a/standards/src/wiki/06_ui_api_reference.md
+++ /dev/null
@@ -1,115 +0,0 @@
-# UI + API Reference
-
-The vet application is the reference implementation of the **UI + API
-pattern**: a spec-first backend (`react/vet/server`) paired with a
-react-openapi frontend (`react/vet`) that renders itself directly from the
-backend's OpenAPI document. Everything about the pairing — auth, persistence,
-uploads, SSE — is driven from `openapi.yaml`.
-
----
-
-## Architecture
-
-```
-react/vet/ # frontend (vite, react-openapi, react-auth)
- src/
- openapi-config.ts # reads VITE_API_BASE_URL + VITE_AUTH_BASE_URL
- .env # VITE_API_BASE_URL=http://localhost:8020
-
-react/vet/server/ # backend (OpenAPIFirstApp, port 8020)
- openapi.yaml # single source of truth (24 operations)
- domain/ # parents, vets, treatments, pets, appointments
- infra/db/repos/ # IntIdMemoryRepository / IntIdMongoRepository
- interfaces/ # one route module per resource + system (health)
-```
-
-The frontend's `specConfiguration` passes `specUrl` and `baseApiUrl` derived
-from `VITE_API_BASE_URL`; `react-openapi` fetches `${base}/openapi.json` and
-builds the admin UI from the `x-` extensions in the spec.
-
----
-
-## Auth (bearer JWT)
-
-`openapi.yaml` declares a single `bearerAuth` security scheme
-(`type: http`, `bearerFormat: JWT`) with `x-server-url: "{AUTH_SERVER}"` and
-`x-introspect-path: "/introspect"`. `openapi_first` resolves `{AUTH_SERVER}`
-from the environment at app build time and generates a `Depends` that calls
-the auth-server's `/introspect` endpoint per request. No hand-written auth
-code exists in the backend.
-
-The frontend uses `react-auth`'s `AuthProvider` and `AuthPage` gate; the API
-client attaches `Authorization: Bearer ` via
-`specConfiguration.getToken = () => tokenStore.get()`.
-
----
-
-## Persistence (dual backend)
-
-`REPO_BACKEND` selects the repository flavor:
-
-| Value | Adapter | Notes |
-| ------ | ---------------------------- | ------------------------------------------ |
-| `memory` (default) | `IntIdMemoryRepository` | Process-local dict + counter; zero deps |
-| `mongo` | `IntIdMongoRepository` | Motor via `MongoConnectionManager`; counter via `_counters` collection |
-
-Both adapters expose the same duck-type (`create`, `get_by_id`, `get_many`,
-`update`, `patch`, `delete`) consumed by the hexa-wired
-`CrudsServiceAdaptor` services, so domain code and route handlers are
-backend-agnostic. **Integer ids** are used end-to-end (the wire contract
-declares `type: integer` primary keys); this is a documented deviation from
-mongo-ops' default `ObjectId` allocation.
-
----
-
-## Resource modelling
-
-Every resource follows the same slice:
-
-- `domain//models.py` — plain pydantic models (`extra: "ignore"`)
-- `domain//ports/db.py` — `XDocument(X, BaseDocument)` + `create_x_repo()`
-- `domain//ports/service.py` — `XCrudsService(CrudsServiceAdaptor)` + `get_x_service(repo)`
-- `interfaces//routes.py` — handlers named after operationIds
-
-Foreign keys are embedded objects on the wire (e.g. `Appointment.pet` is a
-full `Pet`). Create/update handlers accept either a bare id or an expanded
-object and normalize through `expand_ref` / `expand_ref_list` before
-persisting. Unresolvable references return `422`.
-
----
-
-## Non-CRUD surface
-
-- **File upload** — `POST /pets/{id}/photo` (multipart) writes to
- `UPLOADS_DIR` and stores the path on the pet document.
-- **SSE** — `GET /pets/{id}/actions` returns `text/event-stream` with random
- species-scoped behaviors (see `infra/adapters/actions_stream.py`).
-- **Health** — `GET /health` is public (`security: []`) and used by the
- Docker healthcheck.
-
----
-
-## Running
-
-```bash
-cd react/vet/server
-python -m venv .venv && .venv\Scripts\pip install -e .[dev]
-AUTH_SERVER=https://auth.aetoskia.com REPO_BACKEND=memory .venv\Scripts\uvicorn main:app --port 8020
-```
-
-The frontend (`react/vet`) reads `VITE_API_BASE_URL` from `.env` and proxies
-nothing — CORS is open (`allow_origins=["*"]`) in `main.py`.
-
----
-
-## What the standards say
-
-The backend follows the [Start an App](03_start_an_app.md) blueprint
-(composition root, hexa wiring, `CrudsServiceAdaptor` services, spec-first
-routing via `openapi_first`). The deviations are intentional and documented:
-
-- **No worker/pipelines** — no background jobs for this app.
-- **Integer-id repositories** — required by the wire contract; replaces the
- ObjectId-based `mongo_ops.BaseRepository`.
-- **Both persistence flavors** — memory (default, zero-dep) and Mongo
- (production) selected via `REPO_BACKEND`.
diff --git a/standards/wiki/05_mcp_setup/index.html b/standards/wiki/05_mcp_setup/index.html
index fc8cf97..8bb236b 100644
--- a/standards/wiki/05_mcp_setup/index.html
+++ b/standards/wiki/05_mcp_setup/index.html
@@ -844,18 +844,11 @@ for an internal library instead of reimplementing one (see the
http://localhost:8012/mcp
https://mcp.aetoskia.com/react-openapi/mcp
-
-
vet
-
vet
-
8013
-
http://localhost:8013/mcp
-
https://mcp.aetoskia.com/vet/mcp
-
The hosted endpoints are served over HTTPS at https://mcp.aetoskia.com/<slug>/mcp.
The tabs below are linked (content.tabs.link): switching one switches all of
-them. Every snippet registers all thirteen servers, so a single paste is
+them. Every snippet registers all twelve servers, so a single paste is
enough.
🤖 Claude Code
@@ -879,8 +872,7 @@ entry with a url but no type is read as a stdio server
131415
-16
-17
{"mcpServers":{"standards":{"type":"http","url":"http://localhost:8009/mcp"},"doc-forge":{"type":"http","url":"http://localhost:8001/mcp"},
@@ -893,10 +885,9 @@ entry with a url but no type is read as a stdio server
"hexa":{"type":"http","url":"http://localhost:8008/mcp"},"doc-weaver":{"type":"http","url":"http://localhost:8010/mcp"},"react-auth":{"type":"http","url":"http://localhost:8011/mcp"},
-"react-openapi":{"type":"http","url":"http://localhost:8012/mcp"},
-"vet":{"type":"http","url":"http://localhost:8013/mcp"}
-}
-}
+"react-openapi":{"type":"http","url":"http://localhost:8012/mcp"}
+}
+}
@@ -915,8 +906,7 @@ entry with a url but no type is read as a stdio server
131415
-16
-17
{"mcpServers":{"standards":{"type":"http","url":"https://mcp.aetoskia.com/standards/mcp"},"doc-forge":{"type":"http","url":"https://mcp.aetoskia.com/doc-forge/mcp"},
@@ -929,10 +919,9 @@ entry with a url but no type is read as a stdio server
"hexa":{"type":"http","url":"https://mcp.aetoskia.com/hexa/mcp"},"doc-weaver":{"type":"http","url":"https://mcp.aetoskia.com/doc-weaver/mcp"},"react-auth":{"type":"http","url":"https://mcp.aetoskia.com/react-auth/mcp"},
-"react-openapi":{"type":"http","url":"https://mcp.aetoskia.com/react-openapi/mcp"},
-"vet":{"type":"http","url":"https://mcp.aetoskia.com/vet/mcp"}
-}
-}
+"react-openapi":{"type":"http","url":"https://mcp.aetoskia.com/react-openapi/mcp"}
+}
+}
@@ -979,10 +968,7 @@ entry with a url but no type is read as a stdio server
323334
-35
-36
-37
-38
[mcp_servers.standards]url="http://localhost:8009/mcp"[mcp_servers.doc-forge]
@@ -1017,9 +1003,6 @@ entry with a url but no type is read as a stdio server
[mcp_servers.react-openapi]url="http://localhost:8012/mcp"
-
-[mcp_servers.vet]
-url="http://localhost:8013/mcp"
@@ -1057,10 +1040,7 @@ entry with a url but no type is read as a stdio server
323334
-35
-36
-37
-38
[mcp_servers.standards]url="https://mcp.aetoskia.com/standards/mcp"[mcp_servers.doc-forge]
@@ -1095,9 +1075,6 @@ entry with a url but no type is read as a stdio server
[mcp_servers.react-openapi]url="https://mcp.aetoskia.com/react-openapi/mcp"
-
-[mcp_servers.vet]
-url="https://mcp.aetoskia.com/vet/mcp"
@@ -1127,8 +1104,7 @@ entry with a url but no type is read as a stdio server
131415
-16
-17
{"mcpServers":{"standards":{"serverUrl":"http://localhost:8009/mcp"},"doc-forge":{"serverUrl":"http://localhost:8001/mcp"},
@@ -1141,10 +1117,9 @@ entry with a url but no type is read as a stdio server
"hexa":{"serverUrl":"http://localhost:8008/mcp"},"doc-weaver":{"serverUrl":"http://localhost:8010/mcp"},"react-auth":{"serverUrl":"http://localhost:8011/mcp"},
-"react-openapi":{"serverUrl":"http://localhost:8012/mcp"},
-"vet":{"serverUrl":"http://localhost:8013/mcp"}
-}
-}
+"react-openapi":{"serverUrl":"http://localhost:8012/mcp"}
+}
+}
@@ -1163,8 +1138,7 @@ entry with a url but no type is read as a stdio server
131415
-16
-17
{"mcpServers":{"standards":{"serverUrl":"https://mcp.aetoskia.com/standards/mcp"},"doc-forge":{"serverUrl":"https://mcp.aetoskia.com/doc-forge/mcp"},
@@ -1177,10 +1151,9 @@ entry with a url but no type is read as a stdio server
"hexa":{"serverUrl":"https://mcp.aetoskia.com/hexa/mcp"},"doc-weaver":{"serverUrl":"https://mcp.aetoskia.com/doc-weaver/mcp"},"react-auth":{"serverUrl":"https://mcp.aetoskia.com/react-auth/mcp"},
-"react-openapi":{"serverUrl":"https://mcp.aetoskia.com/react-openapi/mcp"},
-"vet":{"serverUrl":"https://mcp.aetoskia.com/vet/mcp"}
-}
-}
+"react-openapi":{"serverUrl":"https://mcp.aetoskia.com/react-openapi/mcp"}
+}
+}
@@ -1207,8 +1180,7 @@ entry with a url but no type is read as a stdio server
141516
-17
-18
{"$schema":"https://opencode.ai/config.json","mcp":{"standards":{"type":"remote","url":"http://localhost:8009/mcp","enabled":true},
@@ -1222,10 +1194,9 @@ entry with a url but no type is read as a stdio server
"hexa":{"type":"remote","url":"http://localhost:8008/mcp","enabled":true},"doc-weaver":{"type":"remote","url":"http://localhost:8010/mcp","enabled":true},"react-auth":{"type":"remote","url":"http://localhost:8011/mcp","enabled":true},
-"react-openapi":{"type":"remote","url":"http://localhost:8012/mcp","enabled":true},
-"vet":{"type":"remote","url":"http://localhost:8013/mcp","enabled":true}
-}
-}
+"react-openapi":{"type":"remote","url":"http://localhost:8012/mcp","enabled":true}
+}
+}
@@ -1245,8 +1216,7 @@ entry with a url but no type is read as a stdio server
141516
-17
-18
{"$schema":"https://opencode.ai/config.json","mcp":{"standards":{"type":"remote","url":"https://mcp.aetoskia.com/standards/mcp","enabled":true},
@@ -1260,10 +1230,9 @@ entry with a url but no type is read as a stdio server
"hexa":{"type":"remote","url":"https://mcp.aetoskia.com/hexa/mcp","enabled":true},"doc-weaver":{"type":"remote","url":"https://mcp.aetoskia.com/doc-weaver/mcp","enabled":true},"react-auth":{"type":"remote","url":"https://mcp.aetoskia.com/react-auth/mcp","enabled":true},
-"react-openapi":{"type":"remote","url":"https://mcp.aetoskia.com/react-openapi/mcp","enabled":true},
-"vet":{"type":"remote","url":"https://mcp.aetoskia.com/vet/mcp","enabled":true}
-}
-}
+"react-openapi":{"type":"remote","url":"https://mcp.aetoskia.com/react-openapi/mcp","enabled":true}
+}
+}
One reusable architecture for every backend application in the Aetos ecosystem: a spec-first HTTP edge, a framework-free domain core, a pluggable infrastructure layer, and a single composition root.
Fill in two axes only and a new app is done: your resource model, and (if needed) your pipeline family. The blueprint itself never names a business domain \u2014 expenses, health metrics, inventory, and analytics are all the same shape underneath.
Doc model: this wiki is written for humans \u2014 how-to guides, the build recipe, and quality gates. The enforceable rules (layering, composition root, CRUD template, pipeline scaffold) and the library registry live in the reference pages, mirrored by the machine-readable standards MCP bundle. Query find_library before writing any infrastructure.
\ud83d\udd0c Single composition root \u2014 every dependency is wired in exactly one place
\ud83e\udde9 Generic, not app-specific \u2014 reusable rules versus two authoring axes
\ud83d\udcda Library-backed \u2014 DB, DI, pipelines, ingestion, auth, and docs come from internal libraries, never hand-written
\ud83d\udea6 Explicit state machines \u2014 durable job statuses, progress, and human-in-the-loop pauses
"},{"location":"#documentation-structure","title":"\ud83d\udcc1 Documentation Structure","text":"Section Description Overview Philosophy, the one-way dependency rule, architecture App Components The component model and how the pieces wire together Start an App The entry point: gather the two axes, then build Testing & Quality Test layout and acceptance gates
The blueprint is a hexagonal (ports & adapters) application with a spec-first web edge and a single composition root. It favors explicit wiring over implicit magic and existing internal libraries over hand-written infrastructure. Start here to build the mental model; the enforceable details live in the reference pages.
The rule holds at every edge: code in an outer ring may depend on a ring closer to the core, never the reverse. domain/ never imports a web framework; only its ports/ seam reaches into infra/ to bind adapters.
Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter binding, performed by the composition root at startup.
"},{"location":"01_overview/#the-one-way-dependency-rule","title":"\ud83d\udd04 The one-way dependency rule","text":"Ring May import Never imports interfaces/domain, framework glue (glue only) business logic, infra directly domain/ its own models + its ports/ seam (which binds infra/libraries) web frameworks, interfaces/infra/domain ports, drivers, libraries \u2014 init.py everything (the wiring point) \u2014"},{"location":"01_overview/#core-principles","title":"\ud83e\udded Core principles","text":"
One-way dependencies. interfaces -> domain -> infra is allowed; the reverse is forbidden.
Ports in the domain, adapters in infra. A service depends on a port; the port's factory binds the concrete adapter, and the composition root calls those factories.
One composition root. Nothing constructs its own dependencies. All wiring lives in one module (init.py) exposed as a flat resource registry.
Spec-first. The HTTP contract is declared in openapi.yaml; route handlers are looked up by operationId. There are no decorator routers.
Reuse the libraries. Orchestration, DI, Mongo repositories, ingestion, auth, and docs come from internal libraries \u2014 see the Library Registry.
Explicit state machines. Long-running work moves through explicit, durable statuses and can pause for human input.
What ships in every standard app, and how the pieces relate. This is the blueprint's component model \u2014 templates/app/ is a runnable instance of it.
The blueprint has two orthogonal views: runtime flow (who calls whom) and composition-root wiring (who constructs whom). Keeping them separate makes the layering visible instead of hiding it behind a hub.
Legend \u2014 init.py is the only place that imports concrete adapters. The ports/ factory seams bind infra; the API reads resources from app.state.db_resources, the worker calls init() directly.
"},{"location":"02_components/#component-roles","title":"\ud83d\udce6 Component roles","text":""},{"location":"02_components/#1-interfaces-the-edge","title":"1. interfaces/ \u2014 the edge","text":"
Thin handlers bound to openapi.yamloperationIds. They parse the request, call a service, and shape the response. No business rules, no I/O.
interfaces/openapi.py re-exports every route module so the OpenAPI-first app can resolve handlers.
Routes may mount the cache decorator on read endpoints.
"},{"location":"02_components/#2-domain-the-core","title":"2. domain/ \u2014 the core","text":"
Models, ports, and services (use cases). No framework imports. The ports/ package is the one seam allowed to reach into infra/: each port module declares the document shape and a factory that binds the adapter.
Extraction and enrichment families, each a deterministic step chain (abc.py / impl.py / pipeline.yaml / resources/). Roots are registered in the pattern registry and driven either by a worker job or a CLI.
"},{"location":"02_components/#5-workerpy-the-job-worker-optional","title":"5. worker.py \u2014 the job worker (optional)","text":"
A separate process that polls durable job collections, runs pipeline roots, and publishes progress to the progress bus. It shares the same composition root as the API.
"},{"location":"02_components/#6-initpy-the-composition-root","title":"6. init.py \u2014 the composition root","text":"
The only place anything is constructed: leaf repositories \u2192 dependent repositories \u2192 services \u2192 adapters. Exposes get_svc overloads and starts / stops resources on the app lifespan.
"},{"location":"02_components/#how-the-request-flows","title":"\ud83d\udd17 How the request flows","text":"
sequenceDiagram\n participant C as Client\n participant R as route (interfaces)\n participant S as service (port factory / adapter)\n participant P as repo (infra)\n participant DB as MongoDB\n\n C->>R: POST /items (operationId)\n R->>S: svc.create(payload)\n S->>P: repo.insert(doc)\n P->>DB: insert\n DB-->>P: ack\n P-->>S: doc\n S-->>R: result\n R-->>C: 200 JSON
"},{"location":"03_start_an_app/","title":"\ud83d\ude80 Start an App","text":"
This is the entry point for building a new application from the blueprint. The goal is one deterministic pass: you supply only the two axes, and the agent does the rest by reading the standards MCP, importing Tier A libraries, and copying Tier B template components.
Doc model: this page is the human entry point. The deterministic, machine-facing checklist lives in the reference page Build Recipe, served as docs://modules/build_recipe.
"},{"location":"03_start_an_app/#what-the-agent-needs-from-you","title":"\ud83e\uddfe What the agent needs from you","text":"
Gather these before starting \u2014 the start_app MCP prompt asks for them:
App name + one-line purpose.
Axis 1 \u2014 resources. Each resource, its fields, and relationships. Paste openapi.yaml if you already have it.
Axis 2 \u2014 pipelines. Only if an input needs a multi-step transformation: the families and their input formats (or \"none\").
Auth needed now? Default no. Auth is deferred (py-jwt primitives only), and an authless app is just a spec with no security keys \u2014 see Spec-First Routing \u2192 Authless apps.
If you can state axes 1 and 2, the rest is mechanical.
"},{"location":"03_start_an_app/#how-to-start","title":"\u25b6\ufe0f How to start","text":"
From an MCP client connected to the standards server:
Invoke the intake prompt (or start_app with no arguments) to get the intake questions.
Answer them, then invoke build (build(app_name=..., resources=..., pipelines=..., auth=...)), or use start_app to intake-and-build in one call.
The prompt hands off to docs://modules/build_recipe; the agent scaffolds with get_app_template() and pulls exact files with get_app_component(kind, resource=..., family=...), authoring only the app-specific parts.
To validate the standard without touching a real app, invoke trial (trial(app_name=..., build_path=...)) to build into a scratch directory, prove it builds, and report findings.
Manual alternative \u2014 copy the skeleton and follow the reference recipe:
Every repository ships its documentation as a Model Context Protocol (MCP) server, and the standards registry server ties them together. The docs portal runs each one as a FastMCP streamable-http app bound to 0.0.0.0:<port>, exposing the endpoint at /mcp (see docs/main.py and docs/config.yml).
Connect your agent to the standards server first: it answers find_library, list_repos, and the start_app prompt, so the agent reaches for an internal library instead of reimplementing one (see the Library Registry).
"},{"location":"05_mcp_setup/#run-the-servers-locally","title":"\u25b6\ufe0f Run the servers locally","text":"
From the docs checkout:
.venv\\Scripts\\python.exe main.py # every server\n.venv\\Scripts\\python.exe main.py --servers standards # just the registry\n
:8000 is the health endpoint (GET /healthz), not an MCP server.
The hosted endpoints are served over HTTPS at https://mcp.aetoskia.com/<slug>/mcp. The tabs below are linked (content.tabs.link): switching one switches all of them. Every snippet registers all thirteen servers, so a single paste is enough.
"},{"location":"05_mcp_setup/#claude-code","title":"\ud83e\udd16 Claude Code","text":"
Project-scoped .mcp.json at the repo root. The type field is required \u2014 an entry with a url but no type is read as a stdio server and skipped.
Or add them with the CLI: opencode mcp add standards --url http://localhost:8009/mcp --global.
"},{"location":"05_mcp_setup/#pycharm-junie-ai-assistant","title":"\ud83e\udd16 PyCharm (Junie / AI Assistant)","text":"
~/.junie/mcp/mcp.json (user scope) or .junie/mcp/mcp.json (project scope). The same config is reachable from Settings | Tools | MCP Server \u2192 Manual Client Configuration \u2192 Copy HTTP Stream Config.
Two suites run under pytest with asyncio auto mode: integration (the HTTP edge against fakes) and unit (pure domain and pipeline logic). Finish a blueprint app only when every gate below passes.
For rule-driven pipelines, add a test that cross-references rules against the declared pattern registry: every flag emitted must be defined; every defined flag must be reachable. This catches drift without running the pipeline.
How to cut a release of any Aetos repository. The rule lives in the reference page Versioning & Changelog; this is the runbook you follow.
Every repository is one of two Drone shapes \u2014 a PyPI package or a Docker deployment (Drone Pipelines). Both release the same way: the version bump and changelog promotion go in the merge request, and the tag is created on the merged commit.
"},{"location":"04_development/02_releasing/#before-you-start","title":"\ud83e\uddfe Before you start","text":"
You have an open merge request holding the change, and main is green (the Drone quality gate passed on the last run).
You know whether this is a breaking, feature, or fix release.
The version file exists:
package \u2192 version in pyproject.toml
service \u2192 VERSION at the repo root
"},{"location":"04_development/02_releasing/#pick-the-next-version","title":"\ud83d\udea6 Pick the next version","text":"
The version file and the tag must be identical. For 0.y.z, 0.y is the major line (see the reference page); once at 1.0.0, normal SemVer applies.
Change 0.y.z \u2192 1.0.0+ \u2192 Breaking 0.(y+1).0(x+1).0.0 Feature 0.y.(z+1)x.(y+1).0 Fix 0.y.(z+1)x.y.(z+1)"},{"location":"04_development/02_releasing/#release-in-the-mr","title":"\ud83c\udf3f Release in the MR","text":"
The version is known while the change is still a merge request, so the bump rides along with it. Do all edits in the MR:
Reconcile the changelog first. [Unreleased] is a draft, not the source of truth: diff the change against the last tag (git log <last-tag>..HEAD, git diff --stat <last-tag>..HEAD) and add an entry for every user-facing change that is missing. Always show the proposed additions to the user and wait for approval before promoting \u2014 ask rather than guess when an entry is ambiguous.
Promote the changelog in CHANGELOG.md: move the (now reconciled) [Unreleased] content under a new ## [x.y.z] - YYYY-MM-DD heading, leave a fresh empty [Unreleased] on top, and add the compare links \u2014 naming the version/tag, never a commit id:
The agent does not author or merge the MR and never tags before the merge. After you confirm the MR is merged/squashed and tell it to proceed, the agent:
Verifies that commit's version file equals x.y.z, then tags only that commit: git tag x.y.z <sha>.
Does not push \u2014 it asks you to run git push origin x.y.z.
"},{"location":"04_development/02_releasing/#what-drone-does","title":"\ud83e\udd16 What Drone does","text":"
Type A (package): asserts tag == pyproject version, then skips if that version already exists on the index, runs the quality gate, builds, and uploads the wheel + sdist.
Type B (service): asserts tag == VERSION, resolves the tag, skips if the image already exists, runs the quality gate, builds, pushes, and restarts the container.
If the guard fails, nothing is published \u2014 fix the version file, amend the commit, and move the tag only if it was never pushed.
If an older tag never published (a past version-file mismatch, an index outage):
Package: run the backfill-pypi-from-tags pipeline (custom event). It checks out every tag, verifies tag == pyproject version at that commit, and uploads the ones missing from the index. Already-fixed history is never rewritten.
Service: rebuild the image for the missing tag with a fresh commit and a new patch version; do not move a published tag.
"},{"location":"04_development/02_releasing/#if-a-release-is-wrong","title":"\ud83d\udd01 If a release is wrong","text":"
Never reuse or force-move a pushed tag.
Fix forward: bump to the next patch, promote the changelog, tag again.
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83e\udde9 Standards \u2014 the Aetos App Blueprint","text":"
One reusable architecture for every backend application in the Aetos ecosystem: a spec-first HTTP edge, a framework-free domain core, a pluggable infrastructure layer, and a single composition root.
Fill in two axes only and a new app is done: your resource model, and (if needed) your pipeline family. The blueprint itself never names a business domain \u2014 expenses, health metrics, inventory, and analytics are all the same shape underneath.
Doc model: this wiki is written for humans \u2014 how-to guides, the build recipe, and quality gates. The enforceable rules (layering, composition root, CRUD template, pipeline scaffold) and the library registry live in the reference pages, mirrored by the machine-readable standards MCP bundle. Query find_library before writing any infrastructure.
\ud83d\udd0c Single composition root \u2014 every dependency is wired in exactly one place
\ud83e\udde9 Generic, not app-specific \u2014 reusable rules versus two authoring axes
\ud83d\udcda Library-backed \u2014 DB, DI, pipelines, ingestion, auth, and docs come from internal libraries, never hand-written
\ud83d\udea6 Explicit state machines \u2014 durable job statuses, progress, and human-in-the-loop pauses
"},{"location":"#documentation-structure","title":"\ud83d\udcc1 Documentation Structure","text":"Section Description Overview Philosophy, the one-way dependency rule, architecture App Components The component model and how the pieces wire together Start an App The entry point: gather the two axes, then build Testing & Quality Test layout and acceptance gates
The blueprint is a hexagonal (ports & adapters) application with a spec-first web edge and a single composition root. It favors explicit wiring over implicit magic and existing internal libraries over hand-written infrastructure. Start here to build the mental model; the enforceable details live in the reference pages.
The rule holds at every edge: code in an outer ring may depend on a ring closer to the core, never the reverse. domain/ never imports a web framework; only its ports/ seam reaches into infra/ to bind adapters.
Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter binding, performed by the composition root at startup.
"},{"location":"01_overview/#the-one-way-dependency-rule","title":"\ud83d\udd04 The one-way dependency rule","text":"Ring May import Never imports interfaces/domain, framework glue (glue only) business logic, infra directly domain/ its own models + its ports/ seam (which binds infra/libraries) web frameworks, interfaces/infra/domain ports, drivers, libraries \u2014 init.py everything (the wiring point) \u2014"},{"location":"01_overview/#core-principles","title":"\ud83e\udded Core principles","text":"
One-way dependencies. interfaces -> domain -> infra is allowed; the reverse is forbidden.
Ports in the domain, adapters in infra. A service depends on a port; the port's factory binds the concrete adapter, and the composition root calls those factories.
One composition root. Nothing constructs its own dependencies. All wiring lives in one module (init.py) exposed as a flat resource registry.
Spec-first. The HTTP contract is declared in openapi.yaml; route handlers are looked up by operationId. There are no decorator routers.
Reuse the libraries. Orchestration, DI, Mongo repositories, ingestion, auth, and docs come from internal libraries \u2014 see the Library Registry.
Explicit state machines. Long-running work moves through explicit, durable statuses and can pause for human input.
What ships in every standard app, and how the pieces relate. This is the blueprint's component model \u2014 templates/app/ is a runnable instance of it.
The blueprint has two orthogonal views: runtime flow (who calls whom) and composition-root wiring (who constructs whom). Keeping them separate makes the layering visible instead of hiding it behind a hub.
Legend \u2014 init.py is the only place that imports concrete adapters. The ports/ factory seams bind infra; the API reads resources from app.state.db_resources, the worker calls init() directly.
"},{"location":"02_components/#component-roles","title":"\ud83d\udce6 Component roles","text":""},{"location":"02_components/#1-interfaces-the-edge","title":"1. interfaces/ \u2014 the edge","text":"
Thin handlers bound to openapi.yamloperationIds. They parse the request, call a service, and shape the response. No business rules, no I/O.
interfaces/openapi.py re-exports every route module so the OpenAPI-first app can resolve handlers.
Routes may mount the cache decorator on read endpoints.
"},{"location":"02_components/#2-domain-the-core","title":"2. domain/ \u2014 the core","text":"
Models, ports, and services (use cases). No framework imports. The ports/ package is the one seam allowed to reach into infra/: each port module declares the document shape and a factory that binds the adapter.
Extraction and enrichment families, each a deterministic step chain (abc.py / impl.py / pipeline.yaml / resources/). Roots are registered in the pattern registry and driven either by a worker job or a CLI.
"},{"location":"02_components/#5-workerpy-the-job-worker-optional","title":"5. worker.py \u2014 the job worker (optional)","text":"
A separate process that polls durable job collections, runs pipeline roots, and publishes progress to the progress bus. It shares the same composition root as the API.
"},{"location":"02_components/#6-initpy-the-composition-root","title":"6. init.py \u2014 the composition root","text":"
The only place anything is constructed: leaf repositories \u2192 dependent repositories \u2192 services \u2192 adapters. Exposes get_svc overloads and starts / stops resources on the app lifespan.
"},{"location":"02_components/#how-the-request-flows","title":"\ud83d\udd17 How the request flows","text":"
sequenceDiagram\n participant C as Client\n participant R as route (interfaces)\n participant S as service (port factory / adapter)\n participant P as repo (infra)\n participant DB as MongoDB\n\n C->>R: POST /items (operationId)\n R->>S: svc.create(payload)\n S->>P: repo.insert(doc)\n P->>DB: insert\n DB-->>P: ack\n P-->>S: doc\n S-->>R: result\n R-->>C: 200 JSON
"},{"location":"03_start_an_app/","title":"\ud83d\ude80 Start an App","text":"
This is the entry point for building a new application from the blueprint. The goal is one deterministic pass: you supply only the two axes, and the agent does the rest by reading the standards MCP, importing Tier A libraries, and copying Tier B template components.
Doc model: this page is the human entry point. The deterministic, machine-facing checklist lives in the reference page Build Recipe, served as docs://modules/build_recipe.
"},{"location":"03_start_an_app/#what-the-agent-needs-from-you","title":"\ud83e\uddfe What the agent needs from you","text":"
Gather these before starting \u2014 the start_app MCP prompt asks for them:
App name + one-line purpose.
Axis 1 \u2014 resources. Each resource, its fields, and relationships. Paste openapi.yaml if you already have it.
Axis 2 \u2014 pipelines. Only if an input needs a multi-step transformation: the families and their input formats (or \"none\").
Auth needed now? Default no. Auth is deferred (py-jwt primitives only), and an authless app is just a spec with no security keys \u2014 see Spec-First Routing \u2192 Authless apps.
If you can state axes 1 and 2, the rest is mechanical.
"},{"location":"03_start_an_app/#how-to-start","title":"\u25b6\ufe0f How to start","text":"
From an MCP client connected to the standards server:
Invoke the intake prompt (or start_app with no arguments) to get the intake questions.
Answer them, then invoke build (build(app_name=..., resources=..., pipelines=..., auth=...)), or use start_app to intake-and-build in one call.
The prompt hands off to docs://modules/build_recipe; the agent scaffolds with get_app_template() and pulls exact files with get_app_component(kind, resource=..., family=...), authoring only the app-specific parts.
To validate the standard without touching a real app, invoke trial (trial(app_name=..., build_path=...)) to build into a scratch directory, prove it builds, and report findings.
Manual alternative \u2014 copy the skeleton and follow the reference recipe:
Every repository ships its documentation as a Model Context Protocol (MCP) server, and the standards registry server ties them together. The docs portal runs each one as a FastMCP streamable-http app bound to 0.0.0.0:<port>, exposing the endpoint at /mcp (see docs/main.py and docs/config.yml).
Connect your agent to the standards server first: it answers find_library, list_repos, and the start_app prompt, so the agent reaches for an internal library instead of reimplementing one (see the Library Registry).
"},{"location":"05_mcp_setup/#run-the-servers-locally","title":"\u25b6\ufe0f Run the servers locally","text":"
From the docs checkout:
.venv\\Scripts\\python.exe main.py # every server\n.venv\\Scripts\\python.exe main.py --servers standards # just the registry\n
:8000 is the health endpoint (GET /healthz), not an MCP server.
The hosted endpoints are served over HTTPS at https://mcp.aetoskia.com/<slug>/mcp. The tabs below are linked (content.tabs.link): switching one switches all of them. Every snippet registers all twelve servers, so a single paste is enough.
"},{"location":"05_mcp_setup/#claude-code","title":"\ud83e\udd16 Claude Code","text":"
Project-scoped .mcp.json at the repo root. The type field is required \u2014 an entry with a url but no type is read as a stdio server and skipped.
Or add them with the CLI: opencode mcp add standards --url http://localhost:8009/mcp --global.
"},{"location":"05_mcp_setup/#pycharm-junie-ai-assistant","title":"\ud83e\udd16 PyCharm (Junie / AI Assistant)","text":"
~/.junie/mcp/mcp.json (user scope) or .junie/mcp/mcp.json (project scope). The same config is reachable from Settings | Tools | MCP Server \u2192 Manual Client Configuration \u2192 Copy HTTP Stream Config.
Two suites run under pytest with asyncio auto mode: integration (the HTTP edge against fakes) and unit (pure domain and pipeline logic). Finish a blueprint app only when every gate below passes.
For rule-driven pipelines, add a test that cross-references rules against the declared pattern registry: every flag emitted must be defined; every defined flag must be reachable. This catches drift without running the pipeline.
How to cut a release of any Aetos repository. The rule lives in the reference page Versioning & Changelog; this is the runbook you follow.
Every repository is one of two Drone shapes \u2014 a PyPI package or a Docker deployment (Drone Pipelines). Both release the same way: the version bump and changelog promotion go in the merge request, and the tag is created on the merged commit.
"},{"location":"04_development/02_releasing/#before-you-start","title":"\ud83e\uddfe Before you start","text":"
You have an open merge request holding the change, and main is green (the Drone quality gate passed on the last run).
You know whether this is a breaking, feature, or fix release.
The version file exists:
package \u2192 version in pyproject.toml
service \u2192 VERSION at the repo root
"},{"location":"04_development/02_releasing/#pick-the-next-version","title":"\ud83d\udea6 Pick the next version","text":"
The version file and the tag must be identical. For 0.y.z, 0.y is the major line (see the reference page); once at 1.0.0, normal SemVer applies.
Change 0.y.z \u2192 1.0.0+ \u2192 Breaking 0.(y+1).0(x+1).0.0 Feature 0.y.(z+1)x.(y+1).0 Fix 0.y.(z+1)x.y.(z+1)"},{"location":"04_development/02_releasing/#release-in-the-mr","title":"\ud83c\udf3f Release in the MR","text":"
The version is known while the change is still a merge request, so the bump rides along with it. Do all edits in the MR:
Reconcile the changelog first. [Unreleased] is a draft, not the source of truth: diff the change against the last tag (git log <last-tag>..HEAD, git diff --stat <last-tag>..HEAD) and add an entry for every user-facing change that is missing. Always show the proposed additions to the user and wait for approval before promoting \u2014 ask rather than guess when an entry is ambiguous.
Promote the changelog in CHANGELOG.md: move the (now reconciled) [Unreleased] content under a new ## [x.y.z] - YYYY-MM-DD heading, leave a fresh empty [Unreleased] on top, and add the compare links \u2014 naming the version/tag, never a commit id:
The agent does not author or merge the MR and never tags before the merge. After you confirm the MR is merged/squashed and tell it to proceed, the agent:
Verifies that commit's version file equals x.y.z, then tags only that commit: git tag x.y.z <sha>.
Does not push \u2014 it asks you to run git push origin x.y.z.
"},{"location":"04_development/02_releasing/#what-drone-does","title":"\ud83e\udd16 What Drone does","text":"
Type A (package): asserts tag == pyproject version, then skips if that version already exists on the index, runs the quality gate, builds, and uploads the wheel + sdist.
Type B (service): asserts tag == VERSION, resolves the tag, skips if the image already exists, runs the quality gate, builds, pushes, and restarts the container.
If the guard fails, nothing is published \u2014 fix the version file, amend the commit, and move the tag only if it was never pushed.
If an older tag never published (a past version-file mismatch, an index outage):
Package: run the backfill-pypi-from-tags pipeline (custom event). It checks out every tag, verifies tag == pyproject version at that commit, and uploads the ones missing from the index. Already-fixed history is never rewritten.
Service: rebuild the image for the missing tag with a fresh commit and a new patch version; do not move a published tag.
"},{"location":"04_development/02_releasing/#if-a-release-is-wrong","title":"\ud83d\udd01 If a release is wrong","text":"
Never reuse or force-move a pushed tag.
Fix forward: bump to the next patch, promote the changelog, tag again.
Provide the MUI theme, color-mode context and CSS variables to children.
-
Builds the theme from getDesignTokens(mode) + getSemanticColors(mode) +
-the component customizations, and re-creates it whenever the mode changes.
-Also sets --bg-page, --bg-card, --bg-elevated, --border-default,
---border-subtle, --text-primary, --text-secondary and --text-muted
-on a wrapper Box.
-
Props: children and defaultMode (see AppThemeProps).
Build the MUI design tokens (palette, typography, shape, shadows) for a mode.
-
Light and dark palettes share the ramps but override specific slots in dark
-mode. Also sets shadows[1] (the "baseShadow") per mode. The returned object
-is spread into createTheme by AppTheme.
A fixed bottom AppBar (pinned above the drawer via
-zIndex.drawer + 1) with a blurred translucent background and a top
-divider, rendering the Copyright line. Rendered once by main.jsx below
-the routed content.
Top navigation bar for the Veterinary Clinic demo app.
-
A fixed AppBar with blurred translucent background and bottom divider,
-showing the app title/logo and an Admin button. Rendered once by main.jsx
-above the routed content; the title navigates to / and the Admin button to
-/admin.
Creates the React root from #root and mounts the app: AppProvider
-(fed by specConfiguration) wraps a BrowserRouter → AppTheme →
-Header → routed content (Home at /, Admin at /admin/*) →
-Footer.
Runtime OpenAPI configuration for the Veterinary Clinic demo.
-
Reads the API base URL from import.meta.env.VITE_API_BASE_URL and points
-react-openapi at ${base}/openapi.json, sets the app title, and enables
-client-side filtering for the appointments resource.
Wraps the MUI ThemeProvider with the design tokens (getDesignTokens),
-semantic colors (getSemanticColors) and component customizations
-(inputsCustomizations, dataDisplayCustomizations,
-feedbackCustomizations, navigationCustomizations,
-surfacesCustomizations). Exposes a light/dark ColorModeContext and
-publishes the semantic colors as CSS custom properties (--bg-page,
---bg-card, --text-primary, ...) for the app subtree.
Exports inputsCustomizations, a Components<Theme> map applied by
-AppTheme, covering buttons, icon buttons, toggles, checkbox, outlined
-inputs and form labels with light/dark variants.
Adds a semantic slice to the MUI theme (augmenting @mui/material/styles)
-with surface/border/text colors, resolved per light/dark mode by
-getSemanticColors. AppTheme publishes these as CSS custom properties.
Base design tokens and palettes for the Veterinary Clinic theme.
-
Defines the raw color ramps (brand, gray, green, orange, red), the
-light/dark design tokens (getDesignTokens), and the colorSchemes,
-typography, shape and shadows primitives. Also augments MUI's theme
-types with the ColorRange, flows and baseShadow extensions.
Fixed bottom app bar showing the site copyright.
-