@typeroll/mcp-server 0.45.69 → 0.45.73
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/compact-tools.js +2 -3
- package/dist/server.js +1 -1
- package/dist/structured-data.js +1 -2
- package/dist/tools/apps.js +1 -1
- package/dist/tools/block-types.js +5 -5
- package/dist/tools/content-types.js +4 -4
- package/dist/tools/email.js +1 -1
- package/dist/tools/extensions.js +5 -5
- package/dist/tools/forms.js +1 -1
- package/dist/tools/owner-review.js +2 -2
- package/dist/tools/page-blocks.js +2 -2
- package/dist/tools/pages.js +4 -4
- package/dist/tools/partials.js +1 -1
- package/dist/tools/reusable-blocks.js +2 -2
- package/dist/tools/settings.js +3 -3
- package/dist/tools/styles.js +3 -3
- package/dist/tools/workflows.js +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace.js +1 -1
- package/package.json +2 -3
package/dist/compact-tools.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
3
2
|
import { fail, ok } from './tools/helpers.js';
|
|
4
3
|
export const COMPACT_INSTRUCTIONS = `Typeroll CMS. Use search_tools to find a task's tools, describe_tool to read one exact schema, then call_read_tool, call_write_tool or call_admin_tool. Discover before you write: identify Site/Version, capabilities and enabled app documentation. Read only the relevant skill or guide section; do not load the full manual by default. Larger changes: create_branch and keep the same version. Working copy, saved content and live output differ. Publishing and other external effects require the user's authorization; tool access alone is not approval.`;
|
|
5
4
|
/** Stable discovery surface: no dynamic registration or client list-change support needed. */
|
|
@@ -18,12 +17,12 @@ export function compactTools(catalog) {
|
|
|
18
17
|
}),
|
|
19
18
|
local('describe_tool', 'Read one tool’s complete description, effect and JSON input schema before calling it.', { name: z.string().max(100) }, async (args) => {
|
|
20
19
|
const tool = byName.get(String(args.name));
|
|
21
|
-
return tool ? ok({ name: tool.name, effect: tool.effect, description: tool.description, inputSchema:
|
|
20
|
+
return tool ? ok({ name: tool.name, effect: tool.effect, description: tool.description, inputSchema: z.toJSONSchema(z.object(tool.schema).meta({ additionalProperties: false }), { target: 'draft-7', io: 'input' }) }) : fail(new Error('Unknown tool. Use search_tools.'));
|
|
22
21
|
}),
|
|
23
22
|
...['read', 'write', 'admin'].map(effect => ({
|
|
24
23
|
name: `call_${effect}_tool`, effect,
|
|
25
24
|
description: effect === 'read' ? 'Call a discovered read-only tool with its exact arguments. Does not accept write/admin tools.' : `Call a discovered ${effect} tool. May ${effect === 'admin' ? 'change app or email configuration, send a test email or activate releases' : 'save, delete, publish or otherwise change state'}. Inspect describe_tool first and follow the user’s authorization.`,
|
|
26
|
-
schema: { name: z.string().max(100), arguments: z.record(z.unknown()).default({}) },
|
|
25
|
+
schema: { name: z.string().max(100), arguments: z.record(z.string(), z.unknown()).default({}) },
|
|
27
26
|
invoke: async (args) => {
|
|
28
27
|
const tool = byName.get(String(args.name));
|
|
29
28
|
if (!tool || tool.effect !== effect)
|
package/dist/server.js
CHANGED
|
@@ -324,7 +324,7 @@ export function buildServer(options) {
|
|
|
324
324
|
}
|
|
325
325
|
for (const tool of options.toolMode === 'compact' ? compactTools(catalog) : catalog) {
|
|
326
326
|
register(tool.name, {
|
|
327
|
-
description: tool.description, inputSchema: tool.schema,
|
|
327
|
+
description: tool.description, inputSchema: z.object(tool.schema).meta({ additionalProperties: false }),
|
|
328
328
|
annotations: { readOnlyHint: tool.effect === 'read', destructiveHint: tool.effect !== 'read', openWorldHint: true },
|
|
329
329
|
}, tool.invoke);
|
|
330
330
|
}
|
package/dist/structured-data.js
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
// of the same @type is replaced (re-running a tool updates it), other nodes are
|
|
13
13
|
// kept in an @graph, so an FAQ added later does not erase a LocalBusiness.
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
-
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
16
15
|
/** Invalid input or an unusable existing json_ld; `issues` name the field. */
|
|
17
16
|
export class StructuredDataError extends Error {
|
|
18
17
|
issues;
|
|
@@ -170,7 +169,7 @@ export const STRUCTURED_DATA = {
|
|
|
170
169
|
* outside MCP (the portal chat passes it to the model as input_schema).
|
|
171
170
|
*/
|
|
172
171
|
export function structuredDataInputSchema(kind) {
|
|
173
|
-
const schema =
|
|
172
|
+
const schema = z.toJSONSchema(z.object({ page_id: z.string().min(1), ...STRUCTURED_DATA[kind].shape }).meta({ additionalProperties: false }), { target: 'draft-7', io: 'input' });
|
|
174
173
|
const { $schema: _schema, ...rest } = schema;
|
|
175
174
|
return rest;
|
|
176
175
|
}
|
package/dist/tools/apps.js
CHANGED
|
@@ -32,7 +32,7 @@ export const appTools = [
|
|
|
32
32
|
inputSchema: {
|
|
33
33
|
app_id: z.string().min(1).describe('Registry id returned by list_apps.'),
|
|
34
34
|
enabled: z.boolean(),
|
|
35
|
-
config: z.record(z.unknown()).optional().describe('Schema-driven app config. Omitted fields preserve their existing values.'),
|
|
35
|
+
config: z.record(z.string(), z.unknown()).optional().describe('Schema-driven app config. Omitted fields preserve their existing values.'),
|
|
36
36
|
},
|
|
37
37
|
handler: withErrorBoundary(async (args, { client, siteId }) => {
|
|
38
38
|
return ok(await client.put(siteId, `apps/${encodeURIComponent(args.app_id)}`, {
|
|
@@ -55,7 +55,7 @@ const definitionShape = {
|
|
|
55
55
|
item_compatible: z.boolean().optional().describe('Mark true if this block is designed to render as a repeater item (no outer padding).'),
|
|
56
56
|
expand_to: z.object({
|
|
57
57
|
target: z.string(),
|
|
58
|
-
defaults: z.record(z.unknown()),
|
|
58
|
+
defaults: z.record(z.string(), z.unknown()),
|
|
59
59
|
}).optional().describe('Alias mechanism — render as another block type with these defaults merged under authored data.'),
|
|
60
60
|
schema: z.array(z.unknown()).optional().describe('FieldDefinition[]: { name, type, label, help?, required?, default?, options? (select), fields? (array/object), item_label?, min_items?, max_items? }. Replaces the whole schema on update.'),
|
|
61
61
|
composition: z.array(z.unknown()).optional().describe('Block[] a composed type is built from. Bind data to props with "{{props.name}}" and, inside a core/repeater over an array prop, to items with "{{item.name}}" — always as the whole value.'),
|
|
@@ -176,7 +176,7 @@ export const blockTypeTools = [
|
|
|
176
176
|
name: 'validate_block_type',
|
|
177
177
|
description: `Check a block type definition without saving it, with the exact validator create_block_type and update_block_type use. Pass the whole definition, or with type_id a patch to that stored type (renames are checked too). Returns { ok, problems, merged }: every error and warning with a JSON-pointer path (e.g. /schema/0/fields/2/name, /composition/0/children/1/data/text) and a line for markup and CSS, and the definition as it would be stored. Writes nothing. ${AUTHORING_GUIDE}`,
|
|
178
178
|
inputSchema: {
|
|
179
|
-
definition: z.record(z.unknown()).describe('The block type definition (as for create_block_type), or a patch when type_id is set.'),
|
|
179
|
+
definition: z.record(z.string(), z.unknown()).describe('The block type definition (as for create_block_type), or a patch when type_id is set.'),
|
|
180
180
|
type_id: z.string().optional().describe('Validate `definition` as a patch to this stored block type.'),
|
|
181
181
|
version: versionParam,
|
|
182
182
|
},
|
|
@@ -194,9 +194,9 @@ export const blockTypeTools = [
|
|
|
194
194
|
name: 'preview_block_type',
|
|
195
195
|
description: 'Render a block type definition that need not be saved, through the same preview renderer as the portal (the Site\'s render version, theme CSS, named styles, custom CSS, the other site block types, and the sanitizer). Without `data` the block shows sample values derived from its fields. Returns { ok, html, css, document, data, problems }: `html` is the block\'s markup as a page carries it, `css` the scoped block CSS that ships, `document` a standalone HTML page. A definition with errors is not rendered (ok:false, problems listed). Block scripts never run in a preview. Writes nothing.',
|
|
196
196
|
inputSchema: {
|
|
197
|
-
definition: z.record(z.unknown()).optional().describe('The block type definition, or a patch when type_id is set. Omit with type_id to preview the stored type.'),
|
|
197
|
+
definition: z.record(z.string(), z.unknown()).optional().describe('The block type definition, or a patch when type_id is set. Omit with type_id to preview the stored type.'),
|
|
198
198
|
type_id: z.string().optional().describe('Preview `definition` as a patch to this stored block type.'),
|
|
199
|
-
data: z.record(z.unknown()).optional().describe('Field values for the previewed block. Omit for sample data.'),
|
|
199
|
+
data: z.record(z.string(), z.unknown()).optional().describe('Field values for the previewed block. Omit for sample data.'),
|
|
200
200
|
render_version: z.number().int().optional().describe('Render with this platform render version instead of the Site\'s own.'),
|
|
201
201
|
version: versionParam,
|
|
202
202
|
},
|
|
@@ -237,7 +237,7 @@ export const blockTypeTools = [
|
|
|
237
237
|
inputSchema: {
|
|
238
238
|
type_id: z.string().describe('Block type id (a site type). Cannot be a core/* block.'),
|
|
239
239
|
...definitionShape,
|
|
240
|
-
renames: z.record(z.string()).optional().describe('Old field path → new field name, e.g. { "title": "heading", "items.label": "text" }. Send together with the new schema.'),
|
|
240
|
+
renames: z.record(z.string(), z.string()).optional().describe('Old field path → new field name, e.g. { "title": "heading", "items.label": "text" }. Send together with the new schema.'),
|
|
241
241
|
confirm_data_loss: z.boolean().optional().describe('Drop the data of removed or retyped fields that hold data. Only after the user agreed; the 409 lists what would be lost.'),
|
|
242
242
|
version: versionParam,
|
|
243
243
|
},
|
|
@@ -3,18 +3,18 @@ import { ok, withErrorBoundary, versionParam, writeAuthority } from './helpers.j
|
|
|
3
3
|
const schema = z.array(z.object({ name: z.string(), label: z.string(), type: z.enum(['text', 'textarea', 'richtext', 'image', 'file', 'color', 'select', 'multiselect', 'boolean', 'number', 'url', 'email', 'date', 'datetime', 'list', 'list_simple', 'array', 'object', 'page_ref', 'page_ref_list']) }).passthrough());
|
|
4
4
|
const fields = {
|
|
5
5
|
label_singular: z.string().optional(), label_plural: z.string().optional(), fields: schema.optional(),
|
|
6
|
-
page_field_rules: z.record(z.object({ label: z.string().optional(), writable_by: z.array(z.enum(['portal', 'owner', 'agent', 'app', 'import'])) })).optional(),
|
|
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
8
|
route_template: z.string().optional(), template: z.string().optional(), schema_type: z.string().optional(),
|
|
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()).optional(),
|
|
10
|
+
schema_field_map: z.record(z.string(), z.string()).optional(),
|
|
11
11
|
icon: z.string().optional(), sort_field: z.string().optional(), sort_dir: z.enum(['asc', 'desc']).optional(),
|
|
12
|
-
facets: z.array(z.record(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.'),
|
|
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
|
};
|
|
14
14
|
export const contentTypeTools = [
|
|
15
15
|
{ name: 'page_completeness', description: 'Report missing, unverified and stale fields for Pages of one content type, with per-field counts and worst-first Page IDs. Read-only; agent-writable fields only by default.', inputSchema: { content_type: z.string(), limit: z.number().int().min(1).max(200).optional(), stale_after_days: z.number().min(0).optional(), agent_writable_only: z.boolean().optional(), version: versionParam },
|
|
16
16
|
handler: withErrorBoundary(async (args, { client, siteId }) => { const { content_type, ...query } = args; return ok(await client.get(siteId, `content-types/${encodeURIComponent(content_type)}/completeness`, query)); }) },
|
|
17
|
-
{ name: 'change_page_content_type', description: 'Change a saved Page to another content type. Preserves its identity, block body, status and existing URL. Save or discard its working copy first. If supplied, fields replaces the complete custom fields object; omitted fields are kept and must be valid for the new type. Snapshots a revision before changing.', inputSchema: { page_id: z.string(), content_type: z.string(), fields: z.record(z.unknown()).optional(), ...writeAuthority, version: versionParam },
|
|
17
|
+
{ name: 'change_page_content_type', description: 'Change a saved Page to another content type. Preserves its identity, block body, status and existing URL. Save or discard its working copy first. If supplied, fields replaces the complete custom fields object; omitted fields are kept and must be valid for the new type. Snapshots a revision before changing.', inputSchema: { page_id: z.string(), content_type: z.string(), fields: z.record(z.string(), z.unknown()).optional(), ...writeAuthority, version: versionParam },
|
|
18
18
|
handler: withErrorBoundary(async (args, { client, siteId }) => { const { page_id, version, ...body } = args; return ok(await client.post(siteId, `pages/${encodeURIComponent(page_id)}/content-type`, body, { version })); }) },
|
|
19
19
|
{ name: 'list_content_types', description: 'List the field schemas, URL patterns, sorting, allowed templates and default templates for Pages. All content is a Page; use list_pages with content_type to filter.', inputSchema: { version: versionParam },
|
|
20
20
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.get(siteId, 'content-types', { version: args.version }))) },
|
package/dist/tools/email.js
CHANGED
|
@@ -17,7 +17,7 @@ export const emailTools = [
|
|
|
17
17
|
type: z.string().min(1).describe('Provider type from get_email_settings providers, e.g. "postmark" or "smtp".'),
|
|
18
18
|
from: z.string().min(1).describe('Sender, e.g. "Acme <hello@acme.com>". Must be a sender the provider accepts.'),
|
|
19
19
|
reply_to: z.string().optional().describe('Optional default Reply-To address.'),
|
|
20
|
-
config: z.record(z.unknown()).optional().describe('Provider fields keyed by the field schema.'),
|
|
20
|
+
config: z.record(z.string(), z.unknown()).optional().describe('Provider fields keyed by the field schema.'),
|
|
21
21
|
},
|
|
22
22
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.put(siteId, 'integrations/email', {
|
|
23
23
|
type: args.type,
|
package/dist/tools/extensions.js
CHANGED
|
@@ -7,7 +7,7 @@ export const extensionTools = [
|
|
|
7
7
|
inputSchema: {
|
|
8
8
|
installation_id: z.string().min(1), page_id: z.string().min(1),
|
|
9
9
|
path: z.string().describe('Relative operation below the approved native API base, from the installed app guide.'),
|
|
10
|
-
method: z.enum(['GET', 'POST']), query: z.record(z.string()).optional(), body: z.record(z.unknown()).optional(),
|
|
10
|
+
method: z.enum(['GET', 'POST']), query: z.record(z.string(), z.string()).optional(), body: z.record(z.string(), z.unknown()).optional(),
|
|
11
11
|
},
|
|
12
12
|
handler: withErrorBoundary(async (args, { client, siteId }) => {
|
|
13
13
|
const { installation_id, ...operation } = args;
|
|
@@ -39,7 +39,7 @@ export const extensionTools = [
|
|
|
39
39
|
inputSchema: {
|
|
40
40
|
installation_id: z.string().min(1),
|
|
41
41
|
version: z.string().min(1).describe('Pending published release to activate.'),
|
|
42
|
-
config: z.record(z.unknown()).optional(),
|
|
42
|
+
config: z.record(z.string(), z.unknown()).optional(),
|
|
43
43
|
granted_scopes: z.array(z.string()).optional(),
|
|
44
44
|
},
|
|
45
45
|
handler: withErrorBoundary(async (args, { client, siteId }) => {
|
|
@@ -53,7 +53,7 @@ export const extensionTools = [
|
|
|
53
53
|
inputSchema: {
|
|
54
54
|
version: versionParam,
|
|
55
55
|
installation_id: z.string().min(1).describe('Installation id returned by list_extension_installations.'),
|
|
56
|
-
config: z.record(z.unknown()).describe('Config keys and values declared by the installation manifest config schema.'),
|
|
56
|
+
config: z.record(z.string(), z.unknown()).describe('Config keys and values declared by the installation manifest config schema.'),
|
|
57
57
|
deploy: z.boolean().optional().describe('Queue a deploy of the selected site version after saving. Defaults to true.'),
|
|
58
58
|
},
|
|
59
59
|
handler: withErrorBoundary(async (args, { client, siteId }) => {
|
|
@@ -76,7 +76,7 @@ export const extensionTools = [
|
|
|
76
76
|
extension_id: z.string().min(1),
|
|
77
77
|
version: z.string().min(1).describe('Release version to install.'),
|
|
78
78
|
granted_scopes: z.array(z.string()).optional().describe('Approved scopes; defaults to none.'),
|
|
79
|
-
config: z.record(z.unknown()).optional(),
|
|
79
|
+
config: z.record(z.string(), z.unknown()).optional(),
|
|
80
80
|
developer_org_id: z.string().optional().describe('Organization that publishes the Extension. Defaults to the key\'s own organization.'),
|
|
81
81
|
},
|
|
82
82
|
handler: withErrorBoundary(async (args, { client, siteId }) => {
|
|
@@ -168,7 +168,7 @@ export const developerExtensionTools = [
|
|
|
168
168
|
description: 'Save (create or replace) a draft release from a full Extension manifest. Published releases are immutable; bump manifest.version instead. Validate locally with `typeroll extension validate` first. Requires an organization API key.',
|
|
169
169
|
inputSchema: {
|
|
170
170
|
extension_id: z.string().min(1),
|
|
171
|
-
manifest: z.record(z.unknown()).describe('Complete manifest; its id must equal extension_id.'),
|
|
171
|
+
manifest: z.record(z.string(), z.unknown()).describe('Complete manifest; its id must equal extension_id.'),
|
|
172
172
|
},
|
|
173
173
|
handler: withErrorBoundary(async (args, { client }) => ok(await client.developer('POST', `${ext(args.extension_id)}/versions`, { manifest: args.manifest }))),
|
|
174
174
|
},
|
package/dist/tools/forms.js
CHANGED
|
@@ -39,7 +39,7 @@ const showProgressSchema = z.union([z.boolean(), z.enum(['text', 'bar'])]).optio
|
|
|
39
39
|
.describe('Multi-step forms: true or "text" shows "Step X of Y", "bar" a slim progress bar (with the same text for screen readers). Off by default; false turns it off.');
|
|
40
40
|
const targetSchema = z.object({
|
|
41
41
|
context_params: z.array(z.string()).max(8).optional().describe('Explicit URL query keys forwarded to the app hydration endpoint; reserved security keys are rejected.'),
|
|
42
|
-
installation_id: z.string(), path: z.string(), hydrate: z.boolean().optional(), session_param: z.string().optional(),
|
|
42
|
+
installation_id: z.string(), path: z.string(), hydrate: z.boolean().optional(), session_param: z.string().optional(), session_exchange: z.literal('confirm_post').optional().describe('Wait for a click and POST the one-time token; prevents GET link scanning from consuming it.'),
|
|
43
43
|
}).optional().describe('Admin-only: bind to a declared POST route of an enabled app installation. Read its authenticated guide first.');
|
|
44
44
|
// Actions run after a successful submission: email notifications, signed
|
|
45
45
|
// webhooks and app-provided action types. Same validation as the portal's
|
|
@@ -11,7 +11,7 @@ export const ownerReviewTools = [
|
|
|
11
11
|
{
|
|
12
12
|
name: 'override_owner_answers',
|
|
13
13
|
description: 'Explicit administrator correction of protected answers. Supply the current revision from read_owner_answers, changed fields, and the factual reason. Does not publish. Never use this tool to bypass an import or scraping conflict without explicit authorization.',
|
|
14
|
-
inputSchema: { page_id: z.string(), base_revision: z.string(), fields: z.record(z.unknown()), reason: z.string().min(1), version: versionParam },
|
|
14
|
+
inputSchema: { page_id: z.string(), base_revision: z.string(), fields: z.record(z.string(), z.unknown()), reason: z.string().min(1), version: versionParam },
|
|
15
15
|
handler: withErrorBoundary(async ({ version, ...input }, { client, siteId }) => ok(await client.post(siteId, 'owner-review', { ...input, action: 'override' }, versionQuery(version)))),
|
|
16
16
|
},
|
|
17
17
|
{
|
|
@@ -35,7 +35,7 @@ export const ownerReviewTools = [
|
|
|
35
35
|
{
|
|
36
36
|
name: 'decide_owner_proposal',
|
|
37
37
|
description: 'Explicit site-administrator editorial decision: approve, reject, or approve with adjustments. Requires current content/schema and a stable request_id for retries. First decision wins. Approval updates accepted CMS content, never deploys; reviewer adjustments retain separate provenance.',
|
|
38
|
-
inputSchema: { proposal_id: z.string(), action: z.enum(['approve', 'reject']), request_id: z.string().min(16).max(100), adjustments: z.record(z.unknown()).optional(), version: versionParam },
|
|
38
|
+
inputSchema: { proposal_id: z.string(), action: z.enum(['approve', 'reject']), request_id: z.string().min(16).max(100), adjustments: z.record(z.string(), z.unknown()).optional(), version: versionParam },
|
|
39
39
|
handler: withErrorBoundary(async ({ version, ...input }, { client, siteId }) => ok(await client.post(siteId, 'owner-review', input, versionQuery(version)))),
|
|
40
40
|
},
|
|
41
41
|
{
|
|
@@ -8,7 +8,7 @@ import { ok, withErrorBoundary, versionParam } from './helpers.js';
|
|
|
8
8
|
const blockSchema = z.object({
|
|
9
9
|
id: z.string().optional().describe('Stable id. Omit to let the server generate one.'),
|
|
10
10
|
type: z.string().describe('Block type id, e.g. "core/heading", "core/section", or a custom "user/banner".'),
|
|
11
|
-
data: z.record(z.unknown()).optional().describe('Field values matching the block-type schema.'),
|
|
11
|
+
data: z.record(z.string(), z.unknown()).optional().describe('Field values matching the block-type schema.'),
|
|
12
12
|
children: z.array(z.any()).optional().describe('Nested blocks for container types.'),
|
|
13
13
|
slots: z.array(z.array(z.any())).optional().describe('Slot contents for slot-container types.'),
|
|
14
14
|
style_overrides: z.object({
|
|
@@ -107,7 +107,7 @@ export const pageBlockTools = [
|
|
|
107
107
|
target: targetSchema.optional(),
|
|
108
108
|
page_id: z.string().optional(),
|
|
109
109
|
block_id: z.string(),
|
|
110
|
-
data: z.record(z.unknown()).optional().describe('Field values to merge into block.data. null removes the key.'),
|
|
110
|
+
data: z.record(z.string(), z.unknown()).optional().describe('Field values to merge into block.data. null removes the key.'),
|
|
111
111
|
style_overrides: z.object({
|
|
112
112
|
spacing_before: z.string().optional(),
|
|
113
113
|
spacing_after: z.string().optional(),
|
package/dist/tools/pages.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Pages tools. Read + write + batch + blocks-view + preview.
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ok, withErrorBoundary, versionParam, writeAuthority } from './helpers.js';
|
|
4
|
-
const answerSources = z.record(z.object({ source_url: z.string().url().optional(), import_run_id: z.string().max(200).optional() }).strict()).optional().describe('Evidence per schema leaf path. Actor and timestamp are always assigned by the server; unchanged answers are not confirmed.');
|
|
4
|
+
const answerSources = z.record(z.string(), z.object({ source_url: z.string().url().optional(), import_run_id: z.string().max(200).optional() }).strict()).optional().describe('Evidence per schema leaf path. Actor and timestamp are always assigned by the server; unchanged answers are not confirmed.');
|
|
5
5
|
function authorityBody(args) {
|
|
6
6
|
return args.override_reason ? { override_reason: args.override_reason } : {};
|
|
7
7
|
}
|
|
@@ -91,7 +91,7 @@ export const pageTools = [
|
|
|
91
91
|
inputSchema: {
|
|
92
92
|
title: z.string().min(1),
|
|
93
93
|
content_type: z.string().optional().describe('Content type ID; defaults to page.'),
|
|
94
|
-
fields: z.record(z.unknown()).optional().describe('Custom field values defined by the content type.'),
|
|
94
|
+
fields: z.record(z.string(), z.unknown()).optional().describe('Custom field values defined by the content type.'),
|
|
95
95
|
slug: z.string().optional().describe('URL slug — single path segment, no slashes (e.g. "about", "kontakt"). Empty string "" = homepage. For nested URLs set the `path` field explicitly — `slug` stays a leaf id. Already in use → 409 slug_in_use naming the existing page; omit it to derive one from the title (numbered with a slug_adjusted warning on conflict).'),
|
|
96
96
|
path: z.string().optional().describe('Optional explicit URL path for nested pages (e.g. "/erbjudanden/sommar-2026"). When set, takes precedence over slug for routing. Must start with "/", lowercase a-z/0-9/-/_/ only, no "..", no "//", no trailing slash. Slug is still required as the leaf id but two pages CAN share a slug under different paths.'),
|
|
97
97
|
content_mode: z.enum(['blocks', 'html']).optional().describe('Default "blocks". "blocks" stores a Block[] tree (the modern default — supports the full ~40-block library, page templates, and the responsive system). "html" stores raw markup in html_content (legacy path, still fully supported for imported content or hand-written HTML). Passing html_content without content_mode also opts into "html".'),
|
|
@@ -132,7 +132,7 @@ export const pageTools = [
|
|
|
132
132
|
patch: z
|
|
133
133
|
.object({
|
|
134
134
|
title: z.string().optional(),
|
|
135
|
-
fields: z.record(z.unknown()).optional(),
|
|
135
|
+
fields: z.record(z.string(), z.unknown()).optional(),
|
|
136
136
|
slug: z.string().optional(),
|
|
137
137
|
html_content: z.string().optional(),
|
|
138
138
|
blocks: z.array(z.any()).optional().describe('Block tree, only used when content_mode="blocks".'),
|
|
@@ -208,7 +208,7 @@ export const pageTools = [
|
|
|
208
208
|
updates: z
|
|
209
209
|
.array(z.object({
|
|
210
210
|
page_id: z.string(),
|
|
211
|
-
patch: z.record(z.unknown()),
|
|
211
|
+
patch: z.record(z.string(), z.unknown()),
|
|
212
212
|
save: z.boolean().optional(),
|
|
213
213
|
answer_sources: answerSources,
|
|
214
214
|
...writeAuthority,
|
package/dist/tools/partials.js
CHANGED
|
@@ -93,7 +93,7 @@ export const partialTools = [
|
|
|
93
93
|
inputSchema: {
|
|
94
94
|
id: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
|
|
95
95
|
name: z.string().optional(),
|
|
96
|
-
blocks: z.array(z.record(z.unknown())).optional().describe('Block tree for a block global block.'),
|
|
96
|
+
blocks: z.array(z.record(z.string(), z.unknown())).optional().describe('Block tree for a block global block.'),
|
|
97
97
|
html_content: z.string().optional().describe('HTML for <x-include> in HTML pages. Send blocks or html_content.'),
|
|
98
98
|
status: z.enum(['draft', 'published']).optional(),
|
|
99
99
|
version: versionParam,
|
|
@@ -55,7 +55,7 @@ export const reusableBlockTools = [
|
|
|
55
55
|
description: z.string().optional().describe('When to use it.'),
|
|
56
56
|
id: z.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/).optional(),
|
|
57
57
|
from: z.object({ page_id: z.string(), block_id: z.string() }).optional(),
|
|
58
|
-
blocks: z.array(z.record(z.unknown())).optional(),
|
|
58
|
+
blocks: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
59
59
|
},
|
|
60
60
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.post(siteId, 'block-templates', args))),
|
|
61
61
|
},
|
|
@@ -66,7 +66,7 @@ export const reusableBlockTools = [
|
|
|
66
66
|
template_id: z.string(),
|
|
67
67
|
name: z.string().optional(),
|
|
68
68
|
description: z.string().optional(),
|
|
69
|
-
blocks: z.array(z.record(z.unknown())).optional(),
|
|
69
|
+
blocks: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
70
70
|
},
|
|
71
71
|
handler: withErrorBoundary(async (args, { client, siteId }) => {
|
|
72
72
|
const { template_id, ...body } = args;
|
package/dist/tools/settings.js
CHANGED
|
@@ -76,8 +76,8 @@ export const settingsTools = [
|
|
|
76
76
|
}).strict().nullable().optional().describe('Organization schema.org JSON-LD emitted on every page. Replaces the stored value as a whole; null (or all-empty) clears it.'),
|
|
77
77
|
staging_url: z.string().nullable().optional().describe('URL of a staging environment for this site, reported as urls.staging. Stored on the Site, not per version. Trailing slashes are dropped; empty string or null clears.'),
|
|
78
78
|
render_version: z.number().int().min(1).optional().describe('Platform render version. Platform output changes ship as new versions so existing sites never change look on their own. Upgrade only after previewing (get_preview_link render_version) and with the user\'s approval; the tr-upgrade-rendering skill compares every page. Lowering it returns to an earlier version.'),
|
|
79
|
-
colors: z.record(z.string().nullable()).optional().describe('Palette: primary, secondary, accent, background, surface, text, text_light (shallow-merged). Optional on_primary, on_secondary, on_accent set the text colour on that theme colour (buttons, badges, tabs, call-to-action sections, styles using the on_* tokens) as a hex colour such as "#ffffff"; null returns to the automatic black or white (chosen by APCA from render version 8, by the WCAG 2 ratio before). A chosen on_* colour applies on every render version. The response lists `on_colors` with each one\'s APCA Lc and WCAG 2 ratio; a choice below APCA Lc 60 (button text, 16px/600) is kept and returned as a warning. Prefer the automatic choice unless the brand specifies a colour.'),
|
|
80
|
-
fonts: z.record(z.unknown()).optional().describe('heading, body (Google Fonts family or "system"), size_base (px) and smoothing: "auto" (browser default) or "antialiased" (thinner text on macOS, for light text on dark backgrounds); null clears smoothing. Unset smoothing is auto from render version 6.'),
|
|
79
|
+
colors: z.record(z.string(), z.string().nullable()).optional().describe('Palette: primary, secondary, accent, background, surface, text, text_light (shallow-merged). Optional on_primary, on_secondary, on_accent set the text colour on that theme colour (buttons, badges, tabs, call-to-action sections, styles using the on_* tokens) as a hex colour such as "#ffffff"; null returns to the automatic black or white (chosen by APCA from render version 8, by the WCAG 2 ratio before). A chosen on_* colour applies on every render version. The response lists `on_colors` with each one\'s APCA Lc and WCAG 2 ratio; a choice below APCA Lc 60 (button text, 16px/600) is kept and returned as a warning. Prefer the automatic choice unless the brand specifies a colour.'),
|
|
80
|
+
fonts: z.record(z.string(), z.unknown()).optional().describe('heading, body (Google Fonts family or "system"), size_base (px) and smoothing: "auto" (browser default) or "antialiased" (thinner text on macOS, for light text on dark backgrounds); null clears smoothing. Unset smoothing is auto from render version 6.'),
|
|
81
81
|
contact: z
|
|
82
82
|
.object({
|
|
83
83
|
email: z.string().optional(),
|
|
@@ -99,7 +99,7 @@ export const settingsTools = [
|
|
|
99
99
|
})
|
|
100
100
|
.passthrough()
|
|
101
101
|
.optional(),
|
|
102
|
-
social: z.record(z.string()).optional(),
|
|
102
|
+
social: z.record(z.string(), z.string()).optional(),
|
|
103
103
|
cookie_consent: z.object({
|
|
104
104
|
enabled: z.boolean().optional(),
|
|
105
105
|
text: z.string().optional().describe('Localized consent copy. May include a privacy-policy link.'),
|
package/dist/tools/styles.js
CHANGED
|
@@ -90,9 +90,9 @@ export const styleTools = [
|
|
|
90
90
|
role: styleFields.role,
|
|
91
91
|
targets: styleFields.targets,
|
|
92
92
|
description: styleFields.description,
|
|
93
|
-
base: z.record(z.unknown()).optional().describe('Property patch for base; null removes a property.'),
|
|
94
|
-
at: z.record(z.unknown()).optional().describe('Per-breakpoint property patches; null removes a breakpoint or property.'),
|
|
95
|
-
hover: z.record(z.unknown()).optional(),
|
|
93
|
+
base: z.record(z.string(), z.unknown()).optional().describe('Property patch for base; null removes a property.'),
|
|
94
|
+
at: z.record(z.string(), z.unknown()).optional().describe('Per-breakpoint property patches; null removes a breakpoint or property.'),
|
|
95
|
+
hover: z.record(z.string(), z.unknown()).optional(),
|
|
96
96
|
css: z.string().optional(),
|
|
97
97
|
parts: styleFields.parts,
|
|
98
98
|
version: versionParam,
|
package/dist/tools/workflows.js
CHANGED
|
@@ -24,7 +24,7 @@ export const workflowTools = [
|
|
|
24
24
|
description: 'Start a server-side workflow on this Site; it runs in the background and returns workflow_id. Poll get_workflow. Config per type (same fields as the portal form): migration { wp_url, helper_api_key? } (needs verified organization import storage); site_planning { business_description }; content_generation { topic, tone?, audience? }; content_improvement { limit? }; url_parity { target_origin?, source_origin?, check_source?, statuses? }; rebuild_deploy { environment: staging | production }; seo_audit, link_check, performance_audit and schema_markup take none. Needs write permission; rebuild_deploy publishes and needs admin. Workflows that change content write to the given version (default main) — get the user\'s agreement first. ' + AI_WORKFLOWS,
|
|
25
25
|
inputSchema: {
|
|
26
26
|
type: z.enum(WORKFLOW_TYPES),
|
|
27
|
-
config: z.record(z.unknown()).optional(),
|
|
27
|
+
config: z.record(z.string(), z.unknown()).optional(),
|
|
28
28
|
version: versionParam,
|
|
29
29
|
},
|
|
30
30
|
handler: withErrorBoundary(async (args, { client, siteId }) => ok(await client.post(siteId, 'workflows', { type: args.type, config: args.config }, args.version ? { version: args.version } : undefined))),
|
package/dist/version.js
CHANGED
package/dist/workspace.js
CHANGED
|
@@ -15,7 +15,7 @@ export const workspaceSchema = z.object({
|
|
|
15
15
|
version: z.string().min(1), tool_mode: z.enum(['compact', 'full']),
|
|
16
16
|
}).strict();
|
|
17
17
|
export const defaultWorkspace = { schema_version: 1, portal: 'https://app.typeroll.com', organization_id: null, site_id: null, version: 'main', tool_mode: 'compact' };
|
|
18
|
-
const lockSchema = z.object({ schema_version: z.literal(1), generator_version: z.string(), files: z.record(z.string().regex(/^[a-f0-9]{64}$/)) }).strict();
|
|
18
|
+
const lockSchema = z.object({ schema_version: z.literal(1), generator_version: z.string(), files: z.record(z.string(), z.string().regex(/^[a-f0-9]{64}$/)) }).strict();
|
|
19
19
|
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
20
20
|
export const ignores = ['.typeroll/', '.env', '.env.*', '!.env.example', '.mcp.json', '.cursor/mcp.json', '.vscode/mcp.json', 'qa/reports/', 'sources/private/'];
|
|
21
21
|
export const agentInstructions = `# Working on this website
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typeroll/mcp-server",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.73",
|
|
4
4
|
"description": "Typeroll CMS MCP server – connect AI agents to the REST API to manage content and publish static websites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -44,8 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
47
|
-
"zod": "^
|
|
48
|
-
"zod-to-json-schema": "3.25.2"
|
|
47
|
+
"zod": "^4.6.7"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
50
|
"typescript": "^5.6.0",
|