Skip to content

New: AI agent integration via Model Context Protocol (MCP).Learn more

Getting started

This walk-through installs the authoring layer in an existing Evolve project. By the end you can open a page on your storefront, see the editor chrome, and have a chat session that generates and pushes a page to your CMS.

For a production-grade rollout — including the CMS-service module, the gateway plugin, and the storefront wiring in full — follow the installation guide; this page is the quick tour.

  • An Evolve project with a working storefront and federation gateway
  • A CMS service (Contentful) mounted on the gateway, with the authoring module composed in (ContentfulAuthoringModule from @evolve-framework/contentful/authoring)
  • One AI provider you plan to use for chat (Anthropic, OpenAI, or Google) and an API key for it
  • An OAuth app on your CMS provider so editors can sign in with their own permissions (Contentful: Developers > OAuth applications)

Install the four base packages in your monorepo. Pick the workspace that runs your authoring backend (a service following the standard Evolve service anatomy) and the one that runs your storefront (typically a Next.js app):

Terminal window
# In the authoring backend service
pnpm add @evolve-framework/authoring-core \
@evolve-framework/authoring-server \
@evolve-framework/authoring-ai-providers
# In the storefront (Next.js)
pnpm add @evolve-framework/authoring-core \
@evolve-framework/authoring-chrome

For headless workflows, also add @evolve-framework/authoring-workflows in the backend service. You can defer that until you actually want the addon: the chrome works without it.

You need at least:

  • AI provider: CHAT_PROVIDER (one of anthropic, openai, google), CHAT_MODEL (a model id), and the matching API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY). PROVIDER sets a default for all modalities at once.
  • CMS access: the editor’s own OAuth token covers reads and writes; the OAuth app credentials (CONTENTFUL_OAUTH_CLIENT_ID, CONTENTFUL_OAUTH_CLIENT_SECRET) live on the CMS service, which exchanges auth codes on the authoring server’s behalf
  • Internal secret: AUTHORING_INTERNAL_SECRET, a shared secret between the authoring service and the CMS service’s internal OAuth endpoints
  • Session signing: two 32-byte keys, SESSION_ENCRYPT_KEY and SESSION_SIGN_KEY, used to encrypt and sign the editor session cookie. Generate with openssl rand -hex 16. These must be identical across the authoring service, the storefront, and the gateway — all three read the same cookie.

Optional, for AI image and video generation: IMAGE_PROVIDER, IMAGE_MODEL, VIDEO_PROVIDER, VIDEO_MODEL (falling back to PROVIDER when unset).

@evolve-framework/authoring-core/codegen is a graphql-codegen plugin that walks the fragments in your project’s documents and emits a runtime schema registry the AI uses to know which fields exist on which content types.

Add it to your storefront’s codegen.ts:

import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {
schema: "../../backend/services/graphql-gateway/supergraph.generated.graphql",
documents: ["src/**/*.{ts,tsx}"],
generates: {
"./src/authoring/generated/schema-registry.generated.ts": {
plugins: ["@evolve-framework/authoring-core/codegen"],
},
},
};
export default config;

Also generate the same registry into your authoring backend service so both sides describe the same shapes. The simplest path is a second generates entry in the same codegen config that writes to the service’s source tree:

"../../backend/services/authoring/src/generated/schema-registry.generated.ts": {
plugins: ["@evolve-framework/authoring-core/codegen"],
},

Run pnpm codegen to produce the files. They are deterministic from a single codegen run, so the two outputs cannot drift.

If you plan to expose tools over the MCP endpoint (opt-in but recommended), add a second pair of codegen configs in the authoring service that compile an authoring-operations.graphql file into ready-to-mount MCP tools through @evolve-framework/mcp-core. The MCP page walks through the full pipeline.

The authoring service follows the standard Evolve service anatomy: a module whose getHttpConfig() mounts the editor routes, booted with createDomainService. Register the schema registry at startup, then mount the editor with mountAuthoringEditor:

