@typeroll/mcp-server 0.12.0 → 0.14.0
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 +8 -1
- package/README.md +30 -10
- package/dist/index.js +1 -14
- package/dist/resolve-site-id.js +29 -0
- package/dist/tools/settings.js +1 -0
- package/package.json +1 -1
- package/skills/tr-brand.md +18 -0
- package/skills/tr-imagegen.md +6 -1
package/AGENTS.md
CHANGED
|
@@ -84,7 +84,14 @@ maps to one HTTP endpoint; the actual logic runs in the customer's portal
|
|
|
84
84
|
before working with blocks — never hardcode block ids or field names,
|
|
85
85
|
the available set is per-site.
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Three specifics worth knowing:
|
|
88
|
+
- **`core/section` is natively full-bleed on block pages** (since
|
|
89
|
+
template_capabilities_version 0.14.0): the section's background runs
|
|
90
|
+
edge-to-edge and meets the header with zero gap; content inside is
|
|
91
|
+
constrained by the section's own inner container (`width` field:
|
|
92
|
+
narrow/normal/wide/full). Never use 100vw negative-margin hacks.
|
|
93
|
+
Top-level blocks that are NOT sections still get a classic centered
|
|
94
|
+
container as fallback.
|
|
88
95
|
- **`core/html`** is the raw-HTML escape hatch for block-mode pages —
|
|
89
96
|
one `html` field rendered verbatim (then sanitized like HTML-mode
|
|
90
97
|
content). Use it for the genuinely unique thing no block covers: a
|
package/README.md
CHANGED
|
@@ -96,11 +96,20 @@ the full reference + concrete operation recipes.
|
|
|
96
96
|
- **Discovery** — `get_site`, `update_site` (name/slug/domain), `list_versions`,
|
|
97
97
|
`read_site_settings`, `update_site_settings`.
|
|
98
98
|
- **Pages** — list, read, batch-read, create, update (PATCH), replace
|
|
99
|
-
(PUT), batch-update, delete, clone, get-
|
|
99
|
+
(PUT), batch-update, delete, clone, get-preview, `set_page_mode`
|
|
100
|
+
(flip between blocks/html), `convert_page_to_blocks`.
|
|
101
|
+
- **Blocks (instances)** — `get_page_blocks`, `add_block`,
|
|
102
|
+
`update_block`, `move_block`, `remove_block`, `duplicate_block`,
|
|
103
|
+
`set_block_responsive`. All take a `target` (page, partial, page
|
|
104
|
+
template, or collection item-template), so one tool family edits
|
|
105
|
+
every block container.
|
|
100
106
|
- **Global blocks (partials)** — list (summary mode by default), read,
|
|
101
107
|
create free block, update, replace, delete, find-pages-using-block.
|
|
102
|
-
- **Block types** — list, read,
|
|
103
|
-
|
|
108
|
+
- **Block types** — list, read, create, update, delete,
|
|
109
|
+
find-pages-using-block-type, plus `.tcblocks` export/import. Custom
|
|
110
|
+
client-side JS (`script`) is honoured only when the site has enabled
|
|
111
|
+
"Allow AI to write block scripts" (a human-set portal setting) —
|
|
112
|
+
otherwise it's stripped with a warning.
|
|
104
113
|
- **Collections + items** — create/update/delete the collection schema
|
|
105
114
|
itself (incl. `route_template` for per-item URLs); list/read/batch-
|
|
106
115
|
read/create/update/delete items.
|
|
@@ -115,7 +124,13 @@ the full reference + concrete operation recipes.
|
|
|
115
124
|
vision model).
|
|
116
125
|
- **Redirects** — list, create, delete. Plus automatic 301 on slug change.
|
|
117
126
|
- **Forms** — list, read, create, update, delete, list submissions.
|
|
118
|
-
|
|
127
|
+
Read/create responses include `submit_token` + `submit_url` — a plain
|
|
128
|
+
`<form method="POST">` with a hidden `_token` input is a fully working
|
|
129
|
+
no-JS embed (the endpoint answers form posts with an HTML
|
|
130
|
+
confirmation page).
|
|
131
|
+
- **Settings** — read + patch, including `scripts_head` /
|
|
132
|
+
`scripts_body_end` / `custom_css` (trusted because the caller holds an
|
|
133
|
+
API key; the in-portal chat AI does NOT get these).
|
|
119
134
|
- **Search** — `search_pages` with substring or regex.
|
|
120
135
|
- **Bulk** — `bulk_replace_text` with dry-run.
|
|
121
136
|
- **Branches** — create, read, delete, merge. Branch deploys get their
|
|
@@ -138,17 +153,21 @@ The MCP server is purely an ergonomics layer on top of that.
|
|
|
138
153
|
|
|
139
154
|
## Security model
|
|
140
155
|
|
|
141
|
-
- API keys are **site-scoped
|
|
142
|
-
|
|
156
|
+
- API keys are **site-scoped or org-scoped** (see "Key scopes" above) —
|
|
157
|
+
enforced server-side. A site-scoped key cannot touch any other site;
|
|
158
|
+
an org-scoped key reaches the org's own sites plus sites explicitly
|
|
159
|
+
shared into the org, with the share's permission level applied.
|
|
143
160
|
- All write calls (`POST`, `PUT`, `PATCH`, `DELETE`) are **audit-logged**
|
|
144
161
|
with the key prefix, IP, method, path, and status. Reads are not
|
|
145
162
|
logged (cost vs. value).
|
|
146
163
|
- **Rate limits**: 600 reads/min, 60 writes/min per key. 429 responses
|
|
147
164
|
carry `Retry-After` headers.
|
|
148
165
|
- **HTML sanitization** happens at save time on the server — `<script>`,
|
|
149
|
-
event handlers, and `javascript:` URLs are stripped
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
event handlers, and `javascript:` URLs are stripped from page/partial
|
|
167
|
+
content (including `core/html` block output). The scriptable surfaces
|
|
168
|
+
(`scripts_*`, `custom_css`, block-type `script`) are deliberate
|
|
169
|
+
exceptions: the first are writable with an API key, and block-type
|
|
170
|
+
scripts additionally require the site's per-site opt-in.
|
|
152
171
|
- Keys can be **revoked** at any time from the portal. Revocation takes
|
|
153
172
|
effect on the next request (no in-flight requests get cancelled, but
|
|
154
173
|
the next one returns 401).
|
|
@@ -158,7 +177,8 @@ The MCP server is purely an ergonomics layer on top of that.
|
|
|
158
177
|
- Full end-to-end production setup recipe with troubleshooting:
|
|
159
178
|
[docs/claude-code-mcp-setup.md](../../docs/claude-code-mcp-setup.md)
|
|
160
179
|
- Agent operations briefing: [AGENTS.md](./AGENTS.md)
|
|
161
|
-
- Boilerplate skills (
|
|
180
|
+
- Boilerplate skills (site building, brand, forms, SEO, blog,
|
|
181
|
+
collections, migration, image generation, redesign, …):
|
|
162
182
|
[skills/](./skills/)
|
|
163
183
|
|
|
164
184
|
## License
|
package/dist/index.js
CHANGED
|
@@ -15,22 +15,9 @@
|
|
|
15
15
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
16
16
|
import { TyperollClient } from './client.js';
|
|
17
17
|
import { runInstallSkillsCli } from './install-skills.js';
|
|
18
|
+
import { resolveSiteId } from './resolve-site-id.js';
|
|
18
19
|
import { buildServer } from './server.js';
|
|
19
20
|
const VERSION = '0.7.12';
|
|
20
|
-
async function resolveSiteId(client) {
|
|
21
|
-
const fromEnv = process.env.TYPEROLL_SITE_ID?.trim();
|
|
22
|
-
if (fromEnv)
|
|
23
|
-
return fromEnv;
|
|
24
|
-
const res = await client.rootGet('sites');
|
|
25
|
-
if (!res.sites || res.sites.length === 0) {
|
|
26
|
-
throw new Error('No sites returned for this API key. Check the key is valid.');
|
|
27
|
-
}
|
|
28
|
-
if (res.sites.length > 1) {
|
|
29
|
-
throw new Error(`This key authorises ${res.sites.length} sites; set TYPEROLL_SITE_ID to pick one. ` +
|
|
30
|
-
'Org-scoped keys span multiple sites — stdio currently binds to one at a time.');
|
|
31
|
-
}
|
|
32
|
-
return res.sites[0].id;
|
|
33
|
-
}
|
|
34
21
|
function bail(message) {
|
|
35
22
|
console.error(`typeroll-mcp: ${message}`);
|
|
36
23
|
process.exit(1);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Discover which site a stdio invocation binds to.
|
|
2
|
+
//
|
|
3
|
+
// TYPEROLL_SITE_ID wins when set. Otherwise we ask GET /v1/sites: a
|
|
4
|
+
// site-scoped key returns exactly one entry; an org-scoped key can return
|
|
5
|
+
// many, in which case TYPEROLL_SITE_ID is required so this stdio invocation
|
|
6
|
+
// maps onto one specific site.
|
|
7
|
+
export async function resolveSiteId(client) {
|
|
8
|
+
const fromEnv = process.env.TYPEROLL_SITE_ID?.trim();
|
|
9
|
+
if (fromEnv)
|
|
10
|
+
return fromEnv;
|
|
11
|
+
const res = await client.rootGet('sites');
|
|
12
|
+
if (!res.sites || res.sites.length === 0) {
|
|
13
|
+
throw new Error('No sites returned for this API key. Check the key is valid.');
|
|
14
|
+
}
|
|
15
|
+
if (res.sites.length > 1) {
|
|
16
|
+
throw new Error(`This key authorises ${res.sites.length} sites; set TYPEROLL_SITE_ID to pick one. ` +
|
|
17
|
+
'Org-scoped keys span multiple sites — stdio currently binds to one at a time.');
|
|
18
|
+
}
|
|
19
|
+
// Defense in depth: portals before the /v1/sites org-key fix returned a
|
|
20
|
+
// single placeholder entry with an empty id for org-scoped keys. Binding
|
|
21
|
+
// to "" would make every subsequent call fail incomprehensibly — reject
|
|
22
|
+
// it with an actionable message instead.
|
|
23
|
+
const id = res.sites[0].id?.trim();
|
|
24
|
+
if (!id) {
|
|
25
|
+
throw new Error('The portal returned a site with an empty id (org-scoped key against an older portal). ' +
|
|
26
|
+
'Set TYPEROLL_SITE_ID to pick a site explicitly, or upgrade the portal.');
|
|
27
|
+
}
|
|
28
|
+
return id;
|
|
29
|
+
}
|
package/dist/tools/settings.js
CHANGED
|
@@ -29,6 +29,7 @@ export const settingsTools = [
|
|
|
29
29
|
tagline: z.string().optional(),
|
|
30
30
|
logo: z.string().optional(),
|
|
31
31
|
favicon: z.string().optional(),
|
|
32
|
+
apple_touch_icon: z.string().optional().describe('URL to a 180x180 PNG for iOS/Android home-screen bookmarks. Emitted as <link rel="apple-touch-icon">.'),
|
|
32
33
|
default_seo_suffix: z.string().optional(),
|
|
33
34
|
language: z.string().optional().describe('BCP-47 tag (e.g. "en", "sv", "en-GB"). Drives <html lang> on the rendered site.'),
|
|
34
35
|
robots_txt: z.string().optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typeroll/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Model Context Protocol server for the Typeroll public API. Use with Claude Code or any MCP-compatible client to manage a Typeroll site.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/skills/tr-brand.md
CHANGED
|
@@ -108,6 +108,24 @@ update_site_settings {
|
|
|
108
108
|
|
|
109
109
|
Read back to confirm: `read_site_settings`.
|
|
110
110
|
|
|
111
|
+
### Site icons — always propose them, never leave them empty
|
|
112
|
+
|
|
113
|
+
Every site gets a favicon + apple touch icon as part of brand setup:
|
|
114
|
+
|
|
115
|
+
1. **Brand assets exist** (favicon-*.png, app icon, symbol): upload the
|
|
116
|
+
right sizes via `upload_media_inline` (favicon: 32–64px PNG or SVG;
|
|
117
|
+
apple touch icon: 180×180 PNG) and set BOTH in one call:
|
|
118
|
+
`update_site_settings { "favicon": "<url>", "apple_touch_icon": "<url>" }`.
|
|
119
|
+
2. **No icon assets:** derive a proposal instead of skipping — crop the
|
|
120
|
+
logo's symbol to a square and resize locally (`sips -z 180 180 in.png
|
|
121
|
+
--out icon-180.png` on macOS, or ImageMagick), or generate a simple
|
|
122
|
+
icon candidate with the imagegen lab (see `tr-imagegen`; respect the
|
|
123
|
+
style profile, no text). Upload, set, and tell the user it's a
|
|
124
|
+
proposal they can swap.
|
|
125
|
+
|
|
126
|
+
A site shipping with the browser's default globe icon is a build gap —
|
|
127
|
+
treat icons like the logo: part of done.
|
|
128
|
+
|
|
111
129
|
## Step 5 — Update partials to use the new palette
|
|
112
130
|
|
|
113
131
|
Partials that hardcoded hex colors need updating. Fetch the header:
|
package/skills/tr-imagegen.md
CHANGED
|
@@ -135,7 +135,12 @@ connector in Claude Desktop, for editors who don't use Claude Code.)
|
|
|
135
135
|
## Pitfalls
|
|
136
136
|
|
|
137
137
|
- **Never put text in generated images** (headlines, buttons) — text is
|
|
138
|
-
HTML's job; generated text renders as gibberish in non-English.
|
|
138
|
+
HTML's job; generated text renders as gibberish in non-English. And
|
|
139
|
+
models sneak text onto surfaces where it "feels natural" — jerseys,
|
|
140
|
+
signs, banners, packages — even when the prompt doesn't ask for any.
|
|
141
|
+
Forbid it explicitly in the prompt ("plain unmarked clothing, no
|
|
142
|
+
text, no letters, no numbers anywhere in the image") and make the
|
|
143
|
+
same rule part of every site's style profile.
|
|
139
144
|
- **Don't commit `.env` or `images/lab/`** — both are gitignored by the
|
|
140
145
|
kit convention; keep it that way.
|
|
141
146
|
- **Cost discipline:** generation costs real money per image. Batch
|