Getting started
This tutorial installs the SDK, defines a backend-neutral resource, renders the standard list lifecycle, and places it inside an SDK page. At the end you will have a useful screen—not only a successful import.
If you first need the architectural vocabulary, read SDK architecture. For the complete export inventory, use the public API map.
1. Install the package
Run installation from the consuming application's Git and npm root:
npm install @dev-mainsequence/command-center-sdk react react-dom
Main Sequence Vite applications keep package.json, package-lock.json, .env, .agents/,
src/, and vite.config.* at that root. Do not create a nested frontend/ application.
React and React DOM are peer dependencies. Keep one compatible React runtime in the host.
2. Load theme and component styles
Import global SDK CSS once near the browser entrypoint, with theme variables first:
// src/main.tsx
import "@dev-mainsequence/command-center-sdk/theme/styles.css";
import "@dev-mainsequence/command-center-sdk/styles.css";
The theme stylesheet establishes semantic variables. Component styles consume those variables. Do not repeat these imports in individual screens.
3. Define a normalized resource
The resource definition is backend-neutral. It describes stable identity, labels, columns, and the adapter that returns SDK models:
// src/services/service-resource.ts
import { defineResourceApplication } from "@dev-mainsequence/command-center-sdk/resource";
export interface Service {
uid: string;
name: string;
status: "active" | "paused";
}
export const services = defineResourceApplication<Service, string>({
id: "services",
label: "Services",
itemLabel: "service",
getId: (service) => service.uid,
activation: {
resolve: (service) => ({ resource: "services", uid: service.uid }),
},
columns: [
{ id: "name", header: "Name", getValue: (service) => service.name },
{ id: "status", header: "Status", getValue: (service) => service.status },
],
adapter: {
async list({ pageIndex, pageSize, search, signal }) {
const query = new URLSearchParams({
offset: String(pageIndex * pageSize),
limit: String(pageSize),
...(search ? { search } : {}),
});
const response = await fetch(`/api/services?${query}`, { signal });
if (!response.ok) throw new Error("Services could not be loaded.");
const body = (await response.json()) as {
count: number;
results: Service[];
};
return {
items: body.results,
pageInfo: {
pageIndex,
pageSize,
totalItems: body.count,
hasNextPage: (pageIndex + 1) * pageSize < body.count,
hasPreviousPage: pageIndex > 0,
},
};
},
},
});
In production, route fetch through an application-owned client so authentication headers, base
URLs, retries, and error normalization remain outside the definition. Always pass the provided
signal; stale list requests must not overwrite current state.
pageInfo is authoritative. Do not infer a server total or next page from the number of rows in
the current response.
4. Render the standard list lifecycle
// src/services/ServicesPage.tsx
import { ResourceListPage } from "@dev-mainsequence/command-center-sdk/views";
import { services } from "./service-resource";
export function ServicesPage() {
return (
<ResourceListPage
definition={services}
searchable
searchPlaceholder="Search services"
refreshable
pageSize={25}
navigation={{
open: ({ uid }) => {
window.location.assign(`/services/${encodeURIComponent(uid)}`);
},
}}
/>
);
}
ResourceListPage now owns loading, failure, empty and no-results states, debounced search,
authoritative pagination, refresh, row activation, and stale-request handling. Your application
still owns the URL change and backend policy.
Do not wrap the list in a second toolbar, pager, selection bar, or loading system. Extend it through
columns, cells, filters, actions, renderCard, and the documented narrow contribution props.
5. Put the screen in an application page
import {
ApplicationPage,
ApplicationPageHeader,
ApplicationPageStack,
} from "@dev-mainsequence/command-center-sdk/layout";
import { ServicesPage } from "./services/ServicesPage";
export function ServicesRoute() {
return (
<ApplicationPage maxWidth="wide">
<ApplicationPageStack>
<ApplicationPageHeader
eyebrow="Operations"
title="Services"
description="Inspect runtime state and open a service."
/>
<ServicesPage />
</ApplicationPageStack>
</ApplicationPage>
);
}
The layout surface owns responsive page gutters, width, header wrapping, and section rhythm. The resource view owns its internal collection layout. Avoid adding another padded page wrapper or ordinary card around the entire list.
6. Add only the concepts you need
Choose the highest-level surface that already owns the workflow:
| Requirement | Start with | Guide |
|---|---|---|
| Resource list, detail, picker, or actions | /resource, /resource/react, /views | Resources |
| Application hierarchy and destinations | /navigation | Navigation |
| Responsive pages and cards | /layout | Application layout |
| Startup, retry, or terminal failure | /feedback | Application feedback |
| Buttons, badges, and labelled fields | /controls | Application controls |
| Theme presets, tokens, and chart colors | /theme | Themes |
| Application-owned cross-origin UI | /embed, /embed/react, and /vite for local development | Static-site embeds |
| Language-neutral backend payloads | Contract manifest and schemas | Backend contracts |
| A chat or AI capabilities | Not in the SDK: @dev-mainsequence/command-center-ai | Add AI capabilities |
Definitions, adapters, controlled props, and narrow renderers are the normal extension seams. Change SDK source only when missing behavior is reusable, backend-neutral, and useful across consumers.
7. Verify the integration
At minimum, run the consuming application's typecheck, unit tests, and production build. Exercise the screen at narrow and wide viewports and cover loading, error, empty, populated, search, paging, refresh, and row activation states. Include a touch phone viewport; see Mobile and touch.
For standard page geometry, use the real-browser verifier:
import {
assertCommandCenterPageLayout,
} from "@dev-mainsequence/command-center-sdk/layout/testing";
await assertCommandCenterPageLayout(page);
If publishing behavior or package contents matter, install an npm pack tarball into a clean
fixture. Workspace symlinks do not prove that declarations, CSS, schemas, fixtures, or docs are in
the published artifact.
Use public imports only
Supported:
import { createHttpResourceAdapter } from "@dev-mainsequence/command-center-sdk/resource";
import { ResourceListPage } from "@dev-mainsequence/command-center-sdk/views";
Unsupported:
import { ResourceListPage } from "@dev-mainsequence/command-center-sdk/dist/views/ResourceListPage.js";
The installed package's package.json exports and .d.ts files are authoritative. A source file
or proposed ADR is not automatically a public API.
Install current agent guidance
Package installation copies version-matched SDK skills to .agents/skills/command-center/. When
lifecycle scripts were disabled, refresh them explicitly:
npx command-center-sdk skills install --path . --dry-run
npx command-center-sdk skills install --path .
This namespace is an authoritative mirror of the installed SDK catalog. Refreshing it removes
obsolete or locally added entries inside command-center; keep application-specific guidance in a
different .agents/skills namespace. Other namespaces are not changed.
Use skills sync when backend-owned platform guidance must also be refreshed. Credential and
ownership details are in Application operations.
Add AI capabilities
The SDK has no AI capabilities: no chat with a Main Sequence Agent, no agent sessions, and no model
provider settings. They come from @dev-mainsequence/command-center-ai, a separate package that
takes this SDK as a peer, so the application keeps exactly one SDK. Install it only when its peer
range includes the installed SDK, and refresh its agent skills explicitly:
npm view @dev-mainsequence/command-center-ai peerDependencies --json
npm install @dev-mainsequence/command-center-ai
npx command-center-ai skills install --path .
Its skills install into .agents/skills/command-center-ai/, starting with use-command-center-ai;
they cover the right rail, the expanded page, sessions, model providers, and the platform
connection. Never install a second SDK or pass --legacy-peer-deps.
Initialize application documentation
Preview and create the official same-artifact documentation system from the application root:
npx command-center-sdk application docs init --path . --dry-run
npx command-center-sdk application docs init --path .
It adds an end-user help landing, a schema-version-2 manifest whose hierarchy derives the docs
folders from the visible application menu, and a /docs/ Docusaurus build inside the same dist/
artifact. Continue with Application documentation before
authoring feature and task pages.
Inspect or update the installed SDK
Keep declared, locked, installed, npm wanted, and registry latest versions distinct:
npx command-center-sdk application sdk-status --path .
npx command-center-sdk application update-sdk --path . --dry-run
npx command-center-sdk application update-sdk --path .
The update respects the current declaration and does not commit, tag, push, deploy, or change the application version. See Application operations for drift and constraint-blocked cases.
Refresh dependencies and deploy from Git
After you change dependencies in package.json, refresh the lockfile and the installed packages
from the repository root:
npx command-center-sdk code-repository sync --path .
It runs npm install --package-lock-only and then npm ci, and nothing else: it calls no backend
and does not version, commit, tag, or push. Commit and push the changes yourself, like any other
change.
The platform deploys from Git pushes as the repository's .mainsequence/workflows/*.yaml file says.
With tag_regex omitted, every push to the branch deploys; with a regular expression, a matching
tag deploys the commit it points at, whether that is the branch's latest commit or an older commit
on the branch. The Main Sequence platform
no longer provides tag names: release tags come from the repository's own CI.
Application operations has
an example workflow.