src/module.ts
import {
aiProviderConfigFromEnv,
} from "@evolve-framework/authoring-ai-providers";
import {
mountAuthoringEditor,
registerAuthoringCors,
} from "@evolve-framework/authoring-server";
import { AbstractModule, type HttpConfig } from "@evolve-framework/core";
import { config } from "./config.ts";
export class AuthoringServiceModule extends AbstractModule {
getHttpConfig(): HttpConfig {
return {
routes: (parent): void => {
parent.register(async (app) => {
const applyCorsHeadersRaw = registerAuthoringCors(app, {
allowedOrigins: config.ALLOWED_ORIGINS.split(","),
});
await mountAuthoringEditor(app, {
signerKeys: {
SESSION_ENCRYPT_KEY: config.SESSION_ENCRYPT_KEY,
SESSION_SIGN_KEY: config.SESSION_SIGN_KEY,
JWT_ISSUER: config.JWT_ISSUER,
JWT_AUDIENCE: config.JWT_AUDIENCE,
},
gatewayUrl: config.GATEWAY_URL,
ai: aiProviderConfigFromEnv({
PROVIDER: config.PROVIDER,
CHAT_PROVIDER: config.CHAT_PROVIDER,
CHAT_MODEL: config.CHAT_MODEL,
ANTHROPIC_API_KEY: config.ANTHROPIC_API_KEY,
OPENAI_API_KEY: config.OPENAI_API_KEY,
GOOGLE_API_KEY: config.GOOGLE_API_KEY,
}),
contentful: {
spaceId: config.CONTENTFUL_SPACE_ID,
environmentId: config.CONTENTFUL_ENVIRONMENT,
locale: config.CONTENTFUL_LOCALE,
},
applyCorsHeadersRaw,
});
});
},
};
}
}
src/server.ts
import { createDomainService } from "@evolve-framework/core";
import { setSchemaRegistry } from "@evolve-framework/authoring-core/codegen";
import { authoringSchemaRegistry } from "./generated/schema-registry.generated.ts";
import { AuthoringServiceModule } from "./module.ts";
import { config } from "./config.ts";
export const startServer = async (): Promise<void> => {
setSchemaRegistry(authoringSchemaRegistry);
const service = await createDomainService({
name: config.COMPONENT_NAME,
module: new AuthoringServiceModule(),
http: {
address: { host: config.HTTP_HOST, port: config.HTTP_PORT },
// Chat bodies carry inline image uploads; raise the 1MB default.
bodyLimit: 25 * 1024 * 1024,
},
});
await service.start();
};

To enable the bundled OAuth + login UI, pass an auth block with the OAuth client id, the CMS service’s internal URL, the shared internal secret, and your cookie domain. Make sure your CORS allowlist includes the storefront origin and that cookies are set on a domain the storefront can read.

The reference implementation lives in backend/services/authoring/src/module.ts — including the streaming asset-upload content-type parser and the optional workflows mount.

In your Next.js layout (a server component), wrap the storefront content with AuthoringServerMount from the /server sub-export. It reads the editor cookie itself and short-circuits to children for anonymous shoppers — they pay zero chrome cost. You provide the storefront-specific slots: the CMS config, a page loader, slug routing, and a bindings render-prop that bridges the chrome to your project’s router and components:

import { AuthoringServerMount } from "@evolve-framework/authoring-chrome/server";
import type { ReactNode } from "react";
import { StorefrontAuthoringBindings } from "#authoring/host-bindings.tsx";
import { loadAuthoringPageByPath } from "#authoring/preview/load-page.ts";
import { storefrontSlugRouting } from "#authoring/routing.ts";
export async function AuthoringChromeMount({
locale,
children,
}: {
locale: string;
children: ReactNode;
}) {
return (
<AuthoringServerMount
locale={locale}
loadPage={loadAuthoringPageByPath}
slugRouting={storefrontSlugRouting}
cmsConfig={{
spaceId: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT ?? "master",
}}
bindings={(shell) => (
<StorefrontAuthoringBindings
authoringServerUrl={env.AUTHORING_SERVER_URL}
gatewayUrl={env.CLIENT_API_GATEWAY_URL}
currentLocale={locale}
storeKey={storeConfig.storeKey}
storeCurrency={storeConfig.currency}
>
{shell}
</StorefrontAuthoringBindings>
)}
>
{children}
</AuthoringServerMount>
);
}

The bindings provider is where your project’s routing, block renderer, and locale logic plug in — see frontend/site/src/authoring/host-bindings.tsx in the platform for the reference implementation.

The chrome ships its own stylesheet baked into the JS chunk, so there is no separate CSS file to import or serve. Shoppers never download the chrome: the editor surface is loaded lazily after the session resolves.

Restart your services and visit any page on your storefront while signed in. You should see:

  1. A profile badge in the top-right corner showing your editor name
  2. A floating prompt bar at the bottom of the page, with quick actions to open the chat panel or the page browser
  3. A drawer on the left listing the pages, entries, and assets in your CMS

Open the chat panel, type make this page about summer barbecues, and watch the page update inline. When you’re happy with the draft, click Push to write the result to your CMS.

If you see the chrome but the AI doesn’t respond, check the network tab for the /chat request and confirm your AI provider API key is reaching the authoring server. The /ai/info endpoint (visible in your Swagger docs) reports which provider and model are active.

  • Packages: what each @evolve-framework/* package does in detail, with API surfaces
  • MCP endpoint: serve the same tools to headless AI clients with a project-owned authoring-operations.graphql
  • Workflows (optional): the opt-in addon for headless, long-running content jobs
  • The package READMEs on npm have per-package configuration tables and security notes