remove vet from docs hub and all files
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
This commit is contained in:
@@ -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/<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.
|
||||
|
||||
---
|
||||
@@ -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" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -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 <token>` 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/<resource>/models.py` — plain pydantic models (`extra: "ignore"`)
|
||||
- `domain/<resource>/ports/db.py` — `XDocument(X, BaseDocument)` + `create_x_repo()`
|
||||
- `domain/<resource>/ports/service.py` — `XCrudsService(CrudsServiceAdaptor)` + `get_x_service(repo)`
|
||||
- `interfaces/<resource>/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`.
|
||||
Reference in New Issue
Block a user