@typeroll/mcp-server 0.7.4

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.
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: tr-images
3
+ description: Use when the user asks for an image, hero, illustration, or logo to be created and embedded in a Typeroll page. Covers the two-step signed-URL upload flow so the agent doesn't try to POST bytes through the CMS API (it can't).
4
+ ---
5
+
6
+ # Add images to a Typeroll site
7
+
8
+ The Typeroll API does NOT accept image bytes directly. Uploads go
9
+ through a signed PUT URL straight to Cloudflare R2, and the API only
10
+ sees the metadata. Two-step flow:
11
+
12
+ ## Recipe
13
+
14
+ ### 1. Get an image
15
+
16
+ Options, in order of preference:
17
+
18
+ a. **Reuse an existing one.** `list_media` returns CDN URLs for every
19
+ image already on this site. Search the list before generating
20
+ anything — saves bandwidth and keeps the visual catalog tight.
21
+
22
+ b. **Generate locally.** The user's Claude Code installation has
23
+ access to whatever image-gen tools they've configured (DALL-E,
24
+ Midjourney, Stable Diffusion, Replicate, etc.). Generate and save
25
+ to a tempfile.
26
+
27
+ c. **Source from the web** with appropriate licensing (the user is
28
+ responsible for clearing rights). Save locally before upload.
29
+
30
+ ### 2. Mint a signed upload URL
31
+
32
+ ```
33
+ create_upload_url filename="hero-services.png"
34
+ content_type="image/png"
35
+ size=<bytes>
36
+ alt_text="Office worker reviewing documents at a desk"
37
+ ```
38
+
39
+ Returns:
40
+
41
+ ```json
42
+ {
43
+ "upload_url": "https://...r2.cloudflarestorage.com/.../signed-...",
44
+ "cdn_url": "https://cdn.example.com/orgs/.../images/...png",
45
+ "key": "orgs/.../images/...png",
46
+ "media_id": "abc123",
47
+ "expires_in": 300
48
+ }
49
+ ```
50
+
51
+ The signed URL is valid for 5 minutes. The media doc is already
52
+ registered — even before the upload completes — so it'll show in
53
+ `list_media` immediately.
54
+
55
+ ### 3. PUT the bytes
56
+
57
+ Outside the MCP, hit the signed URL directly:
58
+
59
+ ```
60
+ PUT <upload_url>
61
+ Content-Type: <same content_type as in step 2>
62
+ Body: <file bytes>
63
+ ```
64
+
65
+ In a shell:
66
+
67
+ ```bash
68
+ curl -X PUT "<upload_url>" \
69
+ -H "Content-Type: image/png" \
70
+ --data-binary @hero-services.png
71
+ ```
72
+
73
+ Or from JS (Claude Code can run a one-line script):
74
+
75
+ ```js
76
+ await fetch(uploadUrl, {
77
+ method: 'PUT',
78
+ headers: { 'Content-Type': contentType },
79
+ body: await fs.readFile(path),
80
+ });
81
+ ```
82
+
83
+ A 200 OK from R2 means the image is now live at `cdn_url`.
84
+
85
+ ### 4. Patch metadata (alt text, etc.)
86
+
87
+ You set `alt_text` at create time, but if you generate the image first
88
+ and only THEN realize what to caption it as, patch later:
89
+
90
+ ```
91
+ update_media media_id=<id> alt_text="..." filename="hero-services-v2.png"
92
+ ```
93
+
94
+ ### 4b. Fill missing alt-text on existing media
95
+
96
+ When a customer has uploaded a bunch of images without alt-text (very
97
+ common after a WP migration), don't make it up — use vision:
98
+
99
+ ```
100
+ list_media → find items with empty alt_text
101
+ suggest_alt_text_context media_id=<id> → returns { image_url, suggested_prompt,
102
+ language, used_on_pages, current_alt_text }
103
+ # Pass image_url + the returned suggested_prompt to YOUR OWN vision
104
+ # capability (you can fetch the URL and pass bytes to vision).
105
+ update_media media_id=<id> alt_text="<what vision returned>"
106
+ ```
107
+
108
+ The prompt is tuned for SEO-grade output: short (5-15 words), no "image
109
+ of / picture of" filler, written in the site's content language,
110
+ decorative images return empty string. Run it sequentially on a
111
+ list_media batch and you can fix alt-text gaps across a whole site
112
+ without burning your context on prompt design. The platform does NOT
113
+ run vision on your behalf — your model does, your usage.
114
+
115
+ ### 5. Embed in a page
116
+
117
+ `read_page` the target, insert `<img>` in the right spot:
118
+
119
+ ```html
120
+ <img src="<cdn_url>"
121
+ alt="<alt_text>"
122
+ style="width: 100%; height: auto; display: block; margin: 2rem 0;" />
123
+ ```
124
+
125
+ Then `update_page` with the new HTML. Or, if you're generating a hero
126
+ for a brand-new page, include the `<img>` directly in `create_page`'s
127
+ `html_content`.
128
+
129
+ ## Pitfalls
130
+
131
+ - **Always set `alt_text`.** Empty alt is bad for SEO + accessibility.
132
+ Default to a one-sentence description of what's in the image.
133
+ - **`<script>` etc. in SVGs.** The page sanitizer drops `<script>`
134
+ inside SVG, so an icon set that includes script-based animations
135
+ won't render correctly. Use static SVG or a JPG/PNG export.
136
+ - **CSS background-image references aren't dedup'd.** If you set the
137
+ same image as a CSS background on multiple pages, the alt-text +
138
+ metadata are page-irrelevant. The sanitizer allows
139
+ `background-image: url(...)` in inline styles, but think about
140
+ whether an `<img>` is actually better.
141
+ - **Source URL leakage.** If you generated the image from a prompt
142
+ that contains internal info, don't bake that prompt into the
143
+ filename. Use a descriptive but generic filename.
144
+
145
+ ## Format choice
146
+
147
+ - **PNG** for logos, icons with hard edges, anything with text.
148
+ - **JPG** for photos. Smaller file, better for big hero images.
149
+ - **WebP** if the target audience runs modern browsers (95%+ in 2026).
150
+ - **SVG** for icons + simple illustrations. Vector scales perfectly.
151
+ - **PDF** is supported by `create_upload_url` for document downloads;
152
+ link with `<a href>`, not `<img>`.
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: tr-migrate-wp
3
+ description: Use when the user asks to migrate a WordPress site to Typeroll, mentions wp-json, or names a WP source URL. Walks the WP REST API, rebuilds pages in the target site's design, transfers media, sets redirects, leaves everything as drafts for human review.
4
+ ---
5
+
6
+ # Migrate from WordPress to Typeroll
7
+
8
+ The platform's in-portal migration workflow is the "managed" path for
9
+ customers who want one-click. This skill is the "power-user" path: you
10
+ do it locally, mix data sources freely, and the user (consultant /
11
+ agency) reviews each step in their terminal.
12
+
13
+ ## Preconditions
14
+
15
+ - `@typeroll/mcp-server` configured with a valid `TYPEROLL_API_KEY`.
16
+ - The source WP site has `/wp-json` reachable (Google for "wordpress
17
+ REST API disabled" if not — common for hardened hosts).
18
+ - The Typeroll target site exists. New, blank sites with the
19
+ starter design work best. If the target already has content, you
20
+ must NOT clobber it — always `list_pages` first and only write to
21
+ slugs that don't already exist.
22
+
23
+ ## Recipe
24
+
25
+ ### 1. Probe and inventory
26
+
27
+ ```
28
+ fetch <wp-url>/wp-json # confirm REST is on
29
+ fetch <wp-url>/wp-sitemap.xml or /sitemap.xml # URL inventory
30
+ ```
31
+
32
+ Build a list of every URL you intend to migrate. WP custom post types
33
+ need their REST endpoint (e.g. `/wp-json/wp/v2/news?per_page=100`),
34
+ walking `X-WP-TotalPages` to paginate.
35
+
36
+ ### 2. Learn the target's design
37
+
38
+ ```
39
+ get_site
40
+ read_site_settings # colors, fonts, voice cues
41
+ list_partials # header / footer / shared
42
+ read_partial partial_id="header" # nav structure
43
+ list_pages limit=5
44
+ batch_read_pages page_ids=[<2-3 representative ids>] # see actual conventions
45
+ ```
46
+
47
+ Don't skip this. Imposing a stranger's design on a customer's site is
48
+ the biggest avoidable mistake.
49
+
50
+ ### 3. Migrate one page at a time, draft status
51
+
52
+ For each source URL:
53
+
54
+ a. Fetch from WP. Prefer the helper plugin's authenticated endpoint
55
+ (`/wp-json/typeroll/v1/...`) if available — it bypasses
56
+ `show_in_rest=false` and returns ACF + builder fields. Otherwise
57
+ fall back to `/wp-json/wp/v2/<post-type>?slug=<slug>`.
58
+
59
+ b. Clean the HTML. Strip Elementor / Gutenberg / Breakdance class
60
+ soup. Drop empty `<div>` and `<span>` wrappers. Keep semantic tags,
61
+ tables, iframes from known hosts (YouTube / Vimeo / Calendly).
62
+
63
+ c. Migrate referenced images:
64
+ - For each `<img src>` and CSS `background-image: url()`:
65
+ 1. Download the source image locally.
66
+ 2. `create_upload_url filename=... content_type=...` → returns
67
+ `{ upload_url, cdn_url, media_id }`.
68
+ 3. PUT the bytes to `upload_url` (curl or fetch with the same
69
+ content type).
70
+ 4. Replace the `src` with `cdn_url` in the rewritten HTML.
71
+ - Use `update_media media_id=... alt_text="..."` to set a real alt
72
+ text (existing WP `alt` attribute or `aria-label`; fall back to
73
+ filename only as a last resort).
74
+
75
+ d. Reconstruct in the target's design. The cleaned HTML is rarely
76
+ ready to ship — typical fixes: replace WP `wp-block-*` classes
77
+ with the target's CSS variables; turn Elementor sections into
78
+ plain `<section>` with the target's spacing; fix headings so the
79
+ page has exactly one `<h1>`. If you're confident, batch these
80
+ through `bulk_replace_text` with `dry_run: true` first.
81
+
82
+ e. Write the page as a draft:
83
+
84
+ ```
85
+ create_page title="..." slug="<preserved-from-wp>"
86
+ html_content="<reconstructed>"
87
+ status="draft" kind="article" author="..."
88
+ seo_title="..." seo_description="..."
89
+ ```
90
+
91
+ **Preserve the source URL.** WP post URLs like
92
+ `/2024/01/foo-bar/` go in as `slug: "2024/01/foo-bar"`. The
93
+ slug supports slashes; encode the WP permalink structure verbatim
94
+ when the customer wants existing links to keep working.
95
+
96
+ ### 4. Redirects
97
+
98
+ After migration, every URL the agent didn't preserve verbatim needs a
99
+ redirect:
100
+
101
+ ```
102
+ create_redirect from_path="/old-services" to_path="/services"
103
+ ```
104
+
105
+ Walk the inventory; for each URL: did it become a page with the same
106
+ path? If yes, no redirect. If renamed, `create_redirect`. If
107
+ intentionally dropped, mark it excluded in your notes (the customer
108
+ should sign off on every dropped URL).
109
+
110
+ ### 5. Preview + review with the user
111
+
112
+ ```
113
+ get_preview_link page_id=<id> # one URL the user can click
114
+ ```
115
+
116
+ Open in the user's browser. The preview navigates the whole site from
117
+ one mint. Iterate on feedback: pages, header, footer.
118
+
119
+ ### 6. Ship
120
+
121
+ When the user signs off:
122
+
123
+ ```
124
+ # Bulk-publish drafts that look right
125
+ batch_update_pages updates=[{page_id, patch:{status:"published"}}, ...]
126
+
127
+ # Deploy
128
+ trigger_deploy
129
+ get_deploy_status job_id=<id> # poll
130
+ ```
131
+
132
+ ## Pitfalls
133
+
134
+ - **Don't publish during migration.** Always import as `draft`. Even
135
+ if the agent is confident, the customer needs the chance to spot-check.
136
+ - **WP slugs sometimes drift.** A post saved with slug `foo-bar` may
137
+ have been served at `/2024/01/foo-bar/` due to the permalink
138
+ structure. The full URL is what users see in Google; preserve that,
139
+ not the bare slug.
140
+ - **Image bandwidth.** R2 upload is metered. Use `find_pages_matching`
141
+ contains="<old-domain>" on already-imported content to spot images
142
+ that weren't transferred.
143
+ - **WP-specific JSON-LD** (Yoast, Rank Math) is usually wrong after a
144
+ redesign because it references old URLs. Strip it; let Typeroll
145
+ emit fresh Article/Page schemas via `kind: 'article'` + `author`.
146
+
147
+ ## When the source isn't WordPress
148
+
149
+ The same shape applies for any source — Squarespace export, custom
150
+ CMS, scraped HTML, CSV. Replace step 1's "WP REST" probe with whatever
151
+ discovery the source supports, and the rest of the recipe is unchanged.
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: tr-redesign-branch
3
+ description: Use when the user asks to redesign, modernize, or restructure a Typeroll site (or a section of it). Forces branch-isolated work so the live site stays untouched until the redesign is approved.
4
+ ---
5
+
6
+ # Redesign a site without breaking the live one
7
+
8
+ Site-wide changes are exactly where copy-on-write branches earn their
9
+ keep. This skill enforces the discipline: every redesign happens on a
10
+ branch, preview-checked end-to-end, merged only after user sign-off.
11
+
12
+ ## Recipe
13
+
14
+ ### 1. Discover (always)
15
+
16
+ ```
17
+ get_site
18
+ read_site_settings
19
+ read_partial partial_id="header"
20
+ read_partial partial_id="footer"
21
+ list_pages limit=20
22
+ batch_read_pages page_ids=[<top 3-5 pages>] # see actual conventions
23
+ list_partials # what free blocks exist
24
+ list_collections # any data we need to consider
25
+ ```
26
+
27
+ Write the user a short read-back: *"This is a 12-page agency site
28
+ using CSS variables, primary color #1e40af, Inter heading + Source
29
+ Sans body. Existing pages are content-dense, single-column. Main nav
30
+ has 5 items including a CTA. I'd suggest..."*
31
+
32
+ Confirm direction before touching anything.
33
+
34
+ ### 2. Create a branch
35
+
36
+ ```
37
+ create_branch name="<descriptive name>"
38
+ ```
39
+
40
+ Save the response's `id` — pass it as `version=<id>` on every
41
+ subsequent call. Branches default `robots_blocked: true` so a
42
+ half-finished redesign won't be indexed.
43
+
44
+ ### 3. Iterate on the branch
45
+
46
+ For each redesign step:
47
+
48
+ a. Make the change with `?version=<branch-id>`. Updates here don't
49
+ touch main:
50
+
51
+ ```
52
+ update_partial partial_id="header" patch={...} version="<branch>"
53
+ update_page page_id=home patch={...} version="<branch>"
54
+ ```
55
+
56
+ b. Preview after every meaningful change:
57
+
58
+ ```
59
+ get_preview_link page_id=home version="<branch>"
60
+ ```
61
+
62
+ Send the URL to the user. The preview navigates the whole branch
63
+ from one mint.
64
+
65
+ c. Iterate on feedback. Common rounds: headline tightening, color
66
+ tweaks, swapping hero images.
67
+
68
+ ### 4. Site-wide changes through partials, not pages
69
+
70
+ If the redesign touches every page (e.g. new global header, new
71
+ footer, new CTA bar) — edit a partial, not 23 pages. Before editing a
72
+ shared block, check the blast radius:
73
+
74
+ ```
75
+ find_pages_using_block partial_id="header" version="<branch>"
76
+ ```
77
+
78
+ This returns every page that would show the change. Communicate that
79
+ to the user before the save.
80
+
81
+ ### 5. Bulk content cleanups via dry-run first
82
+
83
+ If the redesign requires content rewrites (e.g. "remove every mention
84
+ of the old company name"), use the bulk tool with dry-run:
85
+
86
+ ```
87
+ search_pages contains="OldCo" version="<branch>"
88
+ bulk_replace_text pattern="OldCo" replacement="NewCo" dry_run=true version="<branch>"
89
+ # Show the user the sample_diffs
90
+ bulk_replace_text pattern="OldCo" replacement="NewCo" dry_run=false version="<branch>"
91
+ ```
92
+
93
+ ### 6. Approval round
94
+
95
+ Send the user a final preview link:
96
+
97
+ ```
98
+ get_preview_link page_id=home version="<branch>" ttl_seconds=86400
99
+ ```
100
+
101
+ The 24h TTL gives them time to share with stakeholders. Wait for an
102
+ explicit "looks good, ship it."
103
+
104
+ ### 7. Merge + deploy
105
+
106
+ ```
107
+ merge_branch version_id="<branch>" # branch's diffs land on main
108
+ trigger_deploy
109
+ get_deploy_status job_id=<id> # poll until succeeded
110
+ ```
111
+
112
+ Optionally, after a successful deploy:
113
+
114
+ ```
115
+ delete_branch version_id="<branch>" # tidy up
116
+ ```
117
+
118
+ (You can also leave the branch around as a record of the redesign;
119
+ disk cost is tiny.)
120
+
121
+ ## Pitfalls
122
+
123
+ - **Forgetting `version=` on writes.** Every call you make on the
124
+ branch must include `version=<branch-id>`. A missing one writes
125
+ straight to main — silent and bad.
126
+ - **Skipping discovery.** "Modernize" without first reading the site
127
+ produces a confidently-out-of-place result. Always sample existing
128
+ pages.
129
+ - **Editing the in-portal preview URL by mistake.** That's the user's
130
+ own preview, not yours. `get_preview_link` returns a signed
131
+ external URL — always use that for sharing.
132
+ - **Auto-merge.** Don't `merge_branch` without explicit user sign-off.
133
+ Once merged, the only undo is another branch + reverse edits.
134
+ - **Header rewrites that drop the brand block.** Even when the
135
+ redesign is dramatic, preserve the brand mark + the nav skeleton
136
+ unless the user said to redo them.
137
+
138
+ ## When to NOT use a branch
139
+
140
+ Tiny edits — "fix the typo on the About page" — don't need a branch.
141
+ The in-portal chat handles those directly on main. This skill is for
142
+ work where:
143
+
144
+ - The user might want to walk away mid-redesign and come back later
145
+ - Multiple changes need to ship together
146
+ - The site is high-traffic and "broken for an hour" is unacceptable
147
+ - Stakeholder review across multiple pages is expected
148
+
149
+ If none of those apply, edit main directly and move on.