Skip to content

Features → code

A complete map of what the platform does and where it lives in the code. Every capability is exposed two ways — as MCP tools (for AI clients) and as REST /v1 endpoints (for the webapp and HTTP clients) — over the single backend in apps/mcp-server, sharing the same repositories, domain schemas, and scopes.

For the exhaustive call-by-call lists see MCP tools and the REST API. This page is the conceptual tour.

Where things live

  • Domain schemas (single source of truth): packages/shared/src/*.ts (Data models)
  • MCP tool registration: apps/mcp-server/src/tools/register*.ts
  • REST routes: apps/mcp-server/src/rest/router.ts
  • Persistence (Kysely repositories): apps/mcp-server/src/db/*.ts

The core: vehicle listings (cars, motorcycles, scooters), owned by a dealer and optionally attributed to an organization.

CapabilityMCP toolRESTCode
Search + filter + facets + sort + paginationsearch_vehiclesGET /v1/listingsdb/repository.ts (search)
Fetch one (by id or VIN)get_vehicleGET /v1/listings/:iddb/repository.ts
Create / update / deletecreate_listing · update_listing · delete_listingPOST · PATCH · DELETE /v1/listings/:iddb/repository.ts
Bulk create / delete (≤50)bulk_create_listings · bulk_delete_listingsPOST /v1/listings/bulk · /bulk-deletedb/repository.ts
The caller's own listings (all statuses)GET /v1/me/listingsdb/repository.ts

Search filters on type, make, model, year/price/mileage ranges (price CHF-normalized for cross-currency comparison), fuel, transmission, condition, drive, energy class, and radius. Writes are owner-scoped — a dealer can only mutate their own listings (or those of an org they belong to) — enforced by writeAuthFilter in db/repository.ts. Registered in tools/registerTools.ts.

Parse free text like "cheap electric SUV after 2020" into a structured query, then run it.

  • Tool: search_vehicles_nl · REST: POST /v1/search/nl
  • Code: apps/mcp-server/src/search/nlSearch.tsNlSearchProvider interface with a deterministic HeuristicNlSearchProvider (make aliases, fuel/type keywords, price/year extraction); pluggable for an LLM provider later.

Analytics: price rating, similar, compare

  • Price rating — fair-price label (great/good/fair/high) vs. the median of comparable active listings. Tool get_price_rating · GET /v1/listings/:id/price-rating.
  • Similar — ranks by closeness in price/year/mileage (bonus for same model). Tool get_similar_vehicles · GET /v1/listings/:id/similar.
  • Compare — fetch 2–5 listings side-by-side. Tool compare_vehicles · GET /v1/listings/compare?ids=….
  • Code: apps/mcp-server/src/analytics/analytics.ts (priceRating, similarListings, compareListings).

Seller profiles & locations

A sellers row is keyed by the Keycloak subject and auto-provisioned on first write.

  • Profile read/update: get_my_profile · update_my_profile · GET/PUT /v1/me.
  • Public profile: GET /v1/dealers/:id and /v1/dealers/slug/:slug.
  • Locations (branches with address, hours, geocoded): add_location · update_location · remove_location · GET/POST/PATCH/DELETE /v1/me/locations.
  • Code: apps/mcp-server/src/db/sellerRepository.ts; registered in tools/registerSellerTools.ts.

Organizations (shared storefronts)

A company many users belong to; a listing can be attributed to an org and any member can manage it, while the creator stays the dealerId for audit.

  • Create/update, membership (owner/member roles), public org page, org inventory.
  • Tools in tools/registerOrganizationTools.ts: create_organization, update_organization, list_my_organizations, get_organization, list_organization_members, add_organization_member, remove_organization_member.
  • REST under /v1/organizations/* and /v1/me/organizations.
  • Code: apps/mcp-server/src/db/organizationRepository.ts.

Buyer accounts: favorites, saved searches, alerts

  • Account profile + notification prefs: get_account · update_account · GET/PUT /v1/me/account.
  • Favorites (watchlist): list_favorites · add_favorite · remove_favorite · GET/POST/DELETE /v1/me/favorites.
  • Saved searches (+ new-match alerts): list_saved_searches · save_search · delete_saved_search · GET/POST/DELETE /v1/me/saved-searches.
  • Alert delivery: apps/mcp-server/src/notify/savedSearchAlerts.ts (runSavedSearchAlerts, cron-friendly, triggerable via POST /v1/admin/run-alerts) → notify/notifier.ts (email/push interface).
  • Code: apps/mcp-server/src/db/accountRepository.ts; tools in tools/registerAccountTools.ts.

Account deletion

The GDPR / nDSG "right to erasure": a user can permanently delete their own account.

  • REST: DELETE /v1/me/account (self-scoped, listings:read). One Postgres transaction hard-deletes every row keyed to the user — listings, seller profile + locations, buyer profile, favorites (theirs and others' favorites of their listings, FK-safe), saved searches, reviews (authored and received), org memberships and audit entries — then the Keycloak login is deleted so the account can no longer sign in. (Organizations the user created are intentionally left intact; only their membership is removed.)
  • Graceful degradation: the data erasure is the transaction; the Keycloak deletion is best-effort and runs after it. With no service account configured it's a no-op (the login is left for an operator); a failure is logged + sent to Sentry but never fails the request. With no deletion repository wired the endpoint returns 501.
  • Keycloak login removal uses a least-privilege confidential service-account client (realm-management:manage-users only), provisioned by keycloak/provision-account-admin.sh and configured via KEYCLOAK_ADMIN_CLIENT_ID / KEYCLOAK_ADMIN_CLIENT_SECRET.
  • Webapp: the account page's "Danger zone" → Delete my account (confirm) calls the endpoint and signs the user out.
  • Code: apps/mcp-server/src/db/accountDeletion.ts (Postgres transaction + in-memory purge), apps/mcp-server/src/auth/keycloakAdmin.ts (Keycloak user deleter); route in rest/router.ts.

Seller reviews & ratings

1–5 star buyer reviews of a seller, aggregated into ratingAvg/ratingCount; carry a moderation status so abusive content can be hidden.

  • list_seller_reviews · review_seller · moderate_review; GET/POST /v1/dealers/:id/reviews, PATCH /v1/admin/reviews/:id/moderation.
  • Code: apps/mcp-server/src/db/reviewRepository.ts; tools in tools/registerEngagementTools.ts.

Media

  • Flows: signed HMAC upload tickets (request_upload), inline base64 (upload_media), and authenticated binary upload (POST /v1/media). Served publicly at /media/:key (immutable cache).
  • Limits/types: ≤25 MiB (MEDIA_MAX_BYTES); JPEG/PNG/WebP/GIF/AVIF + MP4/WebM/MOV.
  • Code: apps/mcp-server/src/media/{store.ts,uploadTicket.ts,uploadPage.ts}; tools in tools/registerMediaTools.ts.

Geocoding

Forward geocoding for location pickers, region-routed (Swisstopo GeoAdmin by default, CH-accurate).

  • REST: GET /v1/geo/suggest?q=…. Code: apps/mcp-server/src/geo/{geocoder.ts,geoAdmin.ts}.
  • Config: GEOCODER_PROVIDER, GEOCODER_DEFAULT_COUNTRY, GEOCODER_TIMEOUT_MS.

Moderation, soft-delete & audit

  • Moderation status (approved/pending/rejected) hides listings from public search; soft-delete (deleted_at) keeps inventory recoverable. Both listings:moderate-only.
  • Tools: moderate_listing, restore_listing, list_moderation_queue, list_audit_log. REST under /v1/admin/*.
  • Append-only audit log records every listing write (create/update/delete/restore/moderate).
  • Code: moderation/soft-delete in db/repository.ts; db/auditRepository.ts.

SEO & structured data

Server-rendered listing/dealer/org pages with schema.org JSON-LD (AutoDealer, AggregateRating) for crawlers and social graphs. The edge Caddy proxies crawler paths (/listings/*, /dealers/*, sitemap.xml, robots.txt, llms.txt) to the mcp-server.

  • Code: apps/mcp-server/src/seo/{render.ts,router.ts}.

The webapp

apps/webapp — a React 19 + Vite SPA that drives the REST API: browse/search, listing detail with price-rating and similar rails, favorites, saved searches, a map radius search, a dealer dashboard (create/edit/delete + profile + locations), organizations, an account hub, and seller reviews. Auth is a Keycloak OIDC redirect (token in localStorage).

Transports & auth

The same tool/route logic runs over two transports — see Architecture:

  • stdio (transport/stdio.ts) — local-trust, no token, fixed dev principal.
  • HTTP Streamable (transport/http.ts) — OAuth 2.1 bearer at /mcp, RFC 8707 audience.
  • Auth: auth/{bearer.ts,principal.ts,resourceMetadata.ts}; Keycloak realm in keycloak/realm-export.json. Scopes: listings:read · listings:write · listings:moderate.

A-Market — AI-first marketplace for cars, motorcycles and scooters.