FastAPI v1 Command Center Adapter Discovery
Status
Implemented
Success Condition
apps/v1 remains backward compatible for all existing frontend routes while the
same API surface becomes discoverable by Main Sequence Command Center through
Adapter from API.
The implementation is successful only when:
- every existing
/api/v1/*path, request model, response model, andoperationIdkeeps its current behavior unless a separate ADR explicitly changes it; GET /.well-known/command-center/connection-contractexists and returns a strict adapter discovery document;GET /healthexists and is the zero-argument health operation referenced by the adapter contract;GET /openapi.jsonremains available and is referenced by the adapter contract, but is not treated as the adapter source of truth;- every current public
apps/v1operation is listed inavailableOperations; - mutating endpoints are not exposed as query-capable operations;
- any endpoint that directly feeds generic tabular consumers returns the SDK
core.tabular_frame@v1contract throughTabularFrameResponse; - provider-native responses stay provider-native, with optional
responseMappingsused only as metadata for frontend/editor context or a future explicit transform path.
Deployment, FastAPI ResourceRelease creation, Command Center connection instance creation, and workspace wiring are intentionally out of scope for this ADR.
Context
The current apps/v1 FastAPI surface already has documented Pydantic contracts
and stable route groups for assets, accounts, portfolios, virtual funds,
calendars, pricing curves, pricing market data, settings, and fixed income
pricing operations.
The current frontend consumes these routes directly. That behavior must remain valid. Adapter from API support must therefore be additive metadata and discovery over the same route set:
existing direct frontend clients
-> /api/v1/*
Command Center Adapter from API
-> /.well-known/command-center/connection-contract
-> /health
-> /openapi.json
-> existing /api/v1/* operations by operationId
The adapter skill defines the required provider-side contract:
- the well-known contract endpoint is the discovery source of truth;
/openapi.jsonis supplementary documentation;- health must be a trivial zero-argument operation;
- available operations must be explicitly allowlisted;
- operation IDs must be stable;
- public config and secret variables must be separated;
- provider-native JSON is not a canonical tabular frame just because a
responseMappingsentry exists; - generic table, chart, statistic, curve, transform, and agent-facing
consumers require an actual
core.tabular_frame@v1payload at the consumption boundary.
The SDK public API tutorial also treats FastAPI as a project API surface and shows the same project-resource deployment model. This ADR does not deploy the API. It only plans the local API contract required before a release can be made.
Decision
Make the existing apps/v1 API adapter-ready without creating a shadow API.
The required adapter control surface consists only of:
GET /health
GET /.well-known/command-center/connection-contract
These endpoints do not replace, wrap, or duplicate business routes. They only
let Command Center discover and call the existing /api/v1/* operations.
Implementation may keep the adapter control contract in dedicated files:
apps/v1/routers/command_center.py
apps/v1/schemas/command_center_adapter.py
apps/v1/services/command_center_adapter.py
Those files are limited to adapter discovery and health. They must not define
business-route variants such as /command-center/assets, /adapter/accounts,
or any other duplicate route family.
The control router will be registered at the application root, not under
/api/v1, because the well-known path is a discovery endpoint for the deployed
API resource.
The adapter contract response will be strict Pydantic, with extra="forbid".
Unknown fields should fail tests unless the contract version is intentionally
advanced.
Required Adapter Control Endpoints
Health
GET /health
Purpose:
- provide a trivial health check for Adapter from API;
- avoid using a parameterized business route as health;
- avoid touching MetaTables or pricing runtime data paths.
Response model:
{
"status": "ok",
"service": "apps/v1",
"version": "0.0.49"
}
The exact version value comes from the installed ms-markets package version.
Adapter Discovery
GET /.well-known/command-center/connection-contract
Response model:
{
"contractVersion": 1,
"adapter": {
"type": "adapter-from-api",
"id": "ms-markets.apps-v1",
"title": "MainSequence Markets API",
"description": "Adapter contract for the apps/v1 markets FastAPI surface."
},
"openapi": {
"url": "/openapi.json",
"version": "3.1.0",
"checksum": null
},
"configVariables": [],
"secretVariables": [],
"availableOperations": [],
"health": {
"operationId": "getApiHealth",
"expectedStatus": 200,
"timeoutMs": 5000
}
}
Implementation details:
openapi.urlshould be resolved from the incoming request when possible, so deployed API resources can return an absolute URL.openapi.checksumshould be a deterministic SHA-256 checksum of the canonical OpenAPI JSON if it can be computed without side effects; otherwise it remainsnull.configVariablesis initially empty because this API does not require public adapter configuration to call its own deployed routes.secretVariablesis initially empty. Do not invent API token variables until the deployment/auth model requires backend secret injection.- request identity, access tokens, refresh tokens, and platform credentials must never be returned by the contract.
Explicit Operation Registry
The adapter contract must expose every current public API operation, but it must not discover or publish routes by blindly dumping every FastAPI route at runtime.
Add an explicit operation registry with stable operation IDs. The registry is an allowlist in the sense that every exposed operation is intentionally described, classified, and tested. It is not a partial rollout.
Read/query operations must be marked as query-capable. Mutating operations must also be included, but they must never be marked as query-capable.
System Operations
getApiHealth
The well-known contract endpoint itself is the discovery document and does not need to be treated as a normal business operation.
Query And Read Operations
Settings:
getApiSettings
Asset read operations:
listAssetsgetAssetgetAssetSummarygetAssetPricingDetails
Asset category read operations:
listAssetCategoriesgetAssetCategoryDetail
Account read operations:
listAccountsgetAccountSummarysearchAccountTargetAllocationTargetsgetAccountHoldingsgetAccountHoldingsByFundgetAccountTargetPositions
Index read operations:
listIndexesgetIndex
Portfolio read operations:
listPortfoliosgetPortfoliogetPortfolioSummarygetPortfolioWeights
Virtual fund read operations:
listVirtualFundsgetVirtualFundgetVirtualFundSummarygetVirtualFundHoldings
Calendar read operations:
listCalendarsgetCalendargetCalendarSummarylistCalendarDatesgetCalendarDatelistCalendarSessionsgetCalendarSessionlistCalendarEventsgetCalendarEvent
Pricing curve read operations:
listPricingCurvesgetPricingCurveSummarygetPricingDiscountCurve
Pricing market-data read operations:
getPricingMarketDataCardlistPricingMarketDataSetsgetPricingMarketDataSetByKeygetPricingMarketDataSetlistPricingMarketDataBindingslistPricingMarketDataSetBindingsresolvePricingMarketDataBindinggetPricingMarketDataBinding
Fixed income pricing operations:
priceFixedIncomeAssetgetFixedIncomeAssetAnalyticsgetFixedIncomeAssetDurationgetFixedIncomeAssetYieldgetFixedIncomeAssetZSpreadgetFixedIncomeAssetCashflowsgetFixedIncomeAssetCashflowsFramegetFixedIncomeAssetNetCashflowsgetFixedIncomeAssetNetCashflowsFramegetFixedIncomeAssetCarryRollDownpreviewFixedIncomeAssetCurvecheckFixedIncomeAssetFixingsAvailability
Mutation Operations
Asset operations:
deleteAsset
Asset category operations:
createAssetCategorybulkDeleteAssetCategoriesupdateAssetCategorydeleteAssetCategory
Account operations:
addAccountHoldingsaddAccountTargetPositions
Index operations:
deleteIndex
Portfolio operations:
bulkDeletePortfoliosdeletePortfolio
Calendar operations:
createCalendarupdateCalendardeleteCalendarcreateCalendarDatebulkUpsertCalendarDatesupdateCalendarDatedeleteCalendarDatecreateCalendarSessionbulkUpsertCalendarSessionsupdateCalendarSessiondeleteCalendarSessioncreateCalendarEventbulkUpsertCalendarEventsupdateCalendarEventdeleteCalendarEvent
Pricing market-data operations:
createPricingMarketDataSetupsertPricingMarketDataSetupdatePricingMarketDataSetdeletePricingMarketDataSetcreatePricingMarketDataBindingupsertPricingMarketDataBindingupdatePricingMarketDataBindingdeletePricingMarketDataBinding
Rules for mutation exposure:
kindmust bemutationor another explicit non-query kind supported by the adapter runtime.capabilitiesmust not includequery.requestBodymust be explicit for POST/PATCH/DELETE operations that require body data.cache.enabledmust befalse.- destructive operations must remain discoverable as operations, but clients should decide how to render confirmation and permission UI from the operation metadata and response contract.
Operation Metadata Shape
Every operation in availableOperations must include:
{
"operationId": "listAssets",
"label": "List assets",
"description": "List assets from the apps/v1 public API.",
"method": "GET",
"path": "/api/v1/asset/",
"kind": "query",
"capabilities": ["query"],
"requiresTimeRange": false,
"supportsVariables": true,
"supportsMaxRows": true,
"parameters": [],
"requestBody": null,
"responseMappings": [],
"cache": {
"enabled": true,
"ttlSeconds": 30
}
}
Rules:
pathis relative to the deployed API root and must include/api/v1for existing business routes.operationIdmust match the FastAPI routeoperation_idexactly.supportsMaxRowsshould be true only for operations that exposelimitor a bounded equivalent.- mutating endpoints must be included but must not be marked with
querycapability. - POST pricing operations can be query operations because they are read-only calculations, but their request body metadata must be explicit.
- cache TTLs must be conservative and operation-specific. Use
enabled=falsefor routes where results depend on live runtime state and no cache behavior is safe yet.
Mutation example:
{
"operationId": "deleteAsset",
"label": "Delete asset",
"description": "Delete one asset by uid.",
"method": "DELETE",
"path": "/api/v1/asset/{uid}/",
"kind": "mutation",
"capabilities": ["mutation"],
"requiresTimeRange": false,
"supportsVariables": true,
"supportsMaxRows": false,
"parameters": [
{
"name": "uid",
"in": "path",
"required": true,
"type": "string"
}
],
"requestBody": null,
"responseMappings": [],
"cache": {
"enabled": false,
"ttlSeconds": null
}
}
Response Mapping Policy
Provider-native responses remain provider-native.
Mappings may be provided for editor/frontend metadata, but must not claim
runtime conversion into core.tabular_frame@v1.
Initial mapping rules:
- paginated list endpoints can advertise
rowsPath: "$.results"; - holdings endpoints can advertise rows under
$.holdings; - target-position endpoints can advertise rows under
$.positions; - by-fund holdings can advertise fund rows under
$.fundsand residual rows under$.residualsas separate mappings; - fixed income cashflow provider-native endpoints can advertise their existing metadata mappings;
- existing endpoints whose response model is
TabularFrameResponsecan be advertised as direct canonical frame operations.
If a generic Command Center consumer needs direct tabular data, it should use an
operation whose response model is TabularFrameResponse, or an explicit
adapter/transform path. It should not bind raw provider-native JSON directly.
This ADR does not introduce new business endpoints just to create additional canonical frames. Any future direct frame route is a separate business/API design decision.
Implementation Tasks
- [x] Add strict adapter contract schemas in
apps/v1/schemas/command_center_adapter.py. - [x] Add
apps/v1/services/command_center_adapter.pywith the curated operation registry and OpenAPI checksum helper. - [x] Add
apps/v1/routers/command_center.pywith: - [x]
GET /health; - [x]
GET /.well-known/command-center/connection-contract. - [x] Register the command-center router in
apps/v1/main.pywithout a/api/v1prefix. - [x] Ensure the contract references the current OpenAPI URL and uses the deployed request base URL when available.
- [x] Add tests in
tests/msm/fastapi/v1/for: - [x] health response shape;
- [x] well-known contract required fields;
- [x]
adapter.type == "adapter-from-api"; - [x]
health.operationIdexists inavailableOperations; - [x] every allowlisted
operationIdexists in/openapi.json; - [x] every current public
operationIdis present inavailableOperations; - [x] mutating operations are present with non-query
kindand without thequerycapability; - [x] mutating operations have
cache.enabled=false; - [x] direct frame operations use
TabularFrameResponse; - [x] provider-native mappings do not mark the operation as a direct frame.
- [x] Update
docs/fast_api/v1/index.mdwith the new discovery and health endpoints. - [x] Update any frontend handoff docs that refer to Command Center discovery.
Compatibility Rules
- Do not move existing routes.
- Do not rename existing
operationIdvalues. - Do not alter existing response payloads for direct frontend clients.
- Do not replace provider-native endpoints with
TabularFrameResponse. - Do not add duplicate business endpoints as part of adapter-readiness.
- Expose mutation endpoints in the adapter contract with explicit non-query semantics, request body metadata, and disabled cache.
Consequences
Positive:
- Command Center can discover the API through a single well-known contract.
- Existing frontend clients continue using the same routes.
- Operation exposure becomes explicit and reviewable.
- Generic tabular consumers can identify which existing operations already return canonical frame contracts and which provider-native operations require a transform path.
- Provider-native response mappings remain useful metadata without pretending to be runtime transformations.
Tradeoffs:
- The operation registry must stay aligned with route
operationIdvalues. - Some provider-native endpoints will still require an adapter/transform path before they can feed generic tabular widgets.
- Mutating workflows become discoverable through Adapter from API, which means their operation metadata must stay accurate enough for clients to render confirmation, permission, and request-body UI correctly.
Validation
Run after implementation:
.venv/bin/python -m pytest tests/msm/fastapi/v1
.venv/bin/python -m ruff check apps/v1 tests/msm/fastapi/v1
.venv/bin/python -m ruff format --check apps/v1 tests/msm/fastapi/v1
.venv/bin/python -c "import apps.v1.main; print(apps.v1.main.API_TITLE)"
.venv/bin/python -m mkdocs build --strict
git diff --check
If platform state is being verified, separately check the FastAPI project resource and ResourceRelease through the Main Sequence platform workflow. That is outside this ADR's implementation scope.