@typeroll/mcp-server 0.45.64 → 0.45.66
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/AGENTS.md +65 -11
- package/README.md +5 -0
- package/dist/bundled-content.js +12 -11
- package/dist/server.js +12 -1
- package/dist/structured-data.js +217 -0
- package/dist/tools/access.js +36 -4
- package/dist/tools/apps.js +5 -2
- package/dist/tools/domain.js +8 -4
- package/dist/tools/migration.js +1 -1
- package/dist/tools/settings.js +2 -2
- package/dist/tools/sites.js +1 -1
- package/dist/tools/structured-data.js +64 -0
- package/dist/tools/workflows.js +8 -4
- package/dist/version.js +1 -1
- package/dist/workspace.js +12 -1
- package/package.json +5 -3
- package/skills/README.md +3 -1
- package/skills/tr-content-write.md +16 -0
- package/skills/tr-edit-site.md +88 -1
- package/skills/tr-import-url.md +9 -2
- package/skills/tr-migrate-astro.md +7 -0
- package/skills/tr-migrate-multisite.md +10 -0
- package/skills/tr-migrate-wp.md +12 -0
- package/skills/tr-migration-evidence.md +31 -0
- package/skills/tr-new-site.md +7 -1
- package/skills/tr-seo.md +58 -3
- package/skills/tr-starter-design.md +79 -0
|
@@ -115,6 +115,22 @@ shortening, adding a CTA, tweaking SEO description.
|
|
|
115
115
|
Don't `update_page status:"published"` without an explicit "looks
|
|
116
116
|
good, publish it" from the user. Same for `trigger_deploy`.
|
|
117
117
|
|
|
118
|
+
## Instead of the content workflows
|
|
119
|
+
|
|
120
|
+
`start_workflow` types `content_generation` and `content_improvement` run the
|
|
121
|
+
model with the Organization's own AI key. On a Site without that access they
|
|
122
|
+
fail with `failure_code: "ai_not_enabled"`; don't retry them. Do the work with
|
|
123
|
+
this recipe and tell the user you did it yourself:
|
|
124
|
+
|
|
125
|
+
- **Generate a page** (`content_generation { topic, tone?, audience? }`):
|
|
126
|
+
steps 1–5 for that topic, created as a draft.
|
|
127
|
+
- **Improve content** (`content_improvement { limit? }`): `list_pages
|
|
128
|
+
status="published"` and `batch_read_pages`, rank the pages by thin text,
|
|
129
|
+
missing `seo_title`/`seo_description` and age (not updated for a year), and
|
|
130
|
+
rewrite the weakest few (five by default) in the site's voice. Keep facts,
|
|
131
|
+
URLs and structure; leave the rewrites as unsaved drafts and show previews
|
|
132
|
+
before anything is saved or published.
|
|
133
|
+
|
|
118
134
|
## SEO conventions worth knowing
|
|
119
135
|
|
|
120
136
|
- **`kind: "article"`** for blog posts and news. Switches to
|
package/skills/tr-edit-site.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tr-edit-site
|
|
3
|
-
description: Edit or create Typeroll pages with a person’s usual AI assistant. Confirm one site, find the right page, preview changes and save with permission.
|
|
3
|
+
description: Edit or create Typeroll pages with a person’s usual AI assistant. Confirm one site, find the right page, handle everyday requests (page links, images, menus, site-wide changes, settings, analytics, structured data), preview changes and save with permission.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Work on one Typeroll site
|
|
@@ -33,6 +33,93 @@ description: Edit or create Typeroll pages with a person’s usual AI assistant.
|
|
|
33
33
|
draft durable; saving and deploying are different actions. Publish only
|
|
34
34
|
when authorized and readiness passes. Report the actual result and URL.
|
|
35
35
|
|
|
36
|
+
## Everyday requests
|
|
37
|
+
|
|
38
|
+
Read the page or global block first, change only what was asked, and keep
|
|
39
|
+
everything else. When several pages fit the description, ask before editing.
|
|
40
|
+
|
|
41
|
+
**Addresses and preview links.** `list_pages` and `read_page` return each
|
|
42
|
+
page's `url` (its path). The public address is `get_site` → `urls.production`
|
|
43
|
+
plus that path, and it serves the page only after it is published and
|
|
44
|
+
deployed. Never invent a domain; when there is none, say why and offer a
|
|
45
|
+
preview instead: `get_preview_link page_id=… include_working_copy:true` shows
|
|
46
|
+
drafts and unpublished pages. Mint one link and reuse it while iterating.
|
|
47
|
+
|
|
48
|
+
**Images the user provides.** Upload first: a URL with `upload_media_from_url`,
|
|
49
|
+
a local file with `create_upload_url` (see `tr-images`); `list_media` finds
|
|
50
|
+
existing ones. Use the returned CDN URL exactly. With a named target, place it
|
|
51
|
+
there (`add_block` with `core/image` on a block page; an `<img>` with `alt` in
|
|
52
|
+
`html_content` on an HTML page). Without one, ask what the image is for. Ask
|
|
53
|
+
for alt text when none was given. Afterwards say in one sentence where it
|
|
54
|
+
landed; to answer "has it been added?", read the page again rather than
|
|
55
|
+
trusting the conversation.
|
|
56
|
+
|
|
57
|
+
**Small edits and new pages.** Block pages: `get_page_blocks`, then
|
|
58
|
+
`update_block` on the one block. HTML pages: `read_page`, then `update_page`
|
|
59
|
+
with the complete `html_content`, never a fragment. New pages: `create_page`
|
|
60
|
+
(draft by default) with a realistic start: heading, introduction and one
|
|
61
|
+
section. A Page template is `update_page` `template` (an allowed ID, or null
|
|
62
|
+
for the content type default). `discard_working_copy` throws an unsaved draft
|
|
63
|
+
away. Publishing means saving the draft, `update_page` `status: "published"`
|
|
64
|
+
(applies immediately) and, when the user wants it live now, `trigger_deploy`.
|
|
65
|
+
|
|
66
|
+
**Site-wide changes are composition, not a series of page edits.** "Change
|
|
67
|
+
every hero", "a CTA on every product page", "our phone number in every
|
|
68
|
+
footer":
|
|
69
|
+
|
|
70
|
+
1. `list_partials` shows what is already shared: header, footer and free
|
|
71
|
+
global blocks.
|
|
72
|
+
2. If a fitting global block exists, edit it once. Run
|
|
73
|
+
`find_pages_using_block` first and tell the user the blast radius ("this
|
|
74
|
+
block is on 8 pages and one template — go ahead?"); a header or footer
|
|
75
|
+
among the results means every page.
|
|
76
|
+
3. If nothing fits, propose a global block: `make_block_global` turns an
|
|
77
|
+
existing block into one, or `create_free_block` with a kebab-case ID. Place
|
|
78
|
+
it with `core/global_block` on block pages or `<x-include name="…" />` on
|
|
79
|
+
HTML pages. Layout that every Page of a type shares belongs in its Page
|
|
80
|
+
template (`tr-page-template`).
|
|
81
|
+
4. Edit pages one by one only for content that really differs per page, such
|
|
82
|
+
as each page's SEO description. Confirm the page list first.
|
|
83
|
+
|
|
84
|
+
"All 23 pages" or "every page" is a strong hint for a global block. Say what
|
|
85
|
+
you will do and wait for confirmation before editing many pages.
|
|
86
|
+
|
|
87
|
+
**Extract while you sweep.** Even when the user asked for per-page edits,
|
|
88
|
+
pause when the same content is about to land on a third page and offer to
|
|
89
|
+
make it a global block instead ("I'm adding the same CTA to several pages —
|
|
90
|
+
make it a reusable `newsletter-cta` block so future edits happen once?").
|
|
91
|
+
Check `list_partials` and `find_pages_using_block` first so you extend an
|
|
92
|
+
existing block rather than create a duplicate.
|
|
93
|
+
|
|
94
|
+
**Menu and footer.** `read_partial partial_id="header"`. In a block header,
|
|
95
|
+
change the `core/navigation` block's `links` with `update_block`; in an HTML
|
|
96
|
+
header, add the `<a href="/pricing">Pricing</a>` to the nav and write back the
|
|
97
|
+
complete HTML with `update_partial`. The footer works the same way. Building
|
|
98
|
+
either from scratch: `tr-header-footer`.
|
|
99
|
+
|
|
100
|
+
**Settings, not page edits.** Brand colors, fonts, logo, icons and contact
|
|
101
|
+
details are `update_site_settings` (admin), for example `colors.primary`. A new
|
|
102
|
+
logo must be uploaded first. Analytics, advertising pixels and chat widgets
|
|
103
|
+
are set by ID in the Integrations app (`read_app app_id="integrations"`, then
|
|
104
|
+
`update_app`), not pasted as scripts; turn on the cookie banner
|
|
105
|
+
(`update_site_settings` `cookie_consent: { enabled: true }`) so they wait for
|
|
106
|
+
consent. Only a tag the Integrations catalog lacks goes in
|
|
107
|
+
`cookie_consent.scripts_optional`. Both reach the site with the next deploy.
|
|
108
|
+
|
|
109
|
+
**Redirects, forms, listings.** A renamed URL gets `create_redirect` (301
|
|
110
|
+
unless the user says otherwise); changing a page's `path` creates one on save.
|
|
111
|
+
Place forms with a `core/form` block or `<x-form id="…" />`, never a
|
|
112
|
+
hand-written `<form>` (`tr-forms`). Lists of articles are a `core/page_list`
|
|
113
|
+
block, never hand-written HTML (`tr-blog`).
|
|
114
|
+
|
|
115
|
+
**Structured data.** For an FAQ, recipe or event page use `add_faq_schema`,
|
|
116
|
+
`add_recipe_schema` or `add_event_schema` instead of hand-writing `json_ld`;
|
|
117
|
+
see `tr-seo`.
|
|
118
|
+
|
|
119
|
+
**When something is out of reach.** If a tool refuses, tell the user what they
|
|
120
|
+
can do about it, not only the error. When no tool covers a request (billing,
|
|
121
|
+
for example), say so plainly and point to the relevant portal page.
|
|
122
|
+
|
|
36
123
|
## Compact discovery
|
|
37
124
|
|
|
38
125
|
Find tools with `search_tools`, inspect one schema with `describe_tool`, then
|
package/skills/tr-import-url.md
CHANGED
|
@@ -5,6 +5,10 @@ description: Use when the user wants to import or migrate content from a non-Wor
|
|
|
5
5
|
|
|
6
6
|
# Import content from a non-WordPress site
|
|
7
7
|
|
|
8
|
+
**Who runs this:** this recipe is for the user's own AI agent (Claude Code, Codex or
|
|
9
|
+
another MCP client) working through MCP and the public API. The portal's built-in
|
|
10
|
+
AI assistant has no migration tools: it does not import, convert or verify sites.
|
|
11
|
+
|
|
8
12
|
**Before bulk conversion:** read `tr-migration-evidence` with `read_skill`.
|
|
9
13
|
Complete its source baseline and one verified prototype per active template.
|
|
10
14
|
Read target content to protect edits, not to justify accidental target defaults.
|
|
@@ -194,8 +198,11 @@ get_deploy_status job_id=<id>
|
|
|
194
198
|
several. Fix it.
|
|
195
199
|
- **Squarespace/Wix forms.** They won't work after import — the backend
|
|
196
200
|
is vendor-locked. Create a Typeroll form instead: `create_form`.
|
|
197
|
-
- **
|
|
198
|
-
|
|
201
|
+
- **Scripts and tracking.** Never copy the source's JavaScript. Carry
|
|
202
|
+
analytics and advertising tags (GA4, GTM, Meta Pixel and others) over by
|
|
203
|
+
ID in the Integrations app, with the cookie banner enabled so they wait for
|
|
204
|
+
consent; never paste the vendor snippet into `scripts_head`. See
|
|
205
|
+
tr-migration-evidence, "Scripts, tracking and consent".
|
|
199
206
|
- **Videos.** YouTube/Vimeo embeds are fine (`<iframe>` is allowed).
|
|
200
207
|
Hosted MP4s need re-uploading if the source URL won't persist.
|
|
201
208
|
|
|
@@ -5,6 +5,10 @@ description: Migrate an existing Astro site, including Astro Content Collections
|
|
|
5
5
|
|
|
6
6
|
# Migrate an Astro site
|
|
7
7
|
|
|
8
|
+
**Who runs this:** this recipe is for the user's own AI agent (Claude Code, Codex or
|
|
9
|
+
another MCP client) working through MCP and the public API. The portal's built-in
|
|
10
|
+
AI assistant has no migration tools: it does not import, convert or verify sites.
|
|
11
|
+
|
|
8
12
|
**Before bulk conversion:** read `tr-migration-evidence` with `read_skill`.
|
|
9
13
|
Complete its source baseline and one verified prototype per active template.
|
|
10
14
|
Read target content to protect edits, not to justify accidental target defaults.
|
|
@@ -50,6 +54,9 @@ assume that a filename alone determines the production URL.
|
|
|
50
54
|
- Browser scripts, integrations and server-rendered endpoints need separate
|
|
51
55
|
review. A static Page cannot inherit a private server runtime by copying its
|
|
52
56
|
source text. Configure the supported Forms/Extension runtime explicitly.
|
|
57
|
+
Do not copy client scripts; analytics and advertising tags move to the
|
|
58
|
+
Integrations app behind the cookie banner (tr-migration-evidence, "Scripts,
|
|
59
|
+
tracking and consent").
|
|
53
60
|
|
|
54
61
|
Create templates with `create_page_template`, types with `create_content_type`,
|
|
55
62
|
then one `create_page` per source record. Use returned Page IDs, not source IDs
|
|
@@ -5,6 +5,10 @@ description: Use when migrating SEVERAL sites at once — a WordPress multisite
|
|
|
5
5
|
|
|
6
6
|
# Migrate a multisite / multi-domain family to Typeroll
|
|
7
7
|
|
|
8
|
+
**Who runs this:** this recipe is for the user's own AI agent (Claude Code, Codex or
|
|
9
|
+
another MCP client) working through MCP and the public API. The portal's built-in
|
|
10
|
+
AI assistant has no migration tools: it does not import, convert or verify sites.
|
|
11
|
+
|
|
8
12
|
**Before bulk conversion:** read `tr-migration-evidence` with `read_skill`.
|
|
9
13
|
Complete its source baseline and one verified prototype per active template.
|
|
10
14
|
Read target content to protect edits, not to justify accidental target defaults.
|
|
@@ -163,6 +167,11 @@ language and attribution. Use authorized synthetic tests with a safe recipient;
|
|
|
163
167
|
record delivery UNVERIFIED when it cannot be tested safely. Never create real
|
|
164
168
|
leads or send mail to third parties as a routine migration test.
|
|
165
169
|
|
|
170
|
+
Scripts follow tr-migration-evidence, "Scripts, tracking and consent": copy no
|
|
171
|
+
source JavaScript, and carry each market's own analytics and advertising IDs
|
|
172
|
+
over in that site's Integrations app with its cookie banner enabled. Never
|
|
173
|
+
reuse one market's tracking IDs on another.
|
|
174
|
+
|
|
166
175
|
## Phase 5 — Redirects for everything you didn't preserve
|
|
167
176
|
|
|
168
177
|
Work the coverage report, not your memory:
|
|
@@ -271,6 +280,7 @@ new canonical build and is not an inventory-only operation):
|
|
|
271
280
|
- [ ] Complete `verify_migration_urls` → 0 `missing`, 0 `broken_redirect`, 0 `error`, 0 untested variants
|
|
272
281
|
- [ ] `record_migration_seo_acceptance` is accepted for the latest deploy
|
|
273
282
|
- [ ] `get_migration_launch_report` → `launch_ready: true`
|
|
283
|
+
- [ ] No source JavaScript copied; analytics/advertising via the Integrations app with the cookie banner on (`scripts.status: "clean"` in the launch report)
|
|
274
284
|
- [ ] hreflang cluster written on both/all sides, absolute, final domains
|
|
275
285
|
- [ ] `language` set on the site; `<html lang>` correct in the deployed HTML
|
|
276
286
|
- [ ] Internal links rewritten (no lingering absolute links to the old domain)
|
package/skills/tr-migrate-wp.md
CHANGED
|
@@ -5,6 +5,10 @@ description: Use when the user asks to migrate a WordPress site to Typeroll, men
|
|
|
5
5
|
|
|
6
6
|
# Migrate from WordPress to Typeroll
|
|
7
7
|
|
|
8
|
+
**Who runs this:** this recipe is for the user's own AI agent (Claude Code, Codex or
|
|
9
|
+
another MCP client) working through MCP and the public API. The portal's built-in
|
|
10
|
+
AI assistant has no migration tools: it does not import, convert or verify sites.
|
|
11
|
+
|
|
8
12
|
**Before bulk conversion:** read `tr-migration-evidence` with `read_skill`.
|
|
9
13
|
Complete its source baseline and one verified prototype per active template.
|
|
10
14
|
Read target content to protect edits, not to justify accidental target defaults.
|
|
@@ -146,6 +150,9 @@ a. Fetch from WP. Prefer the helper plugin's authenticated endpoint
|
|
|
146
150
|
b. Clean the HTML. Strip Elementor / Gutenberg / Breakdance class
|
|
147
151
|
soup. Drop empty `<div>` and `<span>` wrappers. Keep semantic tags,
|
|
148
152
|
tables, iframes from known hosts (YouTube / Vimeo / Calendly).
|
|
153
|
+
Remove every `<script>`: plugin and theme JavaScript is never copied.
|
|
154
|
+
Analytics and advertising tags move to the Integrations app behind the
|
|
155
|
+
cookie banner (tr-migration-evidence, "Scripts, tracking and consent").
|
|
149
156
|
|
|
150
157
|
c. Migrate referenced images with `upload_media_from_url`. The customer's
|
|
151
158
|
transfer Worker copies and verifies them directly in R2. Use the returned
|
|
@@ -291,6 +298,11 @@ get_deploy_status job_id=<id> # poll
|
|
|
291
298
|
- **Image bandwidth.** R2 upload is metered. Use `find_pages_matching`
|
|
292
299
|
contains="<old-domain>" on already-imported content to spot images
|
|
293
300
|
that weren't transferred.
|
|
301
|
+
- **Plugin JavaScript.** Never copy scripts from plugins, the theme or
|
|
302
|
+
WordPress core into settings, pages, partials or block types, and never
|
|
303
|
+
copy the cookie plugin. Carry analytics and advertising over by ID in the
|
|
304
|
+
Integrations app with `cookie_consent.enabled: true`. The launch report
|
|
305
|
+
fails on copied source scripts and on tracking outside consent.
|
|
294
306
|
- **WP-specific JSON-LD** (Yoast, Rank Math) is usually wrong after a
|
|
295
307
|
redesign because it references old URLs. Strip it; let Typeroll
|
|
296
308
|
emit fresh Article/Page schemas via `kind: 'article'` + `author`.
|
|
@@ -5,6 +5,10 @@ description: Source-first design baseline, native capability mapping and evidenc
|
|
|
5
5
|
|
|
6
6
|
# Migration evidence contract
|
|
7
7
|
|
|
8
|
+
**Who runs this:** this recipe is for the user's own AI agent (Claude Code, Codex or
|
|
9
|
+
another MCP client) working through MCP and the public API. The portal's built-in
|
|
10
|
+
AI assistant has no migration tools: it does not import, convert or verify sites.
|
|
11
|
+
|
|
8
12
|
Use this shared contract with WordPress, Astro, URL and multisite recipes.
|
|
9
13
|
Default to preserving the source appearance and behavior. A redesign requires
|
|
10
14
|
explicit scope. A partly migrated target is not the design reference.
|
|
@@ -115,6 +119,33 @@ interaction and conversion separately; only use an authorized synthetic mode and
|
|
|
115
119
|
safe recipient. Unavailable delivery remains UNVERIFIED. No real leads or mail
|
|
116
120
|
to third parties merely to fill a checklist.
|
|
117
121
|
|
|
122
|
+
## Scripts, tracking and consent
|
|
123
|
+
|
|
124
|
+
Do not copy JavaScript from the source site. WordPress plugins and themes
|
|
125
|
+
inject their own runtime: jQuery, sliders, lightboxes, form and cookie
|
|
126
|
+
plugins, page builders (Elementor, Breakdance, Divi), emoji and AJAX calls to
|
|
127
|
+
`admin-ajax.php` or `/wp-json/`. It loads from, or posts to, a site that is
|
|
128
|
+
about to be switched off. Inventory every `<script>` on representative source
|
|
129
|
+
pages (head and body, inline and external, and which plugin or theme it
|
|
130
|
+
belongs to), then decide each one:
|
|
131
|
+
|
|
132
|
+
| Source script | Decision |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| WordPress core, theme or plugin runtime | Never copy. Rebuild what it did with native blocks, Forms or settings, or report a platform gap (see "Report platform gaps"). |
|
|
135
|
+
| Cookie plugin (CookieYes, Complianz, Cookiebot, Borlabs, …) | Never copy. Enable the site's cookie banner (`cookie_consent.enabled: true`) with Swedish or local text and the privacy policy URL. |
|
|
136
|
+
| Analytics or advertising tag in the Integrations catalog (GA4, GTM, Meta Pixel, LinkedIn, TikTok, Hotjar, Clarity, Pinterest, Snap, Reddit, X, Microsoft Ads, HubSpot, Plausible, Fathom, Matomo) | Carry over the ID only: enable the Integrations app with the source's ID (`list_apps`, `update_app`). Enable the cookie banner; analytics and advertising integrations wait for the visitor's consent. Never paste the vendor snippet. |
|
|
137
|
+
| Tracking tag the catalog does not cover | Only with the owner's approval, in `cookie_consent.scripts_optional` with the banner enabled. Never in `scripts_head`, `scripts_body_end`, `scripts_necessary`, pages or partials. |
|
|
138
|
+
| Partner widget or embed the page needs to work | Prefer a native block or an iframe from an allowlisted host. Keep custom code minimal, record why, and do not load it from the source site. |
|
|
139
|
+
|
|
140
|
+
Ask the owner to confirm the analytics IDs; never invent or reuse one from
|
|
141
|
+
another market. Before launch, `get_migration_launch_report` must show
|
|
142
|
+
`scripts.status: "clean"`: it fails on copied source-platform scripts
|
|
143
|
+
(`source_runtime_script`), tracking outside consent
|
|
144
|
+
(`tracking_outside_consent`) and analytics or advertising integrations without
|
|
145
|
+
the banner (`tracking_without_consent_banner`). In the hosted preview, check
|
|
146
|
+
that no request goes to the source host or `/wp-content/`, no tracking request
|
|
147
|
+
fires before consent, and the tags fire after "accept all".
|
|
148
|
+
|
|
118
149
|
## Evidence record (repeat per recipe, viewport and state)
|
|
119
150
|
|
|
120
151
|
- Source URL/date and target URL; Page/template IDs; exact publication ID.
|
package/skills/tr-new-site.md
CHANGED
|
@@ -34,7 +34,13 @@ read_app_documentation when needed; do not bundle private app guides locally.
|
|
|
34
34
|
No Site yet? Use hosted MCP with an Organization key and create_site, or create it
|
|
35
35
|
in the portal. To start from an AI site plan instead of a blank Site, use
|
|
36
36
|
create_site_and_plan, poll get_workflow and approve its structure review only
|
|
37
|
-
with the user's consent.
|
|
37
|
+
with the user's consent. That AI plan needs the Organization's own portal AI
|
|
38
|
+
key for new Sites; without it the call answers `403 ai_not_enabled` and the
|
|
39
|
+
`site_planning` workflow fails with `failure_code: "ai_not_enabled"`. Then plan
|
|
40
|
+
it yourself: `create_site`, propose the site name, tagline and page list (title,
|
|
41
|
+
slug, purpose, target keywords) from the business description, get the
|
|
42
|
+
user's approval, and build with this recipe. Tell the user you planned it
|
|
43
|
+
yourself; don't retry the workflow. Read back its ID before editing. Use one explicit Site/Version for
|
|
38
44
|
related operations. Larger changes to an existing site should use a branch.
|
|
39
45
|
|
|
40
46
|
Before importing content or a media library, require get_import_readiness to pass.
|
package/skills/tr-seo.md
CHANGED
|
@@ -36,7 +36,8 @@ read_site_settings
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
For each page, check:
|
|
39
|
-
- Is `seo_title` set? (if not, Google uses `title` + suffix — often fine
|
|
39
|
+
- Is `seo_title` set? (if not, Google uses `title` + suffix — often fine;
|
|
40
|
+
aim for 50–60 characters, longer gets truncated)
|
|
40
41
|
- Is `seo_description` set? (150–160 chars, unique per page, includes keywords)
|
|
41
42
|
- Is `og_image` set for the homepage and key landing pages?
|
|
42
43
|
- Does the page have exactly one `<h1>`?
|
|
@@ -79,6 +80,20 @@ update_page page_id="home" patch={
|
|
|
79
80
|
}
|
|
80
81
|
```
|
|
81
82
|
|
|
83
|
+
### 3b. URL and article conventions
|
|
84
|
+
|
|
85
|
+
When creating or moving pages:
|
|
86
|
+
|
|
87
|
+
- Slugs are lowercase kebab-case: `our-services`, not `Our_Services`.
|
|
88
|
+
- Reflect hierarchy in the address. A team member lives at `/team/jane-doe`
|
|
89
|
+
(set `path`, or use a content type whose `route_template` adds the
|
|
90
|
+
prefix), not at `/jane-doe`. Breadcrumbs and their BreadcrumbList schema
|
|
91
|
+
follow the resolved route.
|
|
92
|
+
- Blog posts and news get `kind: "article"` and an `author`: that emits
|
|
93
|
+
Article JSON-LD and `og:type=article`. No author means no author in the
|
|
94
|
+
rich result.
|
|
95
|
+
- Give shared pages, especially articles, their own `og_image`.
|
|
96
|
+
|
|
82
97
|
### 4. Add Open Graph images
|
|
83
98
|
|
|
84
99
|
Set `og_image` on pages that get shared on social media. If the site has
|
|
@@ -116,6 +131,37 @@ repeating it on every page (`null` clears it):
|
|
|
116
131
|
update_site_settings organization={"name":"Acme Studio","logo":"<cdn_url>","same_as":["https://www.linkedin.com/company/acme"]}
|
|
117
132
|
```
|
|
118
133
|
|
|
134
|
+
**FAQ, recipe and event pages have tools.** They validate the input, build
|
|
135
|
+
the schema.org JSON-LD and merge it into the page's `json_ld` (a node of the
|
|
136
|
+
same type is replaced, anything else is kept). Use them instead of
|
|
137
|
+
hand-writing JSON-LD, and copy the content from what the page visibly shows:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
add_faq_schema page_id="faq" items=[
|
|
141
|
+
{"question": "Do you deliver on weekends?", "answer": "Yes, Saturdays 9–15."}
|
|
142
|
+
]
|
|
143
|
+
add_recipe_schema page_id="kanelbullar" name="Kanelbullar"
|
|
144
|
+
ingredients=["500 g vetemjöl", "2 tsk kanel"] instructions=["Blanda degen.", "Grädda i 225 °C."]
|
|
145
|
+
prep_time="PT1H30M" cook_time="PT10M" recipe_yield="24 bullar" image_url="<cdn_url>"
|
|
146
|
+
add_event_schema page_id="sommarkonsert" name="Sommarkonsert"
|
|
147
|
+
start_date="2026-06-12T19:00:00+02:00" location_name="Stadshuset"
|
|
148
|
+
location_address="Storgatan 1, Sundsvall" offers_price=250 offers_currency="SEK"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Rules they enforce: FAQ needs at least one question with an answer; a recipe
|
|
152
|
+
needs a name, ingredients and steps, durations are ISO 8601 (`PT30M`) and
|
|
153
|
+
URLs absolute; an event needs a name and an ISO 8601 `start_date` (include
|
|
154
|
+
the UTC offset), `end_date` not before it, and a price needs
|
|
155
|
+
`offers_currency`. Read the returned `warnings`: a recipe without an image or
|
|
156
|
+
an event without a location is valid but not eligible for Google's rich
|
|
157
|
+
results. Like other content writes they land in the draft — `save: true` or
|
|
158
|
+
`commit_working_copy` when approved.
|
|
159
|
+
|
|
160
|
+
FAQ markup only belongs on a page that visibly presents those questions and
|
|
161
|
+
answers; Google penalizes FAQ markup for content the page does not show.
|
|
162
|
+
|
|
163
|
+
For other types write `json_ld` yourself:
|
|
164
|
+
|
|
119
165
|
```
|
|
120
166
|
update_page page_id="home" patch={
|
|
121
167
|
"json_ld": "{\"@context\":\"https://schema.org\",\"@type\":\"LocalBusiness\",\"name\":\"Acme Studio\",\"url\":\"https://acme.se\",\"telephone\":\"+46812345\",\"address\":{\"@type\":\"PostalAddress\",\"streetAddress\":\"Drottninggatan 1\",\"addressLocality\":\"Stockholm\",\"postalCode\":\"111 51\",\"addressCountry\":\"SE\"}}"
|
|
@@ -124,16 +170,25 @@ update_page page_id="home" patch={
|
|
|
124
170
|
|
|
125
171
|
**Important:** JSON-LD goes in the `json_ld` field as a JSON *string*
|
|
126
172
|
(not a nested object). The renderer injects it inside
|
|
127
|
-
`<script type="application/ld+json">`.
|
|
173
|
+
`<script type="application/ld+json">`. To keep several nodes on one page,
|
|
174
|
+
store them as `{"@context":"https://schema.org","@graph":[…]}` — the
|
|
175
|
+
`add_*_schema` tools do this for you. Read the page first: an update
|
|
176
|
+
replaces the whole `json_ld` value.
|
|
128
177
|
|
|
129
178
|
Common schemas worth adding:
|
|
130
179
|
- Homepage: `LocalBusiness` or `Organization`
|
|
131
180
|
- About: `AboutPage`
|
|
132
181
|
- Contact: `ContactPage`
|
|
133
182
|
- Blog articles: auto-generated from `kind:"article"` + `author`
|
|
134
|
-
-
|
|
183
|
+
- FAQ, recipe and event pages: the `add_*_schema` tools above
|
|
135
184
|
- Products: `Product` with `offers`
|
|
136
185
|
|
|
186
|
+
**Instead of the `schema_markup` workflow.** That workflow runs the model with
|
|
187
|
+
the Organization's own AI key; on a Site without that access it fails with
|
|
188
|
+
`failure_code: "ai_not_enabled"`. Don't retry it. Do the same work here: read
|
|
189
|
+
each published page, pick the one type it genuinely represents (or none),
|
|
190
|
+
apply the tool or `json_ld` above, and tell the user you did it yourself.
|
|
191
|
+
|
|
137
192
|
### 6. robots.txt
|
|
138
193
|
|
|
139
194
|
The default robots.txt allows all crawlers. Update if needed:
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tr-starter-design
|
|
3
|
+
description: Reference for the default design new Typeroll sites start with — CSS variables, spacing, radii, container width, header and footer skeletons. Read it when a site is blank or clearly follows these conventions and you need new markup or CSS to fit in; skip it for sites with their own design.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Starter design system
|
|
7
|
+
|
|
8
|
+
A reference for the *default* design that ships with new Typeroll sites.
|
|
9
|
+
The customer may have replaced it — always inspect the actual header partial
|
|
10
|
+
(`read_partial partial_id="header"`) and an existing page before assuming any
|
|
11
|
+
of this is in use. On a site with its own design, mirror what is there.
|
|
12
|
+
|
|
13
|
+
## CSS variables
|
|
14
|
+
|
|
15
|
+
Colors (set in site settings `colors`; each key becomes `--color-<key>`,
|
|
16
|
+
`text_light` becomes `--color-text-light`):
|
|
17
|
+
--color-primary brand CTAs, links, accents
|
|
18
|
+
--color-secondary supporting brand color (rarely used)
|
|
19
|
+
--color-accent a third highlight (badges, callouts)
|
|
20
|
+
--color-background page background (the body)
|
|
21
|
+
--color-surface slightly raised areas (header, footer, cards)
|
|
22
|
+
--color-text body copy + headings
|
|
23
|
+
--color-text-light secondary text (captions, meta, footer fine-print)
|
|
24
|
+
|
|
25
|
+
For subtle borders/dividers that follow the text color without being heavy,
|
|
26
|
+
use `color-mix(in srgb, var(--color-text) 8%, transparent)` (6% for very soft).
|
|
27
|
+
|
|
28
|
+
Spacing scale:
|
|
29
|
+
--spacing-xs (0.25rem) --spacing-sm (0.5rem) --spacing-md (1rem)
|
|
30
|
+
--spacing-lg (2rem) --spacing-xl (4rem)
|
|
31
|
+
|
|
32
|
+
Layout:
|
|
33
|
+
--container-medium (1080px) Center page content with
|
|
34
|
+
`max-width: var(--container-medium); margin: 0 auto; padding: var(--spacing-md);`
|
|
35
|
+
--container-narrow (720px) and --container-wide (1400px) exist for prose
|
|
36
|
+
and wide layouts.
|
|
37
|
+
|
|
38
|
+
Radii:
|
|
39
|
+
--radius-sm (0.25rem) --radius-md (0.5rem) --radius-lg (1rem)
|
|
40
|
+
|
|
41
|
+
Typography (set in site settings `fonts`):
|
|
42
|
+
--font-heading Use for h1-h6 and brand wordmark
|
|
43
|
+
--font-body Default for everything else
|
|
44
|
+
--font-size-base (the rem baseline)
|
|
45
|
+
|
|
46
|
+
Block-mode pages get these through the blocks and named styles; reach for the
|
|
47
|
+
variables in custom CSS, HTML-mode pages and template markup.
|
|
48
|
+
|
|
49
|
+
## Default header skeleton
|
|
50
|
+
|
|
51
|
+
Surface-tinted bar with a hairline bottom border. Brand text on the left
|
|
52
|
+
in the heading font, bold, slight negative letter-spacing. Nav on the
|
|
53
|
+
right. Last nav item is typically a primary CTA: `background:
|
|
54
|
+
var(--color-primary); color: var(--color-background); border-radius:
|
|
55
|
+
var(--radius-md)`.
|
|
56
|
+
|
|
57
|
+
## Default footer skeleton
|
|
58
|
+
|
|
59
|
+
Three-column responsive grid (brand+tagline / Site links / Get in touch),
|
|
60
|
+
then a fine-print row with copyright + Typeroll attribution. Top border
|
|
61
|
+
uses the same color-mix() recipe as the header.
|
|
62
|
+
|
|
63
|
+
## When to use this
|
|
64
|
+
|
|
65
|
+
- A brand new site, or one where the header partial still matches this
|
|
66
|
+
layout.
|
|
67
|
+
- A page or block where the customer wants something that "fits the rest
|
|
68
|
+
of the site" and the rest follows these conventions.
|
|
69
|
+
|
|
70
|
+
## When NOT to use this
|
|
71
|
+
|
|
72
|
+
- The customer has a totally different design (e.g. dark mode, custom
|
|
73
|
+
utility classes, no CSS variables, migrated theme).
|
|
74
|
+
- The customer says "use this brand color" — honor their literal request
|
|
75
|
+
instead of substituting a CSS variable.
|
|
76
|
+
- Mid-edit on an existing page that doesn't follow these conventions —
|
|
77
|
+
match what's already there.
|
|
78
|
+
- Choosing a new palette or fonts is brand work (the MCP recipe `tr-brand`),
|
|
79
|
+
not this reference.
|