@typeroll/mcp-server 0.45.101 → 0.45.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundled-content.js +11 -11
- package/dist/server.js +2 -0
- package/dist/tools/content-types.js +2 -2
- package/dist/tools/deploy.js +1 -1
- package/dist/tools/domain.js +13 -6
- package/dist/tools/media.js +1 -1
- package/dist/tools/migration.js +1 -1
- package/dist/tools/page-templates.js +2 -2
- package/dist/tools/readiness.js +13 -0
- package/dist/tools/settings.js +3 -2
- package/dist/version.js +1 -1
- package/dist/workspace.js +3 -3
- package/package.json +2 -1
- package/skills/tr-brand.md +22 -8
- package/skills/tr-migrate-astro.md +46 -0
- package/skills/tr-migrate-multisite.md +46 -0
- package/skills/tr-migrate-wp.md +40 -2
- package/skills/tr-new-site.md +40 -3
- package/skills/tr-redesign-branch.md +35 -1
- package/skills/tr-seo.md +225 -201
- package/skills/tr-starter-design.md +13 -0
- package/workspace-templates/qa/checklist.md +16 -0
- package/workspace-templates/workflows/interview.md +13 -4
- package/workspace-templates/workflows/new-site.md +10 -1
package/dist/server.js
CHANGED
|
@@ -24,6 +24,7 @@ import { formTools } from './tools/forms.js';
|
|
|
24
24
|
import { emailTools } from './tools/email.js';
|
|
25
25
|
import { searchTools } from './tools/search.js';
|
|
26
26
|
import { bulkTools } from './tools/bulk.js';
|
|
27
|
+
import { readinessTools } from './tools/readiness.js';
|
|
27
28
|
import { versionTools } from './tools/versions.js';
|
|
28
29
|
import { deployTools } from './tools/deploy.js';
|
|
29
30
|
import { previewTools } from './tools/preview.js';
|
|
@@ -226,6 +227,7 @@ export function buildServer(options) {
|
|
|
226
227
|
...developerExtensionTools,
|
|
227
228
|
...searchTools,
|
|
228
229
|
...bulkTools,
|
|
230
|
+
...readinessTools,
|
|
229
231
|
...versionTools,
|
|
230
232
|
...deployTools,
|
|
231
233
|
...previewTools,
|
|
@@ -5,9 +5,9 @@ const fields = {
|
|
|
5
5
|
label_singular: z.string().optional(), label_plural: z.string().optional(), fields: schema.optional(),
|
|
6
6
|
page_field_rules: z.record(z.string(), z.object({ label: z.string().optional(), writable_by: z.array(z.enum(['portal', 'owner', 'agent', 'app', 'import'])) })).optional(),
|
|
7
7
|
allowed_templates: z.array(z.string()).nullable().optional().describe('Allowed Page template IDs, including the default. Null allows all compatible templates; an empty list permits no template.'),
|
|
8
|
-
route_template: z.string().optional(), template: z.string().optional(), schema_type: z.string().optional(),
|
|
8
|
+
route_template: z.string().optional(), template: z.string().optional(), schema_type: z.string().optional().describe('Schema.org type each Page of this type represents, such as BlogPosting, Product or SoftwareApplication.'),
|
|
9
9
|
schema_field_mode: z.enum(['all', 'mapped']).optional().describe('Use mapped to emit only explicitly mapped fields. Resolve typed references in Page json_ld for complex graphs.'),
|
|
10
|
-
schema_field_map: z.record(z.string(), z.string()).optional(),
|
|
10
|
+
schema_field_map: z.record(z.string(), z.string()).optional().describe('Field name to a direct Schema.org property, e.g. {"cover":"image","os":"operatingSystem"}. For Product and SoftwareApplication types, price, priceCurrency, availability and priceValidUntil are published inside offers (an Offer), ratingValue with ratingCount or reviewCount inside aggregateRating, and a brand name as a Brand. A field mapped to image is also the share image.'),
|
|
11
11
|
icon: z.string().optional(), sort_field: z.string().optional(), sort_dir: z.enum(['asc', 'desc']).optional(),
|
|
12
12
|
facets: z.array(z.record(z.string(), z.unknown())).optional(), facet_combinations: z.array(z.tuple([z.string(), z.string()])).optional().describe('Pairs of configured facet field names, not values. Use editable Pages for a curated subset of combinations.'),
|
|
13
13
|
};
|
package/dist/tools/deploy.js
CHANGED
|
@@ -12,7 +12,7 @@ export const deployTools = [
|
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
name: 'trigger_deploy',
|
|
15
|
-
description: "Enqueue a deploy (static-page build → hosting) of the currently active version. Returns a job_id immediately; poll get_deploy_status until the status leaves queued/running. NOT for previewing iterative design/content edits — that's what get_preview_link is for (it renders live from the DB with no build). Reserve trigger_deploy for publishing, a stakeholder link to the compiled site, or pre-merge validation. Deploys build SAVED content only — unsaved drafts (working copies) are excluded, so commit_working_copy (or save:true on your writes) before deploying, or your changes won't ship. Customer publishing freezes a consistent saved source, then resumes source/Git/build-dispatch checkpoints without recapturing later edits. GitHub or Cloudflare performs rendering and media work. With dry_run:true, customer publishing validates frozen source without a Git push or external build; it does not prove an Astro build succeeds. Other self-hosted adapters may build locally without upload. Check the reported phase. All sites in an organization share one build engine: publish one site at a time, starting the next only when the previous job has finished and read_organization_build_engine shows no active_jobs.",
|
|
15
|
+
description: "Enqueue a deploy (static-page build → hosting) of the currently active version. Returns a job_id immediately; poll get_deploy_status until the status leaves queued/running. NOT for previewing iterative design/content edits — that's what get_preview_link is for (it renders live from the DB with no build). Reserve trigger_deploy for publishing, a stakeholder link to the compiled site, or pre-merge validation. Deploys build SAVED content only — unsaved drafts (working copies) are excluded, so commit_working_copy (or save:true on your writes) before deploying, or your changes won't ship. Customer publishing freezes a consistent saved source, then resumes source/Git/build-dispatch checkpoints without recapturing later edits. GitHub or Cloudflare performs rendering and media work. With dry_run:true, customer publishing validates frozen source without a Git push or external build; it does not prove an Astro build succeeds. Other self-hosted adapters may build locally without upload. Check the reported phase. All sites in an organization share one build engine: publish one site at a time, starting the next only when the previous job has finished and read_organization_build_engine shows no active_jobs. A domain switch waiting for DNS approval (awaiting_approval) does not block other sites.",
|
|
16
16
|
inputSchema: {
|
|
17
17
|
environment: z.enum(['production', 'staging']).optional(),
|
|
18
18
|
allow_unsaved: z.boolean().optional().describe('Explicitly publish saved content while leaving the reported unsaved Pages/partials untouched. Only after owner confirmation.'),
|
package/dist/tools/domain.js
CHANGED
|
@@ -24,7 +24,7 @@ export const domainTools = [
|
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
name: 'guide_publishing_domain_change',
|
|
27
|
-
description: 'Guide a site domain move. Start with check: returns hosting, zone, nameserver, certificate, permission, publishing, redirect capacity and candidate checks, current phase, next action and an explicit switch summary. Saving future hosts is inert. Prepare republishes the last public content on main with the new origins, replacing the current site before DNS changes; newer unpublished CMS edits are excluded. Later normal publications retain the new origins. Approve changes DNS for the same main artifact without another build. Adding redirect hosts preserves the main artifact. Approve requires explicit user approval of the switch summary and any validation interruption. Automatic DNS is journaled and rolled back after a failed check or the agreed deadline; later external edits require manual attention. Rollback requests restoration through the serialized publication chain. External DNS remains the caller’s responsibility.',
|
|
27
|
+
description: 'Guide a site domain move. Start with check: returns hosting, zone, nameserver, certificate, permission, publishing, redirect capacity, candidate_noindex and candidate checks, current phase, next action and an explicit switch summary. candidate_noindex blocks prepare and approve while site settings, the last publication or the candidate tell search engines not to index or follow the site (next_action resolve_noindex): the temporary address is already blocked from indexing by Typeroll, so prepare with confirm_disable_noindex: true to turn it off and build an indexable candidate in one step, or pass allow_noindex_launch: true to launch hidden on purpose. Saving future hosts is inert. Prepare republishes the last public content on main with the new origins, replacing the current site before DNS changes; newer unpublished CMS edits are excluded. Later normal publications retain the new origins. Approve changes DNS for the same main artifact without another build. Adding redirect hosts preserves the main artifact. Approve requires explicit user approval of the switch summary and any validation interruption. Automatic DNS is journaled and rolled back after a failed check or the agreed deadline; later external edits require manual attention. Rollback requests restoration through the serialized publication chain. External DNS remains the caller’s responsibility.',
|
|
28
28
|
inputSchema: {
|
|
29
29
|
action: z.enum(['check', 'preview', 'save', 'prepare', 'approve', 'rollback']),
|
|
30
30
|
revision: z.string().optional(), candidate_id: z.string().optional(),
|
|
@@ -32,6 +32,8 @@ export const domainTools = [
|
|
|
32
32
|
redirect_hosts: z.array(z.string()).max(4).optional(), dns_mode: z.enum(['automatic', 'external']).optional(),
|
|
33
33
|
confirm_rebuild: z.boolean().optional(), accept_validation_window: z.boolean().optional(),
|
|
34
34
|
validation_timeout_minutes: z.number().int().min(5).max(45).optional(),
|
|
35
|
+
confirm_disable_noindex: z.boolean().optional().describe('For prepare: Turn off sitewide_noindex and sitewide_nofollow (and remove noindex from a robots meta tag in scripts_head) in the site settings and build the candidate indexable for the new website host in the same step, without a separate publication. Use this after the user confirms the site should be found by search engines; it satisfies the candidate_noindex blocker.'),
|
|
36
|
+
allow_noindex_launch: z.boolean().optional().describe('For prepare and approve: set only when Search engines are told to skip this site (sitewide_noindex, sitewide_nofollow or a robots noindex meta in scripts_head) and you deliberately want to launch it hidden. Without it, a site with authored noindex is rejected with candidate_noindex. Typeroll already blocks indexing of the temporary address, so sitewide_noindex is not needed during migration; normally turn it off and publish instead.'),
|
|
35
37
|
},
|
|
36
38
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.post(siteId, 'publishing/domain-guide', args))),
|
|
37
39
|
},
|
|
@@ -112,7 +114,7 @@ export const domainTools = [
|
|
|
112
114
|
},
|
|
113
115
|
{
|
|
114
116
|
name: 'read_organization_build_engine', noSite: true,
|
|
115
|
-
description: 'Read the selected provider, saved Cloudflare and GitHub build engines, and active jobs for the organization (each with job_id, site_id, kind and status: queued, running, or publishing while a built publication uploads and verifies; image preparation jobs are media-…). A job stays listed until its deploy reaches a final status. Requires an organization API key; REST: GET /api/v1/publishing/builds. No provider credentials are returned.',
|
|
117
|
+
description: 'Read the selected provider, saved Cloudflare and GitHub build engines, and active jobs for the organization (each with job_id, site_id, kind and status: queued, running, or publishing while a built publication uploads and verifies; image preparation jobs are media-…). A job stays listed until its deploy reaches a final status, except a domain switch that is built and verified and only waits for DNS approval: it is listed under awaiting_approval, uses no build capacity, and does not block other sites from publishing. Requires an organization API key; REST: GET /api/v1/publishing/builds. No provider credentials are returned.',
|
|
116
118
|
inputSchema: {},
|
|
117
119
|
handler: withErrorBoundary(async (_args, { client }) => ok(await client.rootGet('publishing/builds'))),
|
|
118
120
|
},
|
|
@@ -184,20 +186,24 @@ export const domainTools = [
|
|
|
184
186
|
{
|
|
185
187
|
name: 'prepare_publishing_domain_change',
|
|
186
188
|
description: 'Publish for the new domain shortly before changing DNS, using only the last successfully published snapshot. This replaces main at the current or temporary address; it does not create a separate preview branch. Publish intended edits normally at the current or temporary address first. Newer unpublished CMS changes are excluded. Normal publication remains available and keeps these embedded domain addresses while DNS is pending. It refreshes the receipt for the same main site; no second preparation is needed. Requires the current domain revision. Poll read_publishing_domains for certificate and DNS requirements, then explicitly approve the verified cutover.',
|
|
187
|
-
inputSchema: {
|
|
189
|
+
inputSchema: {
|
|
190
|
+
revision: z.string(),
|
|
191
|
+
confirm_disable_noindex: z.boolean().optional().describe('Turn off sitewide_noindex and sitewide_nofollow (and remove noindex from a robots meta tag in scripts_head) in the site settings and build the candidate indexable for the new website host in the same step, without a separate publication. Use this after the user confirms the site should be found by search engines; it satisfies the candidate_noindex blocker.'),
|
|
192
|
+
allow_noindex_launch: z.boolean().optional().describe('Set only when Search engines are told to skip this site (sitewide_noindex, sitewide_nofollow or a robots noindex meta in scripts_head) and you deliberately want to launch it hidden. Without it, a site with authored noindex is rejected with candidate_noindex. Typeroll already blocks indexing of the temporary address, so sitewide_noindex is not needed during migration; normally turn it off and publish instead.'),
|
|
193
|
+
},
|
|
188
194
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.post(siteId, 'publishing/prepare', args))),
|
|
189
195
|
},
|
|
190
196
|
{
|
|
191
197
|
name: 'read_organization_media_migration',
|
|
192
198
|
noSite: true,
|
|
193
|
-
description: 'Read progress and actionable failures while original media moves to the organization Cloudflare account. Requires an organization API key.',
|
|
199
|
+
description: 'Read progress and actionable failures while original media moves to the organization Cloudflare account: overall state with updated_at, each site (state, counts, error, updated_at) and failed_media (site_id, media_id, filename, source_url, code, message, at) for files that could not be copied and were set aside. Fix or delete those files, then retry_organization_media_migration. Requires an organization API key.',
|
|
194
200
|
inputSchema: {},
|
|
195
201
|
handler: withErrorBoundary(async (_args, { client }) => ok(await client.rootGet('publishing/media-migration'))),
|
|
196
202
|
},
|
|
197
203
|
{
|
|
198
204
|
name: 'retry_organization_media_migration',
|
|
199
205
|
noSite: true,
|
|
200
|
-
description: 'Resume verified media migration after fixing account or R2 setup. Restarts only failed site migrations and stopped jobs; completed sites are not touched. Source files remain readable; pointer changes require matching hashes. Set prepare_transfer to set up or recheck the transfer worker in the organization Cloudflare account. Requires an organization API key.',
|
|
206
|
+
description: 'Resume verified media migration after fixing account or R2 setup. Restarts only failed site migrations and stopped jobs, giving set-aside files fresh attempts; completed sites are not touched. Source files remain readable; pointer changes require matching hashes. Set prepare_transfer to set up or recheck the transfer worker in the organization Cloudflare account. Requires an organization API key.',
|
|
201
207
|
inputSchema: { prepare_transfer: z.boolean().optional() },
|
|
202
208
|
handler: withErrorBoundary(async (args, { client }) => ok(await client.rootPost('publishing/media-migration', args.prepare_transfer ? { action: 'prepare_transfer', recheck: true } : {}))),
|
|
203
209
|
},
|
|
@@ -214,12 +220,13 @@ export const domainTools = [
|
|
|
214
220
|
revision: z.string(), candidate_id: z.string(),
|
|
215
221
|
validation_timeout_minutes: z.number().int().min(5).max(45).optional(),
|
|
216
222
|
accept_validation_window: z.boolean().optional().describe('Accept the interruption until Cloudflare validates the website host after its single reviewed record changes. Only valid when preparation.validation_blocker.resolution is accept_validation_window.'),
|
|
223
|
+
allow_noindex_launch: z.boolean().optional().describe('Set only when Search engines are told to skip this site (sitewide_noindex, sitewide_nofollow or a robots noindex meta in scripts_head) and you deliberately want to launch it hidden. Without it, a site with authored noindex is rejected with candidate_noindex. Typeroll already blocks indexing of the temporary address, so sitewide_noindex is not needed during migration; normally turn it off and publish instead.'),
|
|
217
224
|
},
|
|
218
225
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.post(siteId, 'publishing/cutover', args))),
|
|
219
226
|
},
|
|
220
227
|
{
|
|
221
228
|
name: 'read_publishing_domains',
|
|
222
|
-
description: 'Read the desired and active website, redirect and media hosts, retained media aliases, DNS management mode and domain configuration revision. host_checks lists each future host with its Cloudflare zone and structured blockers and warnings (code, host, message), for example apex_zone_required (an apex such as example.com needs an active Cloudflare zone in the hosting account), zone_not_active (the zone is pending until its nameservers change) or redirect_zone_required. Resolve blockers before preparing. preparation and redirect_preparations hold DNS records, the zone redirect rule and certificate state after preparation. Saving intent does not switch traffic. published identifies the addresses embedded in main, separately from active (verified traffic). Saving desired hosts is inert. prepare_publishing_domain_change explicitly replaces main with the new origins; later normal publication retains them and refreshes the DNS approval receipt. candidate.sitewide_noindex describes the frozen candidate, not current settings
|
|
229
|
+
description: 'Read the desired and active website, redirect and media hosts, retained media aliases, DNS management mode and domain configuration revision. host_checks lists each future host with its Cloudflare zone and structured blockers and warnings (code, host, message), for example apex_zone_required (an apex such as example.com needs an active Cloudflare zone in the hosting account), zone_not_active (the zone is pending until its nameservers change) or redirect_zone_required. Resolve blockers before preparing. preparation and redirect_preparations hold DNS records, the zone redirect rule and certificate state after preparation. Saving intent does not switch traffic. published identifies the addresses embedded in main, separately from active (verified traffic). Saving desired hosts is inert. prepare_publishing_domain_change explicitly replaces main with the new origins; later normal publication retains them and refreshes the DNS approval receipt. candidate.sitewide_noindex describes the frozen candidate, not current settings. Prepare and approve reject an authored noindex launch with candidate_noindex unless allow_noindex_launch is true.',
|
|
223
230
|
inputSchema: {},
|
|
224
231
|
handler: withErrorBoundary(async (_args, { client, siteId }) => ok(await client.get(siteId, 'publishing/domains'))),
|
|
225
232
|
},
|
package/dist/tools/media.js
CHANGED
|
@@ -79,7 +79,7 @@ export const mediaTools = [
|
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
name: 'upload_media_from_url',
|
|
82
|
-
description: 'Fetch an image (or PDF) from any public URL and push it to the site\'s media library in one call. The
|
|
82
|
+
description: 'Fetch an image (or PDF) from any public URL and push it to the site\'s media library in one call. The organization\'s media transfer Worker downloads the source from verified public HTTP(S) destinations straight into the organization\'s storage; private addresses and unsafe redirects are refused. Integrity-safe: the bytes are streamed, never transcribed as base64 through the model, so (unlike a large `upload_media_inline` payload) they can\'t be silently corrupted in transit. Returns { media_id, cdn_url, filename, finalize }. Each upload queues image preparation (finalize.preparation): one job per site on the organization\'s shared build engine, started once uploads have been quiet for 30 s (at most 5 min after the first), so a series of uploads shares one job. get_media_preparation returns its build_job_id; let it finish, like any other active build, before publishing another site of the organization.',
|
|
83
83
|
inputSchema: {
|
|
84
84
|
source_url: z.string().url().describe('Public URL to download the image from.'),
|
|
85
85
|
filename: z.string().optional().describe('Override the filename used on R2. Defaults to the last path segment of source_url. When its extension does not match the content type, the type\'s extension is appended; the response returns the stored filename.'),
|
package/dist/tools/migration.js
CHANGED
|
@@ -182,7 +182,7 @@ export const migrationTools = [
|
|
|
182
182
|
},
|
|
183
183
|
{
|
|
184
184
|
name: 'verify_migration_urls',
|
|
185
|
-
description: "Inventories over 500 URLs: one call checks at most 500 and stores no launch evidence (the response then has evidence_stored: false and next_step); run start_workflow with type url_parity to check every URL and record the evidence. Pre-cutover parity check: actually REQUEST every observed inventory path against the new site and report what it answers. Slash-equivalent paths share one inventory row but remain separate checks through `observed_paths`, so `summary.checked` can exceed the normalized inventory count. list_migration_urls tells you what the data says; this tells you what the server does — a redirect pointing at an unpublished page, a typo'd path, a missing slash variant, or a redirect loop all read as \"handled\" in coverage and as a 404 to Googlebot. Runs against the site's fallback subdomain by default. The response is compact by default: the full summary plus only `missing`, `broken_redirect`, and `error` rows; successful rows are counted but omitted. Pass `verdicts` for an exact result filter or `include_successes: true` for every row. Canonical trailing-slash normalization counts as `ok`, not `ok_redirect`. Also stamps verified/last_checked on redirect rules it exercised. Deploy before running this — it tests the DEPLOYED site, not your drafts.",
|
|
185
|
+
description: "Inventories over 500 URLs: one call checks at most 500 and stores no launch evidence (the response then has evidence_stored: false and next_step); run start_workflow with type url_parity to check every URL and record the evidence. Pre-cutover parity check: actually REQUEST every observed inventory path against the new site and report what it answers. Slash-equivalent paths share one inventory row but remain separate checks through `observed_paths`, so `summary.checked` can exceed the normalized inventory count. list_migration_urls tells you what the data says; this tells you what the server does — a redirect pointing at an unpublished page, a typo'd path, a missing slash variant, or a redirect loop all read as \"handled\" in coverage and as a 404 to Googlebot. Runs against the site's fallback subdomain by default. The response is compact by default: the full summary plus only `missing`, `broken_redirect`, and `error` rows; successful rows are counted but omitted. Pass `verdicts` for an exact result filter or `include_successes: true` for every row. Canonical trailing-slash normalization counts as `ok`, not `ok_redirect`. A query-string rule (published as an inline script, also from 404.html) is followed and counts as `ok_redirect` with `client_redirect: true`. Also stamps verified/last_checked on redirect rules it exercised. Deploy before running this — it tests the DEPLOYED site, not your drafts.",
|
|
186
186
|
inputSchema: {
|
|
187
187
|
target_origin: z
|
|
188
188
|
.string()
|
|
@@ -5,8 +5,8 @@ const fields = {
|
|
|
5
5
|
blocks: z.array(z.any()).optional(), status: z.enum(['draft', 'published']).optional(),
|
|
6
6
|
};
|
|
7
7
|
export const pageTemplateTools = [
|
|
8
|
-
{ name: 'get_composition_starter', description: 'Read one native editable starting block tree without saving. Choose a Page template, header/footer or archive. Replace sample navigation and map profile field_list fields before saving with the normal Page/template/partial tools. Existing content is never replaced by this read.', inputSchema: {
|
|
9
|
-
kind: z.enum(['header', 'footer', 'archive', 'custom', 'article', 'blog', 'checklist', 'team', 'events', 'products', 'profile', 'landing']),
|
|
8
|
+
{ name: 'get_composition_starter', description: 'Read one native editable starting block tree without saving. Choose a Page template, header/footer or archive, or a Page body for a common marketing page: feature_page, comparison_page (with a comparison table), integration_page, pricing_page (plan table and plan comparison) or use_case_page. Page bodies go into create_page/update_page blocks; replace every [bracketed placeholder] and remove sections you have no real content for. Replace sample navigation and map profile field_list fields before saving with the normal Page/template/partial tools. Existing content is never replaced by this read.', inputSchema: {
|
|
9
|
+
kind: z.enum(['header', 'footer', 'archive', 'custom', 'article', 'blog', 'checklist', 'team', 'events', 'products', 'profile', 'landing', 'feature_page', 'comparison_page', 'integration_page', 'pricing_page', 'use_case_page']),
|
|
10
10
|
content_type: z.string().optional(), title: z.string().optional(), version: versionParam,
|
|
11
11
|
}, handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.get(siteId, 'composition-starters', args))) },
|
|
12
12
|
{ name: 'list_page_templates', description: 'List reusable Page templates in the selected version, including inherited templates.', inputSchema: { version: versionParam },
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Site launch readiness. One read-only report of what the site needs to be
|
|
2
|
+
// found, shared and understood; every item has a fix and a guide link.
|
|
3
|
+
import { ok, withErrorBoundary, versionParam } from './helpers.js';
|
|
4
|
+
export const readinessTools = [
|
|
5
|
+
{
|
|
6
|
+
name: 'read_site_readiness',
|
|
7
|
+
description: 'Read-only launch readiness report for the selected site and version: share image, home page description, organization and profile links, favicon, analytics and consent, Search Console, privacy and contact pages, indexable and orphan pages, duplicate titles and descriptions, H1 per page, description length, duplicate font loads, custom CSS that overrides the theme, conflicts between the site\'s ai_crawler_policy and the Cloudflare zone\'s AI crawler settings, and page JSON-LD problems. ' +
|
|
8
|
+
'Returns {site_id, checked_at, summary:{ok, suggestions, warnings}, items:[{id, status, message, fix, guide, details?}]}. Items are warnings or suggestions, never blockers; a missing default share image is a suggestion. ' +
|
|
9
|
+
'Run it before launch and after larger changes, then fix or record each open item. It reads the Cloudflare zone only when the existing connection already may; it never changes zone settings or content.',
|
|
10
|
+
inputSchema: { version: versionParam },
|
|
11
|
+
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.get(siteId, 'readiness', args.version ? { version: args.version } : undefined))),
|
|
12
|
+
},
|
|
13
|
+
];
|
package/dist/tools/settings.js
CHANGED
|
@@ -13,7 +13,7 @@ function v(version) {
|
|
|
13
13
|
export const settingsTools = [
|
|
14
14
|
{
|
|
15
15
|
name: 'lint_design_tokens',
|
|
16
|
-
description: 'Read-only design audit of saved Pages, partials, page templates, custom block types, styles and custom CSS in the selected Version. Lists hardcoded colours, typography and spacing with exact paths and token suggestions. exact_replacements is a dry-run plan for unambiguous direct block hex matches; never apply ambiguous roles or CSS automatically. Does not block launch. Unsaved working copies are excluded.',
|
|
16
|
+
description: 'Read-only design audit of saved Pages, partials, page templates, custom block types, styles and custom CSS in the selected Version. Lists hardcoded colours, typography and spacing with exact paths and token suggestions. exact_replacements is a dry-run plan for unambiguous direct block hex matches; never apply ambiguous roles or CSS automatically. css_findings lists custom CSS that works against the theme (heavy !important use, fonts not in the theme, theme fonts loaded twice, a first-section background image set in CSS, rules on platform block classes), each with the Style, token or block setting that replaces it. Does not block launch. Unsaved working copies are excluded.',
|
|
17
17
|
inputSchema: { version: versionParam },
|
|
18
18
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.get(siteId, 'design-token-audit', v(args.version)))),
|
|
19
19
|
},
|
|
@@ -28,7 +28,7 @@ export const settingsTools = [
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
name: 'read_site_settings',
|
|
31
|
-
description: "Read every site setting: name, tagline, logo, favicon/app icons, colors, fonts, contact info, social links, URL trailing-slash policy, iframe host allowlist, default SEO suffix/description, default_og_image, twitter_handle, organization (JSON-LD), language, robots_txt, image_sizes_default, cookie_consent, plus the scriptable surfaces scripts_head, scripts_body_end, and custom_css, form_submission_retention_days (days completed form submissions are kept before deletion; null = kept until deleted; site-wide, not per version), `urls` (including `urls.staging`, the site's staging_url) and `render` (the site's platform render version, the latest version and what upgrading would change). Pass `version` to read a branch's settings (with copy-on-write chain-fallback to main for fields the branch hasn't overridden).",
|
|
31
|
+
description: "Read every site setting: name, tagline, logo, favicon/app icons, colors, fonts, contact info, social links, URL trailing-slash policy, iframe host allowlist, default SEO suffix/description, default_og_image, twitter_handle, organization (JSON-LD), language, robots_txt, ai_crawler_policy, image_sizes_default, cookie_consent, plus the scriptable surfaces scripts_head, scripts_body_end, and custom_css, form_submission_retention_days (days completed form submissions are kept before deletion; null = kept until deleted; site-wide, not per version), `urls` (including `urls.staging`, the site's staging_url) and `render` (the site's platform render version, the latest version and what upgrading would change). Pass `version` to read a branch's settings (with copy-on-write chain-fallback to main for fields the branch hasn't overridden).",
|
|
32
32
|
inputSchema: {
|
|
33
33
|
version: versionParam,
|
|
34
34
|
},
|
|
@@ -65,6 +65,7 @@ export const settingsTools = [
|
|
|
65
65
|
default_meta_description: z.string().optional().describe('Site-wide fallback <meta name="description">. Used when a page has no seo_description of its own; falls back further to the tagline when unset.'),
|
|
66
66
|
language: z.string().optional().describe('BCP-47 tag (e.g. "en", "sv", "en-GB"). Drives <html lang> on the rendered site.'),
|
|
67
67
|
robots_txt: z.string().optional(),
|
|
68
|
+
ai_crawler_policy: z.enum(['allow_all', 'no_training', 'block_all']).nullable().optional().describe('AI crawler preference, written as Cloudflare-compatible Content Signals in robots.txt and the Content-Signal response header. `allow_all` (default, writes nothing); `no_training` (search=yes, ai-input=yes, ai-train=no, and Disallow groups for training-only crawlers such as GPTBot, ClaudeBot and Google-Extended); `block_all` (also disallows AI search and user agents such as OAI-SearchBot, ChatGPT-User and PerplexityBot). Googlebot, Bingbot and Applebot are never blocked. An explicit group for an agent in robots_txt wins. A Cloudflare zone AI bot policy is enforced at the edge and can override this; `null` resets to the default.'),
|
|
68
69
|
sitewide_noindex: z.boolean().optional().describe('Exclude public pages from search indexing; following links remains a separate setting.'),
|
|
69
70
|
external_routes: z.array(z.object({ path: z.string().min(2).max(2048), owner: z.string().min(1).max(200) }).strict()).max(1000).optional().describe('Exact same-origin paths owned by an independent deployment, e.g. [{path:"/docs/",owner:"Documentation"}]. No wildcards. Does not configure hosting or verify remote availability. Verify each declared public URL separately. Local routes cannot be exempted.'),
|
|
70
71
|
seo_review: z.object({ forbidden_markers: z.array(z.string().min(1).max(500)).max(100).optional(), claims: z.array(z.object({ phrase: z.string().min(1).max(500), guidance: z.string().min(1).max(500) })).max(100).optional(), notes: z.array(z.string().min(1).max(500)).max(100).optional() }).optional().describe('Editorial review rules checked on all built pages, including unchanged pages. Warnings require human review; no copy is rewritten.'),
|
package/dist/version.js
CHANGED
package/dist/workspace.js
CHANGED
|
@@ -119,10 +119,10 @@ export const workspaceTemplates = {
|
|
|
119
119
|
'README.md': `# Website workspace\n\nKeep project intent here; edit the website through Typeroll CMS.\n\n1. Read AGENTS.md. For guidance, ask your agent to start or resume workflows/interview.md. Otherwise use workflows/new-site.md or workflows/migration.md. Save confirmed goals, rules and SEO strategy in brief/; record text and design/image choices separately for migrations.\n2. Connect hosted MCP at https://app.typeroll.com/api/mcp?tools=compact.\n3. Use an Organization key to create a site, or select an existing one.\n4. Record its IDs and working version in typeroll.json. No secrets belong there.\n5. Run npx @typeroll/mcp-server doctor . (inject TYPEROLL_API_KEY privately).\n\nStart guide: https://typeroll.com/docs/getting-started/agent-workspace/\n\nUpdate the generated scaffold with npx @typeroll/mcp-server init . --update.\nEdited files are preserved and reported for manual reconciliation. Optional local\nrecipes: init . --recipes. They are reference Markdown, not automatically loaded.\n`,
|
|
120
120
|
'brief/site.md': '# Site brief\n\n## Purpose and audience\n\n## Languages and scope\n\n## Primary user journeys\n\n## Content authority and sources\n\n## Editorial constraints and forbidden internal markers\n',
|
|
121
121
|
'brief/brand.md': '# Brand\n\n## Voice and tone\n\n## Visual direction and references\n\n## Typography, colors and image policy\n\n## Desktop and mobile navigation\n\nRecord intent here; current design tokens belong in CMS settings. Choose semantic palette roles and typography/spacing steps; record deliberate hardcoded exceptions in decisions.md.\n',
|
|
122
|
-
'brief/content-model.md': '# Content model\n\n## Page types and routes\n\n## Fields and references\n\n## Shared templates and partials\n\n## Taxonomy and navigation\n\nUse the current CMS schema. This document describes decisions, not a second schema store.\n',
|
|
122
|
+
'brief/content-model.md': '# Content model\n\n## Page plan\n\nThe pages the business needs, agreed before design work: home, about, services or products, pricing, contact, privacy policy and other legal pages, comparisons and articles as relevant. One row per page: title, path, purpose, primary search intent and status (planned, built, published).\n\n| Title | Path | Purpose | Search intent | Status |\n| --- | --- | --- | --- | --- |\n\n## Page types and routes\n\n## Fields and references\n\n## Shared templates and partials\n\n## Taxonomy and navigation\n\nUse the current CMS schema. This document describes decisions, not a second schema store.\n',
|
|
123
123
|
'brief/rules.md': "# Owner preferences and project rules\n\n## Confirmed rules\n\nRecord each rule, its scope, who approved it and the date. Separate confirmed rules from proposals.\n\n## Writing and factual accuracy\n\nVoice, terminology, prohibited claims, source requirements and legal copy that must not be rewritten.\n\n## Design and editing\n\nBrand constraints, accessibility, editable content, standard/custom blocks and documented HTML exceptions.\n\n## Operations and approval\n\nWho may approve content, publication, DNS changes and messages? Credentials stay outside this repository.\n\n## Exceptions and superseded rules\n\nLink decisions.md; do not silently override a confirmed rule. Project notes do not grant permissions beyond the user's authorization or account access.\n",
|
|
124
|
-
'brief/seo.md': "# SEO strategy\n\n## Business goals, audience, markets and languages\n\n## Search intent and topic coverage\n\nRecord research sources and dates. Distinguish evidence from assumptions; do not invent traffic or ranking forecasts.\n\n## Page roles and URL plan\n\nContent types, useful landing pages, taxonomy, internal links and routes to preserve or redirect. Avoid near-duplicate pages created only for keyword variants.\n\n## Indexing and canonical policy\n\nWhich pages should be indexable, public noindex, or private? Record deliberate canonical overrides and sitemap policy; actual values live in the CMS.\n\n## Editorial standards and structured data\n\nUnique useful copy, supported claims, descriptive titles and links, accessible image text, schema matching visible content. No fixed title length or word count is a hard SEO rule.\n\n## Measurement and review\n\nBaseline, analytics/consent decisions, measures of success and review dates. Never store credentials or private analytics exports here.\n",
|
|
125
|
-
'workflows/README.md': "# Project routines\n\nRead only the relevant routine and discover current tool schemas through MCP.\nProject files record intent; the CMS owns current content and configuration.\n\n| Task | Project context | On-demand recipe |\n| --- | --- | --- |\n| Guided start | workflows/interview.md, brief/interview.md | Ask one question at a time; resume confirmed answers |\n| New website | workflows/new-site.md, brief/site.md, brief/brand.md, brief/content-model.md | tr-new-site, tr-starter-design |\n| Migration | workflows/migration.md, brief/migration.md | tr-migration-evidence, then tr-migrate-wp or the matching source recipe |\n| Everyday editing | brief/rules.md, pages/ notes and the relevant Page | tr-edit-site |\n| Page notes and review | workflows/page-notes.md, pages/README.md, pages/index.md | Read before edits; update logs afterward |\n| SEO | brief/seo.md and current site settings | tr-seo |\n| New reusable block | brief/brand.md and recorded editing needs | tr-custom-blocks |\n| Verification and publication | qa/checklist.md, decisions.md | Current preview/deploy tools and publishing readiness |\n\nAdd project-specific routines here with inputs, steps, acceptance evidence and approval boundaries. Do not copy the complete Typeroll manual or private app guides. Discover enabled app guidance with read_app_documentation. Load a recipe when needed so instructions stay current and context stays small.\n",
|
|
124
|
+
'brief/seo.md': "# SEO strategy\n\n## Launch basics\n\nAsk the owner early; each item is optional. Record a skipped item here with the reason. Current values live in the CMS settings.\n\n- Default share image (`default_og_image`): chosen, uploaded or created with tr-imagegen; 1200×630 PNG or JPEG. Never a generated title card.\n- Organization (`organization`): business name and logo, and official profile URLs (`same_as`).\n- Page plan: agreed in brief/content-model.md, including privacy policy and contact pages.\n- Search Console: property type (Domain or URL prefix), verification method, who has access and the date the sitemap was submitted.\n- AI crawlers (`ai_crawler_policy`): allow_all (default), no_training or block_all, and any Cloudflare zone policy that must match.\n\n## Business goals, audience, markets and languages\n\n## Search intent and topic coverage\n\nRecord research sources and dates. Distinguish evidence from assumptions; do not invent traffic or ranking forecasts.\n\n## Page roles and URL plan\n\nContent types, useful landing pages, taxonomy, internal links and routes to preserve or redirect. Avoid near-duplicate pages created only for keyword variants.\n\n## Indexing and canonical policy\n\nWhich pages should be indexable, public noindex, or private? Record deliberate canonical overrides and sitemap policy; actual values live in the CMS.\n\n## Editorial standards and structured data\n\nUnique useful copy, supported claims, descriptive titles and links, accessible image text, schema matching visible content. No fixed title length or word count is a hard SEO rule.\n\n## Measurement and review\n\nBaseline, analytics/consent decisions, measures of success and review dates. Never store credentials or private analytics exports here.\n",
|
|
125
|
+
'workflows/README.md': "# Project routines\n\nRead only the relevant routine and discover current tool schemas through MCP.\nProject files record intent; the CMS owns current content and configuration.\n\n| Task | Project context | On-demand recipe |\n| --- | --- | --- |\n| Guided start | workflows/interview.md, brief/interview.md | Ask one question at a time; resume confirmed answers |\n| New website | workflows/new-site.md, brief/site.md, brief/brand.md, brief/content-model.md | tr-new-site, tr-starter-design |\n| Migration | workflows/migration.md, brief/migration.md | tr-migration-evidence, then tr-migrate-wp or the matching source recipe |\n| Everyday editing | brief/rules.md, pages/ notes and the relevant Page | tr-edit-site |\n| Page notes and review | workflows/page-notes.md, pages/README.md, pages/index.md | Read before edits; update logs afterward |\n| SEO | brief/seo.md and current site settings | tr-seo |\n| New reusable block | brief/brand.md and recorded editing needs | tr-custom-blocks |\n| Verification and publication | qa/checklist.md, decisions.md | Current preview/deploy tools and publishing readiness |\n| Launch | qa/checklist.md (Launch), brief/seo.md (Launch basics) | read_site_readiness, tr-seo |\n\nAdd project-specific routines here with inputs, steps, acceptance evidence and approval boundaries. Do not copy the complete Typeroll manual or private app guides. Discover enabled app guidance with read_app_documentation. Load a recipe when needed so instructions stay current and context stays small.\n",
|
|
126
126
|
'decisions.md': '# Decisions\n\nRecord date, decision, reason, approved scope and alternatives when useful. For design-token exceptions include the source/block, fixed value, reason (e.g. white text on a photograph) and review criteria.\n',
|
|
127
127
|
'sources/README.md': '# Sources\n\nStore research references and copy proposals. Record source URLs and dates.\nPrivate extracts belong in ignored private/ or an approved external store.\nDo not treat local drafts as current CMS content or commit media libraries.\n',
|
|
128
128
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typeroll/mcp-server",
|
|
3
|
-
"
|
|
3
|
+
"mcpName": "io.github.Typeroll/typeroll",
|
|
4
|
+
"version": "0.45.103",
|
|
4
5
|
"description": "Typeroll CMS MCP server \u2013 connect AI agents to the REST API to manage content and publish static websites.",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|
package/skills/tr-brand.md
CHANGED
|
@@ -165,11 +165,16 @@ One call sets everything:
|
|
|
165
165
|
```
|
|
166
166
|
update_site_settings {
|
|
167
167
|
"colors": { ...all 7 tokens },
|
|
168
|
-
"fonts": { "heading": "...", "body": "...", "size_base": 16 }
|
|
169
|
-
"custom_css": "/* optional: utility classes or @keyframes */"
|
|
168
|
+
"fonts": { "heading": "...", "body": "...", "size_base": 16 }
|
|
170
169
|
}
|
|
171
170
|
```
|
|
172
171
|
|
|
172
|
+
The brand lives in three places that editors can see and change: the
|
|
173
|
+
`colors` and `fonts` settings, the design scales, and named Styles (Step 5b).
|
|
174
|
+
Font families are set only in `fonts`; the platform loads each family once.
|
|
175
|
+
Never load a font again through `custom_css` (`@import`, `@font-face`) or
|
|
176
|
+
`scripts_head`: the readiness item `duplicate_fonts` reports it.
|
|
177
|
+
|
|
173
178
|
Read back to confirm: `read_site_settings`.
|
|
174
179
|
|
|
175
180
|
**Inside a redesign branch, scope the brand to the branch** — pass
|
|
@@ -225,11 +230,19 @@ eyebrow, lead, quote, buttons and section rhythm are chosen per block with
|
|
|
225
230
|
`at.desktop`. Prefer this to custom CSS: editors can see and change styles,
|
|
226
231
|
and every page follows.
|
|
227
232
|
|
|
228
|
-
## Step 6 — Custom CSS for
|
|
233
|
+
## Step 6 — Custom CSS only for recorded exceptions
|
|
234
|
+
|
|
235
|
+
Use `custom_css` only for what settings, scales and Styles cannot express,
|
|
236
|
+
and record each rule in `decisions.md` with its reason and the block or
|
|
237
|
+
page it serves. Before writing a rule, check `read_block_type` for a field,
|
|
238
|
+
part or Style that does the same job. Don't override block classes with
|
|
239
|
+
`!important`, restyle every heading or button in CSS, or set a hero's
|
|
240
|
+
background image in CSS: the platform cannot then load that image early, and
|
|
241
|
+
the readiness item `custom_css_overrides` reports it. Use the block's own
|
|
242
|
+
image field instead.
|
|
229
243
|
|
|
230
|
-
|
|
231
|
-
a special border radius
|
|
232
|
-
`custom_css`:
|
|
244
|
+
A legitimate exception is a token beyond the 7 base colours, such as a
|
|
245
|
+
gradient, a special border radius or a branded highlight colour:
|
|
233
246
|
|
|
234
247
|
```css
|
|
235
248
|
:root {
|
|
@@ -363,8 +376,9 @@ temporary/test address:
|
|
|
363
376
|
is light, white nav text becomes unreadable. Either darken `primary` or
|
|
364
377
|
make the header use `secondary`.
|
|
365
378
|
- **Custom_css is global.** Rules here apply to every page. Keep it to
|
|
366
|
-
`:root {}` token additions and
|
|
367
|
-
|
|
379
|
+
`:root {}` token additions and recorded exceptions. A look that repeats
|
|
380
|
+
is a named Style; a one-off treatment is a block's `custom_class` with a
|
|
381
|
+
rule in that Page's `custom_css`.
|
|
368
382
|
- **Font weight on every page.** Two families is fine; a third adds
|
|
369
383
|
measurable LCP impact even when self-hosted. A misspelled family name
|
|
370
384
|
fails silently to the fallback font — verify it as in Step 7.
|
|
@@ -41,6 +41,22 @@ Record a URL inventory from the existing site and source routing. Use explicit
|
|
|
41
41
|
Page paths to preserve nested routes or frontmatter-based permalinks. Do not
|
|
42
42
|
assume that a filename alone determines the production URL.
|
|
43
43
|
|
|
44
|
+
## Launch basics (ask early)
|
|
45
|
+
|
|
46
|
+
Ask the user for these once, early in the project. Read `read_site_settings` first and ask only for
|
|
47
|
+
what is missing. Each item is optional. Record a skipped item in
|
|
48
|
+
`decisions.md`, so the launch check in the last step holds no surprises.
|
|
49
|
+
|
|
50
|
+
- **Default share image.** Ask the user to choose or create the image that
|
|
51
|
+
social networks and chat apps show when a page has no image of its own:
|
|
52
|
+
upload one (`create_upload_url` or `upload_media_from_url`), pick an existing
|
|
53
|
+
one (`list_media`), or create one with `tr-imagegen`. Use a 1200×630 px PNG or
|
|
54
|
+
JPEG and set it as `default_og_image` with `update_site_settings`. Never
|
|
55
|
+
generate a title card from the page title instead. The user may skip this.
|
|
56
|
+
- **Organization.** The business name, logo and official profile URLs
|
|
57
|
+
(LinkedIn, Instagram, GitHub and similar). Set `organization` with `same_as`;
|
|
58
|
+
it becomes the home page's Organization schema.
|
|
59
|
+
|
|
44
60
|
## Map the source
|
|
45
61
|
|
|
46
62
|
- Common frontmatter such as title, slug, author, publication date and SEO maps
|
|
@@ -82,3 +98,33 @@ and Typeroll preview, then a fresh authorized static build. Verify every invento
|
|
|
82
98
|
URL and redirect, canonical/hreflang, sitemap inclusion, noindex state and assets.
|
|
83
99
|
Save working copies before publication. Keep the source host and DNS in place
|
|
84
100
|
until the candidate passes checks and the user authorizes its cutover.
|
|
101
|
+
|
|
102
|
+
## Launch (required)
|
|
103
|
+
|
|
104
|
+
A site is not finished until this step has run. Call `read_site_readiness`
|
|
105
|
+
before the production publication and again after it. For every item with the
|
|
106
|
+
status `warning` or `suggestion`, either fix it with the tool its `fix` names,
|
|
107
|
+
or record in `decisions.md` (or the starter's `qa/` folder) why it stays open.
|
|
108
|
+
Tell the user which items remain. A missing default share image is only a
|
|
109
|
+
suggestion; leave it if the user chose to skip it.
|
|
110
|
+
|
|
111
|
+
The report warns and never blocks a publication. A clean report is not approval
|
|
112
|
+
to publish: publish only when the user has approved it.
|
|
113
|
+
|
|
114
|
+
Publish only after the URL inventory, `verify_migration_urls` and
|
|
115
|
+
`get_migration_launch_report` pass, and the user approves:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
read_site_readiness
|
|
119
|
+
trigger_deploy
|
|
120
|
+
get_deploy_status job_id=<id>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The readiness report does not replace the migration launch report.
|
|
124
|
+
|
|
125
|
+
Run `read_site_readiness` again after the publication. It reads saved content
|
|
126
|
+
and settings, so confirm the rest on the live site: the AI crawler zone check
|
|
127
|
+
needs the site's website host, and a Search Console property verified by DNS
|
|
128
|
+
is not visible to Typeroll. The
|
|
129
|
+
[launch checklist](https://typeroll.com/docs/guides/launch-checklist/)
|
|
130
|
+
explains every item.
|
|
@@ -77,6 +77,22 @@ Ask explicitly:
|
|
|
77
77
|
- Any domains being **retired or merged**? Those need HTTP redirects served by the old hostname; DNS itself is not
|
|
78
78
|
a redirect. Plan that hosting separately.
|
|
79
79
|
|
|
80
|
+
### Launch basics per market (ask early)
|
|
81
|
+
|
|
82
|
+
Ask the user for these once per market, during Phase 0. Read `read_site_settings` first and ask only for
|
|
83
|
+
what is missing. Each item is optional. Record a skipped item in
|
|
84
|
+
`decisions.md`, so the launch check in the last step holds no surprises.
|
|
85
|
+
|
|
86
|
+
- **Default share image.** Ask the user to choose or create the image that
|
|
87
|
+
social networks and chat apps show when a page has no image of its own:
|
|
88
|
+
upload one (`create_upload_url` or `upload_media_from_url`), pick an existing
|
|
89
|
+
one (`list_media`), or create one with `tr-imagegen`. Use a 1200×630 px PNG or
|
|
90
|
+
JPEG and set it as `default_og_image` with `update_site_settings`. Never
|
|
91
|
+
generate a title card from the page title instead. The user may skip this.
|
|
92
|
+
- **Organization.** The business name, logo and official profile URLs
|
|
93
|
+
(LinkedIn, Instagram, GitHub and similar). Set `organization` with `same_as`;
|
|
94
|
+
it becomes the home page's Organization schema.
|
|
95
|
+
|
|
80
96
|
## Phase 1 — Inventory EVERY old site, before building anything
|
|
81
97
|
|
|
82
98
|
Do this for all sites up front. It's cheap, it's the only artefact that tells
|
|
@@ -292,6 +308,35 @@ new canonical build and is not an inventory-only operation):
|
|
|
292
308
|
6. Rerun the complete, unfiltered URL check and SEO review, then require
|
|
293
309
|
`get_migration_launch_report` to return `launch_ready: true`
|
|
294
310
|
|
|
311
|
+
## Phase 8 — Launch (required)
|
|
312
|
+
|
|
313
|
+
A site is not finished until this step has run. Call `read_site_readiness`
|
|
314
|
+
before the production publication and again after it. For every item with the
|
|
315
|
+
status `warning` or `suggestion`, either fix it with the tool its `fix` names,
|
|
316
|
+
or record in `decisions.md` (or the starter's `qa/` folder) why it stays open.
|
|
317
|
+
Tell the user which items remain. A missing default share image is only a
|
|
318
|
+
suggestion; leave it if the user chose to skip it.
|
|
319
|
+
|
|
320
|
+
The report warns and never blocks a publication. A clean report is not approval
|
|
321
|
+
to publish: publish only when the user has approved it.
|
|
322
|
+
|
|
323
|
+
Run it for every site in the family, one site at a time, after Phase 7
|
|
324
|
+
passes and before each site's production publication:
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
read_site_readiness site_id=<de-site>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The readiness report does not replace `get_migration_launch_report`: a site
|
|
331
|
+
needs both.
|
|
332
|
+
|
|
333
|
+
Run `read_site_readiness` again after the publication. It reads saved content
|
|
334
|
+
and settings, so confirm the rest on the live site: the AI crawler zone check
|
|
335
|
+
needs the site's website host, and a Search Console property verified by DNS
|
|
336
|
+
is not visible to Typeroll. The
|
|
337
|
+
[launch checklist](https://typeroll.com/docs/guides/launch-checklist/)
|
|
338
|
+
explains every item.
|
|
339
|
+
|
|
295
340
|
## Definition of done (per site)
|
|
296
341
|
|
|
297
342
|
- [ ] `get_migration_readiness source_url=<this market's old site>` → `ready: true`, warnings reviewed
|
|
@@ -299,6 +344,7 @@ new canonical build and is not an inventory-only operation):
|
|
|
299
344
|
- [ ] Complete `verify_migration_urls` → 0 `missing`, 0 `broken_redirect`, 0 `error`, 0 untested variants
|
|
300
345
|
- [ ] `record_migration_seo_acceptance` is accepted for the latest deploy
|
|
301
346
|
- [ ] `get_migration_launch_report` → `launch_ready: true`
|
|
347
|
+
- [ ] `read_site_readiness` run before and after publication; every open item fixed or recorded with a reason
|
|
302
348
|
- [ ] No source JavaScript copied; analytics/advertising via the Integrations app with the cookie banner on (`scripts.status: "clean"` in the launch report)
|
|
303
349
|
- [ ] hreflang cluster written on both/all sides, absolute, final domains
|
|
304
350
|
- [ ] `language` set on the site; `<html lang>` correct in the deployed HTML
|
package/skills/tr-migrate-wp.md
CHANGED
|
@@ -151,6 +151,22 @@ batch_read_pages page_ids=[<2-3 representative ids>] # see actual conventions
|
|
|
151
151
|
Don't skip this. Imposing a stranger's design on a customer's site is
|
|
152
152
|
the biggest avoidable mistake.
|
|
153
153
|
|
|
154
|
+
### 2b. Launch basics (ask early)
|
|
155
|
+
|
|
156
|
+
Ask the user for these once, early in the project. Read `read_site_settings` first and ask only for
|
|
157
|
+
what is missing. Each item is optional. Record a skipped item in
|
|
158
|
+
`decisions.md`, so the launch check in the last step holds no surprises.
|
|
159
|
+
|
|
160
|
+
- **Default share image.** Ask the user to choose or create the image that
|
|
161
|
+
social networks and chat apps show when a page has no image of its own:
|
|
162
|
+
upload one (`create_upload_url` or `upload_media_from_url`), pick an existing
|
|
163
|
+
one (`list_media`), or create one with `tr-imagegen`. Use a 1200×630 px PNG or
|
|
164
|
+
JPEG and set it as `default_og_image` with `update_site_settings`. Never
|
|
165
|
+
generate a title card from the page title instead. The user may skip this.
|
|
166
|
+
- **Organization.** The business name, logo and official profile URLs
|
|
167
|
+
(LinkedIn, Instagram, GitHub and similar). Set `organization` with `same_as`;
|
|
168
|
+
it becomes the home page's Organization schema.
|
|
169
|
+
|
|
154
170
|
### 3. Normalize shared data before importing page bodies
|
|
155
171
|
|
|
156
172
|
Read taxonomy definitions and paginated terms from the helper's `/taxonomies`
|
|
@@ -343,11 +359,23 @@ Record `fidelity: { desktop, mobile, shared_data, integrations, evidence }` with
|
|
|
343
359
|
The launch report requires this evidence in Core 0.2.5.
|
|
344
360
|
Never mark an unavailable integration or uninspected screenshot as accepted.
|
|
345
361
|
|
|
346
|
-
### 7.
|
|
362
|
+
### 7. Launch and ship (required)
|
|
363
|
+
|
|
364
|
+
A site is not finished until this step has run. Call `read_site_readiness`
|
|
365
|
+
before the production publication and again after it. For every item with the
|
|
366
|
+
status `warning` or `suggestion`, either fix it with the tool its `fix` names,
|
|
367
|
+
or record in `decisions.md` (or the starter's `qa/` folder) why it stays open.
|
|
368
|
+
Tell the user which items remain. A missing default share image is only a
|
|
369
|
+
suggestion; leave it if the user chose to skip it.
|
|
370
|
+
|
|
371
|
+
The report warns and never blocks a publication. A clean report is not approval
|
|
372
|
+
to publish: publish only when the user has approved it.
|
|
347
373
|
|
|
348
374
|
When the user signs off:
|
|
349
375
|
|
|
350
376
|
```
|
|
377
|
+
read_site_readiness
|
|
378
|
+
|
|
351
379
|
# Bulk-publish drafts that look right
|
|
352
380
|
batch_update_pages updates=[{page_id, patch:{status:"published"}}, ...]
|
|
353
381
|
|
|
@@ -356,6 +384,16 @@ trigger_deploy
|
|
|
356
384
|
get_deploy_status job_id=<id> # poll
|
|
357
385
|
```
|
|
358
386
|
|
|
387
|
+
The readiness report does not replace `get_migration_launch_report`: URL
|
|
388
|
+
parity and SEO acceptance remain the migration's own launch gate.
|
|
389
|
+
|
|
390
|
+
Run `read_site_readiness` again after the publication. It reads saved content
|
|
391
|
+
and settings, so confirm the rest on the live site: the AI crawler zone check
|
|
392
|
+
needs the site's website host, and a Search Console property verified by DNS
|
|
393
|
+
is not visible to Typeroll. The
|
|
394
|
+
[launch checklist](https://typeroll.com/docs/guides/launch-checklist/)
|
|
395
|
+
explains every item.
|
|
396
|
+
|
|
359
397
|
## Pitfalls
|
|
360
398
|
|
|
361
399
|
- **Don't publish during migration.** Always import as `draft`. Even
|
|
@@ -364,7 +402,7 @@ get_deploy_status job_id=<id> # poll
|
|
|
364
402
|
have been served at `/2024/01/foo-bar/` due to the permalink
|
|
365
403
|
structure. The full URL is what users see in Google; preserve that,
|
|
366
404
|
not the bare slug.
|
|
367
|
-
- **Image bandwidth.** R2 upload is metered. Use `
|
|
405
|
+
- **Image bandwidth.** R2 upload is metered. Use `search_pages`
|
|
368
406
|
contains="<old-domain>" on already-imported content to spot images
|
|
369
407
|
that weren't transferred.
|
|
370
408
|
- **Plugin JavaScript.** Never copy scripts from plugins, the theme or
|
package/skills/tr-new-site.md
CHANGED
|
@@ -170,6 +170,22 @@ back silently, so verify fonts on the published address (`tr-brand` Step 7).
|
|
|
170
170
|
`tr-brand`). Also set `settings.logo` to the uploaded brand mark — it feeds
|
|
171
171
|
OG/schema even if the header uses a different lockup.
|
|
172
172
|
|
|
173
|
+
#### Launch basics (ask early)
|
|
174
|
+
|
|
175
|
+
Ask the user for these once, early in the project. Read `read_site_settings` first and ask only for
|
|
176
|
+
what is missing. Each item is optional. Record a skipped item in
|
|
177
|
+
`decisions.md`, so the launch check in the last step holds no surprises.
|
|
178
|
+
|
|
179
|
+
- **Default share image.** Ask the user to choose or create the image that
|
|
180
|
+
social networks and chat apps show when a page has no image of its own:
|
|
181
|
+
upload one (`create_upload_url` or `upload_media_from_url`), pick an existing
|
|
182
|
+
one (`list_media`), or create one with `tr-imagegen`. Use a 1200×630 px PNG or
|
|
183
|
+
JPEG and set it as `default_og_image` with `update_site_settings`. Never
|
|
184
|
+
generate a title card from the page title instead. The user may skip this.
|
|
185
|
+
- **Organization.** The business name, logo and official profile URLs
|
|
186
|
+
(LinkedIn, Instagram, GitHub and similar). Set `organization` with `same_as`;
|
|
187
|
+
it becomes the home page's Organization schema.
|
|
188
|
+
|
|
173
189
|
Set `iframe_allowed_hosts` when customer content embeds a provider outside
|
|
174
190
|
the built-in YouTube/Vimeo/Google Maps/Calendly set. Values are exact domain
|
|
175
191
|
hostnames, never URLs or wildcards. Read the setting back before deciding that
|
|
@@ -397,15 +413,36 @@ Share the preview link. Iterate on feedback with `update_block` /
|
|
|
397
413
|
`add_block` / `move_block` — that's the point of block mode: surgical
|
|
398
414
|
edits, not full-page rewrites.
|
|
399
415
|
|
|
400
|
-
### 7.
|
|
416
|
+
### 7. Launch (required)
|
|
417
|
+
|
|
418
|
+
A site is not finished until this step has run. Call `read_site_readiness`
|
|
419
|
+
before the production publication and again after it. For every item with the
|
|
420
|
+
status `warning` or `suggestion`, either fix it with the tool its `fix` names,
|
|
421
|
+
or record in `decisions.md` (or the starter's `qa/` folder) why it stays open.
|
|
422
|
+
Tell the user which items remain. A missing default share image is only a
|
|
423
|
+
suggestion; leave it if the user chose to skip it.
|
|
424
|
+
|
|
425
|
+
The report warns and never blocks a publication. A clean report is not approval
|
|
426
|
+
to publish: publish only when the user has approved it.
|
|
427
|
+
|
|
428
|
+
When the user approves, verify `read_publishing_readiness` and save the
|
|
429
|
+
intended Version before deploying. A published CMS status is not a live
|
|
430
|
+
deployment.
|
|
401
431
|
|
|
402
|
-
When the user approves, verify read_publishing_readiness and save the intended
|
|
403
|
-
Version before deploying. A published CMS status is not a live deployment.
|
|
404
432
|
```
|
|
433
|
+
read_site_readiness
|
|
405
434
|
trigger_deploy
|
|
406
435
|
get_deploy_status job_id=<id>
|
|
436
|
+
read_site_readiness
|
|
407
437
|
```
|
|
408
438
|
|
|
439
|
+
Run `read_site_readiness` again after the publication. It reads saved content
|
|
440
|
+
and settings, so confirm the rest on the live site: the AI crawler zone check
|
|
441
|
+
needs the site's website host, and a Search Console property verified by DNS
|
|
442
|
+
is not visible to Typeroll. The
|
|
443
|
+
[launch checklist](https://typeroll.com/docs/guides/launch-checklist/)
|
|
444
|
+
explains every item.
|
|
445
|
+
|
|
409
446
|
## Secondary path: HTML mode
|
|
410
447
|
|
|
411
448
|
For migrated legacy pages or a hand-crafted one-off, `content_mode:
|