tabbied-mcp 0.2.3 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"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","sourcesContent":["// The template-site tools: what an agent needs to start somebody's site from a\n// finished template rather than from an empty page.\n//\n// The design tools answer \"which pattern?\"; these answer \"which *site*, and\n// what may I change about it?\". The useful move with a template is to swap the\n// brand out of it, and the editable-section spec (docs/editable-templates.md)\n// is the list of what may be swapped.\n//\n// Both are thin readers over artifacts the site already generates, so an agent\n// and the web builder see the same bytes. Neither needs a browser, so unlike\n// `render_design` they work over both transports.\nimport type {\n TemplateCatalog,\n TemplateCatalogEntry,\n TemplateSpec,\n Tool,\n ToolContent,\n ToolContext,\n ToolResult,\n} from './types.js';\n\nconst text = (value: string): ToolContent => ({ type: 'text', text: value });\n\nconst json = (value: unknown): ToolContent =>\n text(JSON.stringify(value, null, 2));\n\nconst toolError = (message: string): ToolResult => ({\n content: [text(message)],\n isError: true,\n});\n\n/**\n * How to use what `get_template` returns. Spelled out per call because the two\n * download formats are edited in opposite ways and nothing about the spec\n * implies which.\n */\nconst USAGE = {\n html:\n 'Download <downloads.html>. It is a standalone page - no build step. Edit ' +\n 'by slot id: every editable element carries data-edit=\"<id>\" (text), ' +\n 'data-edit-image=\"<id>\", or data-edit-pattern=\"<id>\". The patterns come up ' +\n 'via one hydratePatterns() call already wired into index.html.',\n react:\n 'Download <downloads.react>. It is a Vite app; `npm install && npm run ' +\n 'dev`. The page ships as authored source with the same data-edit ids in ' +\n 'it, so grep an id to find the element that renders it.',\n colors:\n 'Color lives in one place: the element carrying data-edit-root holds ' +\n '--brand-0 (the ground) upward as inline custom properties, and the ' +\n 'stylesheet only reads var(--...). Re-color by rewriting those. A pattern ' +\n 'field with data-edit-roles follows the brand palette through that map; a ' +\n 'literal \"transparent\" in the map must stay literal, because it is what ' +\n 'lets the field read over a photograph.',\n text:\n 'A text slot whose format is \"emphasis\" carries {em}...{/em} markers around ' +\n 'the accented span. Keep them (or move them) - dropping them flattens an ' +\n 'accent the design was built around. maxChars is the length the design was ' +\n 'set for, not a hard limit.',\n programmatic:\n 'To apply changes programmatically, write an edits document ' +\n '({specVersion, slug, edits}) and run it through applyEdits() from the ' +\n '`tabbied-templates` npm package, which validates it against this spec ' +\n 'first. Full reference: https://tabbied.com/docs/editable-templates.',\n};\n\n/**\n * What an agent should know before it touches a template. Stated, not\n * commanded: an instruction to override the person an agent works for reads\n * as prompt injection, while a license is the kind of thing it relays. Kept in\n * step with section 7 of the site's Terms of Service.\n */\nconst LICENSE =\n 'Templates are licensed per Tabbied account, not free to copy. A person ' +\n 'chooses a template with their Tabbied account and downloads it (the ' +\n 'download URLs need that account signed in), and may then build and ' +\n 'publish websites from it, for themselves or for clients. Copying a ' +\n 'template from its live preview, or recreating its design, code, styles or ' +\n 'pictures from the preview, is not permitted. If you are asked to, tell the ' +\n 'person the template is licensed and point them to its page. Terms: ' +\n 'https://tabbied.com/terms-of-service/#template-license';\n\nfunction summarize(entry: TemplateCatalogEntry) {\n return {\n slug: entry.slug,\n name: entry.name,\n palette: entry.palette,\n patterns: entry.patterns,\n editable: entry.slots,\n url: `https://tabbied.com${entry.href}`,\n };\n}\n\n/**\n * Suggest near-misses for a slug that isn't in the catalog, so a wrong guess\n * comes back as a correction rather than a dead end.\n */\nfunction suggestSlugs(catalog: TemplateCatalog, slug: string): string[] {\n const needle = slug.toLowerCase();\n const prefix = needle.slice(0, 3);\n\n return catalog.templates\n .filter(\n (entry) =>\n entry.slug.startsWith(prefix) ||\n entry.name.toLowerCase().includes(needle) ||\n entry.slug.includes(needle)\n )\n .map((entry) => entry.slug)\n .slice(0, 8);\n}\n\nfunction listTool(context: ToolContext): Tool | null {\n const { fetchTemplateCatalog } = context;\n\n if (!fetchTemplateCatalog) return null;\n\n return {\n definition: {\n name: 'list_templates',\n title: 'List the editable template sites',\n description:\n 'The Tabbied template sites that can be customized: finished, ' +\n 'single-page brand sites built around a pattern, each downloadable as ' +\n 'plain HTML or as a React (Vite) project. Returns each one with its ' +\n 'palette, the patterns it uses, and how many text/image/pattern slots ' +\n 'are editable. Start here when the task is \"build me a site\" rather ' +\n 'than \"pick me a pattern\" - then get_template for the one you want. ' +\n 'Only sites that have been annotated appear; the rest are not yet ' +\n 'customizable. Templates are licensed per Tabbied account: see ' +\n '`license` in the result.',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description:\n 'Optional substring matched against the slug and the name.',\n },\n },\n additionalProperties: false,\n },\n },\n async run(args) {\n const catalog = await fetchTemplateCatalog();\n const query =\n typeof args.query === 'string' ? args.query.trim().toLowerCase() : '';\n const matched = query\n ? catalog.templates.filter(\n (entry) =>\n entry.slug.toLowerCase().includes(query) ||\n entry.name.toLowerCase().includes(query)\n )\n : catalog.templates;\n\n if (matched.length === 0) {\n return {\n content: [\n json({\n matched: 0,\n total: catalog.templates.length,\n slugs: catalog.templates.map((entry) => entry.slug),\n hint: 'No name or slug contains that. Every available slug is listed above.',\n }),\n ],\n };\n }\n\n return {\n content: [\n json({\n matched: matched.length,\n total: catalog.templates.length,\n license: LICENSE,\n templates: matched.map(summarize),\n }),\n ],\n };\n },\n };\n}\n\nfunction getTool(context: ToolContext): Tool | null {\n const { fetchTemplateCatalog, fetchTemplate } = context;\n\n if (!fetchTemplateCatalog || !fetchTemplate) return null;\n\n return {\n definition: {\n name: 'get_template',\n title: 'Get one template site and everything editable in it',\n description:\n \"A template's full editable-section spec: every text, image, and \" +\n 'pattern slot with its id and its current value, the brand palette, ' +\n 'the fonts, and the two download URLs. The slot ids are the contract - ' +\n 'they exist in the downloaded markup as data-edit* attributes, so an ' +\n 'id from this response is directly greppable in the files you ' +\n 'download. Includes usage notes for editing each format, and the ' +\n 'license the template is under.',\n inputSchema: {\n type: 'object',\n properties: {\n slug: {\n type: 'string',\n description: 'Template slug, from list_templates.',\n },\n },\n required: ['slug'],\n additionalProperties: false,\n },\n },\n async run(args) {\n const slug = typeof args.slug === 'string' ? args.slug.trim() : '';\n\n if (!slug) return toolError('get_template needs a `slug`.');\n\n const catalog = await fetchTemplateCatalog();\n const entry = catalog.templates.find(\n (candidate) => candidate.slug === slug\n );\n\n if (!entry) {\n const suggestions = suggestSlugs(catalog, slug);\n\n return toolError(\n `No editable template \"${slug}\". ` +\n (suggestions.length > 0\n ? `Did you mean: ${suggestions.join(', ')}?`\n : 'Call list_templates for the available slugs.')\n );\n }\n\n let spec: TemplateSpec;\n\n try {\n spec = await fetchTemplate(slug);\n } catch (error) {\n return toolError(\n `Could not load the spec for \"${slug}\" (${\n error instanceof Error ? error.message : String(error)\n }).`\n );\n }\n\n const downloads = {\n html: `https://tabbied.com${entry.downloads.html}`,\n react: `https://tabbied.com${entry.downloads.react}`,\n };\n\n return {\n content: [\n json({\n ...spec,\n url: `https://tabbied.com${entry.href}`,\n license: LICENSE,\n downloads,\n usage: {\n html: USAGE.html.replace('<downloads.html>', downloads.html),\n react: USAGE.react.replace('<downloads.react>', downloads.react),\n colors: USAGE.colors,\n text: USAGE.text,\n programmatic: USAGE.programmatic,\n },\n }),\n ],\n };\n },\n };\n}\n\n/** The template tools, dropped when the host cannot resolve their data. */\nexport function templateTools(context: ToolContext): Tool[] {\n return [listTool(context), getTool(context)].filter(\n (tool): tool is Tool => tool !== null\n );\n}\n"]}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"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","sourcesContent":["// The template-site tools: what an agent needs to start somebody's site from a\n// finished template rather than from an empty page.\n//\n// The design tools answer \"which pattern?\"; these answer \"which *site*, and\n// what may I change about it?\". The useful move with a template is to swap the\n// brand out of it, and the editable-section spec (docs/editable-templates.md)\n// is the list of what may be swapped.\n//\n// Both are thin readers over artifacts the site already generates, so an agent\n// and the web builder see the same bytes. Neither needs a browser, so unlike\n// `render_design` they work over both transports.\nimport type {\n TemplateCatalog,\n TemplateCatalogEntry,\n TemplateSpec,\n Tool,\n ToolContent,\n ToolContext,\n ToolResult,\n} from './types.js';\n\nconst SITE = 'https://tabbied.com';\n\nconst LIST_LIMIT_DEFAULT = 20;\nconst LIST_LIMIT_MAX = 100;\n\nconst text = (value: string): ToolContent => ({ type: 'text', text: value });\n\nconst json = (value: unknown): ToolContent =>\n text(JSON.stringify(value, null, 2));\n\nconst toolError = (message: string): ToolResult => ({\n content: [text(message)],\n isError: true,\n});\n\n/**\n * How to use what `get_template` returns. Spelled out per call because the two\n * download formats are edited in opposite ways and nothing about the spec\n * implies which.\n */\nconst USAGE = {\n html:\n 'Download <downloads.html>. It is a standalone page - no build step. Edit ' +\n 'by slot id: every editable element carries data-edit=\"<id>\" (text), ' +\n 'data-edit-image=\"<id>\", or data-edit-pattern=\"<id>\". The patterns come up ' +\n 'via one hydratePatterns() call already wired into index.html.',\n react:\n 'Download <downloads.react>. It is a Vite app; `npm install && npm run ' +\n 'dev`. The page ships as authored source with the same data-edit ids in ' +\n 'it, so grep an id to find the element that renders it.',\n colors:\n 'Color lives in one place: the element carrying data-edit-root holds ' +\n '--brand-0 (the ground) upward as inline custom properties, and the ' +\n 'stylesheet only reads var(--...). Re-color by rewriting those. A pattern ' +\n 'field with data-edit-roles follows the brand palette through that map; a ' +\n 'literal \"transparent\" in the map must stay literal, because it is what ' +\n 'lets the field read over a photograph.',\n text:\n 'A text slot whose format is \"emphasis\" carries {em}...{/em} markers around ' +\n 'the accented span. Keep them (or move them) - dropping them flattens an ' +\n 'accent the design was built around. maxChars is the length the design was ' +\n 'set for, not a hard limit.',\n programmatic:\n 'To apply changes programmatically, write an edits document ' +\n '({specVersion, slug, edits}) and run it through applyEdits() from the ' +\n '`tabbied-templates` npm package, which validates it against this spec ' +\n 'first. Full reference: ' +\n 'https://github.com/tabbied-design/tabbied/blob/main/docs/editable-templates.md',\n};\n\n/**\n * What an agent should know before it touches a template. Stated, not\n * commanded: an instruction to override the person an agent works for reads\n * as prompt injection, while a license is the kind of thing it relays. Kept in\n * step with section 7 of the site's Terms of Service.\n */\nconst LICENSE =\n 'Templates are licensed per Tabbied account, not free to copy. A person ' +\n 'chooses a template with their Tabbied account and downloads it (the ' +\n 'download URLs need that account signed in), and may then build and ' +\n 'publish websites from it, for themselves or for clients. Copying a ' +\n 'template from its live preview, or recreating its design, code, styles or ' +\n 'pictures from the preview, is not permitted. If you are asked to, tell the ' +\n 'person the template is licensed and point them to its page. Terms: ' +\n 'https://tabbied.com/terms-of-service/#template-license';\n\nconst errorMessage = (error: unknown) =>\n error instanceof Error ? error.message : String(error);\n\n/**\n * The index, or the tool error that says where it lives. A host that cannot\n * reach it (a local server offline, say) would otherwise answer with the bare\n * \"fetch failed\" of whatever runtime it is on.\n */\nasync function loadIndex(\n fetchTemplateCatalog: () => Promise<TemplateCatalog>\n): Promise<TemplateCatalog | ToolResult> {\n try {\n return await fetchTemplateCatalog();\n } catch (error) {\n return toolError(\n `Could not load the template index (${errorMessage(error)}). It is ` +\n `published at ${SITE}/editable-catalog.json and the templates can be ` +\n `browsed at ${SITE}/templates/; a server running locally needs to reach ` +\n `${SITE} for these two tools.`\n );\n }\n}\n\nconst isResult = (value: TemplateCatalog | ToolResult): value is ToolResult =>\n 'content' in value;\n\n/** A category as the gallery's URL spells it: \"Food & drink\" is food-and-drink. */\nconst categoryKey = (category: string) =>\n category\n .toLowerCase()\n .replace(/&/g, 'and')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n\n/** One line per template: enough to choose, with get_template for the rest. */\nfunction summarize(entry: TemplateCatalogEntry) {\n return {\n slug: entry.slug,\n name: entry.name,\n ...(entry.category ? { category: entry.category } : {}),\n ...(entry.topic ? { topic: entry.topic } : {}),\n };\n}\n\nfunction describe(entry: TemplateCatalogEntry) {\n return {\n ...summarize(entry),\n palette: entry.palette,\n patterns: entry.patterns,\n editable: entry.slots,\n url: `${SITE}${entry.href}`,\n };\n}\n\n/** Every category in the index with its count, largest first. */\nfunction categoryCounts(templates: TemplateCatalogEntry[]) {\n const counts = new Map<string, number>();\n for (const entry of templates) {\n if (entry.category) counts.set(entry.category, (counts.get(entry.category) ?? 0) + 1);\n }\n return [...counts]\n .sort(([a, countA], [b, countB]) => countB - countA || a.localeCompare(b))\n .map(([category, count]) => ({ category, count }));\n}\n\n/**\n * Suggest near-misses for a slug that isn't in the catalog, so a wrong guess\n * comes back as a correction rather than a dead end.\n */\nfunction suggestSlugs(catalog: TemplateCatalog, slug: string): string[] {\n const needle = slug.toLowerCase();\n const prefix = needle.slice(0, 3);\n\n if (!needle) return [];\n\n return catalog.templates\n .filter(\n (entry) =>\n entry.slug.startsWith(prefix) ||\n entry.name.toLowerCase().includes(needle) ||\n entry.slug.includes(needle)\n )\n .map((entry) => entry.slug)\n .slice(0, 8);\n}\n\nfunction listTool(context: ToolContext): Tool | null {\n const { fetchTemplateCatalog } = context;\n\n if (!fetchTemplateCatalog) return null;\n\n return {\n definition: {\n name: 'list_templates',\n title: 'List the editable template sites',\n description:\n 'The Tabbied template sites that can be customized: finished, ' +\n 'single-page sites for a business, each built around a pattern and ' +\n 'downloadable as plain HTML or as a React (Vite) project. Returns a ' +\n 'page of one-line entries (slug, name, category, what the business ' +\n 'is), the count that matched, and every category with its count; ' +\n 'filter by category or query and page with offset. Start here when ' +\n 'the task is \"build me a site\" rather than \"pick me a pattern\" - then ' +\n 'get_template for the one you want. Templates are licensed per ' +\n 'Tabbied account: see `license` in the result.',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description:\n 'Words matched against the slug, name, category, and what the ' +\n 'business is (e.g. \"bakery\", \"dentist\"). Every word must appear.',\n },\n category: {\n type: 'string',\n description:\n 'A gallery category, e.g. \"Food & drink\" (or food-and-drink). ' +\n 'Every result lists the categories there are.',\n },\n detail: {\n type: 'boolean',\n description:\n \"Also return each template's palette, patterns, editable slot \" +\n 'counts and URL. Off by default to keep the page short.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: LIST_LIMIT_MAX,\n description: `Templates per page (default ${LIST_LIMIT_DEFAULT}).`,\n },\n offset: {\n type: 'integer',\n minimum: 0,\n description: 'How many matches to skip, for the next page (default 0).',\n },\n },\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async run(args) {\n const catalog = await loadIndex(fetchTemplateCatalog);\n if (isResult(catalog)) return catalog;\n\n const terms =\n typeof args.query === 'string'\n ? args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n : [];\n const category =\n typeof args.category === 'string' && args.category.trim()\n ? args.category.trim()\n : null;\n const limit = Math.min(\n Math.max(\n typeof args.limit === 'number' && Number.isFinite(args.limit)\n ? Math.trunc(args.limit)\n : LIST_LIMIT_DEFAULT,\n 1\n ),\n LIST_LIMIT_MAX\n );\n const offset =\n typeof args.offset === 'number' && Number.isFinite(args.offset)\n ? Math.max(Math.trunc(args.offset), 0)\n : 0;\n\n const matched = catalog.templates.filter((entry) => {\n // The fields the site's template gallery searches too\n // (lib/catalogSearch.ts, `templateKeywords`).\n const haystack =\n `${entry.slug} ${entry.name} ${entry.category ?? ''} ${entry.topic ?? ''}`.toLowerCase();\n return (\n terms.every((term) => haystack.includes(term)) &&\n (!category ||\n (entry.category !== undefined &&\n categoryKey(entry.category) === categoryKey(category)))\n );\n });\n\n const categories = categoryCounts(catalog.templates);\n const total = catalog.templates.length;\n\n if (matched.length === 0) {\n return {\n content: [\n json({\n matched: 0,\n total,\n ...(categories.length > 0 ? { categories } : {}),\n hint:\n category && categories.length === 0\n ? 'This index carries no categories (an older deployment of the ' +\n 'site); filter with query instead.'\n : 'Nothing matches every filter. Check the category against the ' +\n 'list above, use fewer or broader query words, or drop both ' +\n 'to page through everything.',\n }),\n ],\n };\n }\n\n const page = matched.slice(offset, offset + limit);\n const nextOffset = offset + page.length;\n\n return {\n content: [\n json({\n matched: matched.length,\n total,\n offset,\n returned: page.length,\n ...(page.length === 0\n ? { hint: `offset ${offset} is past the last match; there are ${matched.length}.` }\n : nextOffset < matched.length\n ? {\n next:\n `Showing ${offset + 1}-${nextOffset} of ${matched.length}. ` +\n `Pass offset ${nextOffset} for the next page, or narrow with ` +\n 'category or query.',\n }\n : {}),\n // Absent from an index written before categories were added.\n ...(categories.length > 0 ? { categories } : {}),\n license: LICENSE,\n templates: page.map(args.detail === true ? describe : summarize),\n }),\n ],\n };\n },\n };\n}\n\nfunction getTool(context: ToolContext): Tool | null {\n const { fetchTemplateCatalog, fetchTemplate } = context;\n\n if (!fetchTemplateCatalog || !fetchTemplate) return null;\n\n return {\n definition: {\n name: 'get_template',\n title: 'Get one template site and everything editable in it',\n description:\n \"A template's full editable-section spec: every text, image, and \" +\n 'pattern slot with its id and its current value, the brand palette, ' +\n 'the fonts, and the two download URLs. The slot ids are the contract - ' +\n 'they exist in the downloaded markup as data-edit* attributes, so an ' +\n 'id from this response is directly greppable in the files you ' +\n 'download. Includes usage notes for editing each format, and the ' +\n 'license the template is under.',\n inputSchema: {\n type: 'object',\n properties: {\n slug: {\n type: 'string',\n minLength: 1,\n description: 'Template slug, from list_templates.',\n },\n },\n required: ['slug'],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async run(args) {\n const slug = typeof args.slug === 'string' ? args.slug.trim() : '';\n\n if (!slug) return toolError('get_template needs a `slug`.');\n\n const catalog = await loadIndex(fetchTemplateCatalog);\n if (isResult(catalog)) return catalog;\n\n const entry = catalog.templates.find(\n (candidate) => candidate.slug === slug\n );\n\n if (!entry) {\n const suggestions = suggestSlugs(catalog, slug);\n\n return toolError(\n `No editable template \"${slug}\". ` +\n (suggestions.length > 0\n ? `Did you mean: ${suggestions.join(', ')}?`\n : 'Call list_templates for the available slugs.')\n );\n }\n\n let spec: TemplateSpec;\n\n try {\n spec = await fetchTemplate(slug);\n } catch (error) {\n return toolError(\n `Could not load the spec for \"${slug}\" (${errorMessage(error)}). It ` +\n `is published at ${SITE}${entry.spec}, and the template's page is ` +\n `${SITE}${entry.href}.`\n );\n }\n\n const downloads = {\n html: `${SITE}${entry.downloads.html}`,\n react: `${SITE}${entry.downloads.react}`,\n };\n\n return {\n content: [\n json({\n ...spec,\n url: `${SITE}${entry.href}`,\n license: LICENSE,\n downloads,\n usage: {\n html: USAGE.html.replace('<downloads.html>', downloads.html),\n react: USAGE.react.replace('<downloads.react>', downloads.react),\n colors: USAGE.colors,\n text: USAGE.text,\n programmatic: USAGE.programmatic,\n },\n }),\n ],\n };\n },\n };\n}\n\n/** The template tools, dropped when the host cannot resolve their data. */\nexport function templateTools(context: ToolContext): Tool[] {\n return [listTool(context), getTool(context)].filter(\n (tool): tool is Tool => tool !== null\n );\n}\n"]}
package/dist/tools.js CHANGED
@@ -8,6 +8,7 @@
8
8
  // The point is design *selection*. Slugs are opaque ("cleat", "karst") and
9
9
  // these are pictures, so search narrows on closed-vocabulary enums, then
10
10
  // preview_design puts the shortlist in front of the model before it commits.
11
+ import { SNIPPET_SETUPS, buildSnippet, buildSnippets, parseRatio, } from 'tabbied/snippets';
11
12
  import { templateTools } from './templates.js';
12
13
  const SEARCH_LIMIT_DEFAULT = 20;
13
14
  const SEARCH_LIMIT_MAX = 50;
@@ -51,14 +52,57 @@ function summarize(design) {
51
52
  preview: design.preview,
52
53
  };
53
54
  }
55
+ /**
56
+ * What a free-text query is matched against: slug, name, the closed-vocabulary
57
+ * tags, moods and uses, and the description. The site's gallery search
58
+ * (lib/catalogSearch.ts) reads the same fields, so "dots" finds the designs
59
+ * tagged dots in both, not only one whose name says it.
60
+ */
61
+ function designKeywords(design) {
62
+ return [
63
+ design.slug,
64
+ design.name,
65
+ ...design.tags,
66
+ ...design.mood,
67
+ ...design.goodFor,
68
+ design.description ?? '',
69
+ ]
70
+ .join(' ')
71
+ .toLowerCase();
72
+ }
54
73
  function findDesign(catalog, slug) {
55
74
  if (typeof slug !== 'string')
56
75
  return null;
57
76
  return catalog.designs.find((design) => design.slug === slug) ?? null;
58
77
  }
78
+ /**
79
+ * The snippets' input for one design as the catalog describes it: its own
80
+ * ratio (the catalog's `2:3` id, which the builders write as CSS's `2 / 3`)
81
+ * and its own ground color, with nothing else chosen, so each snippet draws
82
+ * the design as authored. The builders are tabbied/snippets, the same ones
83
+ * the site's editor copies from, so the two agree.
84
+ */
85
+ function snippetInput(catalog, design) {
86
+ return {
87
+ slug: design.slug,
88
+ ratio: parseRatio(design.defaultAspectRatio),
89
+ ...(design.palette[0] ? { ground: design.palette[0] } : {}),
90
+ version: catalog.version,
91
+ };
92
+ }
93
+ const isSetup = (value) => typeof value === 'string' && SNIPPET_SETUPS.includes(value);
94
+ /** What the CLI command in `usage.cli` needs where it runs. */
95
+ const CLI_SETUP = 'Rendering needs Playwright, found from the current directory first: ' +
96
+ '`npm i -D playwright && npx playwright install chromium`. With nothing ' +
97
+ 'installed, run `npx -y -p tabbied -p playwright tabbied render ...` ' +
98
+ 'instead (and `npx playwright install chromium` once).';
59
99
  /** Near misses for an unknown slug: a shared three-letter prefix, or a name match. */
60
100
  function suggestSlugs(catalog, slug) {
61
- const needle = slug.toLowerCase();
101
+ const needle = slug.trim().toLowerCase();
102
+ // An empty needle is a prefix of every slug, so it would "suggest" the first
103
+ // eight in the catalog.
104
+ if (!needle)
105
+ return [];
62
106
  return catalog.designs
63
107
  .filter((design) => design.slug.startsWith(needle.slice(0, 3)) ||
64
108
  design.name.toLowerCase().includes(needle))
@@ -85,8 +129,9 @@ function searchTool(catalog) {
85
129
  properties: {
86
130
  query: {
87
131
  type: 'string',
88
- description: 'Free text matched against slug, name, and description. Every ' +
89
- 'whitespace-separated word must appear somewhere in the entry.',
132
+ description: 'Free text matched against slug, name, tags, mood, goodFor and ' +
133
+ 'description. Every whitespace-separated word must appear ' +
134
+ 'somewhere in the entry.',
90
135
  },
91
136
  tags: {
92
137
  type: 'array',
@@ -124,6 +169,7 @@ function searchTool(catalog) {
124
169
  },
125
170
  additionalProperties: false,
126
171
  },
172
+ annotations: { readOnlyHint: true, openWorldHint: false },
127
173
  };
128
174
  return {
129
175
  definition,
@@ -140,7 +186,7 @@ function searchTool(catalog) {
140
186
  ? Math.trunc(args.limit)
141
187
  : SEARCH_LIMIT_DEFAULT, 1), SEARCH_LIMIT_MAX);
142
188
  const matches = (design) => {
143
- const haystack = `${design.slug} ${design.name} ${design.description ?? ''}`.toLowerCase();
189
+ const haystack = designKeywords(design);
144
190
  return (terms.every((term) => haystack.includes(term)) &&
145
191
  (!wantTags || wantTags.every((tag) => design.tags.includes(tag))) &&
146
192
  (!wantMood || wantMood.every((mood) => design.mood.includes(mood))) &&
@@ -160,9 +206,7 @@ function searchTool(catalog) {
160
206
  const breakdown = [
161
207
  ...(terms.length
162
208
  ? [
163
- alone(`query "${args.query}"`, (design) => terms.every((term) => `${design.slug} ${design.name} ${design.description ?? ''}`
164
- .toLowerCase()
165
- .includes(term))),
209
+ alone(`query "${args.query}"`, (design) => terms.every((term) => designKeywords(design).includes(term))),
166
210
  ]
167
211
  : []),
168
212
  ...(wantTags ?? []).map((tag) => alone(`tag "${tag}"`, (design) => design.tags.includes(tag))),
@@ -214,19 +258,32 @@ function getDesignTool(catalog) {
214
258
  title: 'Get design details',
215
259
  description: 'The full catalog record for one design - palette, every option with its ' +
216
260
  'range and default, SVG-export support, preview URL - plus ready-to-paste ' +
217
- 'React and vanilla snippets. Call this once a slug is chosen; ' +
218
- 'search_designs already returns enough to shortlist.',
261
+ 'code for every setup: React, Vue, a Svelte action, the <tabbied-pattern> ' +
262
+ 'web component, plain HTML with no build step, and framework-free ' +
263
+ 'JavaScript (createPattern). Pass `framework` to get only the one the ' +
264
+ 'project uses. Call this once a slug is chosen; search_designs already ' +
265
+ 'returns enough to shortlist.',
219
266
  inputSchema: {
220
267
  type: 'object',
221
268
  properties: {
222
269
  slug: {
223
270
  type: 'string',
271
+ minLength: 1,
224
272
  description: 'The design slug, e.g. "radius". Slugs are not guessable.',
225
273
  },
274
+ framework: {
275
+ type: 'string',
276
+ enum: [...SNIPPET_SETUPS],
277
+ description: 'Only this setup\'s snippet: react, vue, svelte (the action, ' +
278
+ 'SvelteKit included), element (the web component, for any page or ' +
279
+ 'CMS), html (data attributes and one script, no build step) or core ' +
280
+ '(createPattern on any element). Omit for all six.',
281
+ },
226
282
  },
227
283
  required: ['slug'],
228
284
  additionalProperties: false,
229
285
  },
286
+ annotations: { readOnlyHint: true, openWorldHint: false },
230
287
  };
231
288
  return {
232
289
  definition,
@@ -240,18 +297,29 @@ function getDesignTool(catalog) {
240
297
  : '') +
241
298
  'Use search_designs to find one - slugs are opaque and cannot be guessed.');
242
299
  }
243
- const snippet = (template) => template.replaceAll('<slug>', design.slug);
300
+ // The schema rejects an unknown setup before this runs; this keeps a
301
+ // direct call (the toolset, without the SDK in front) as strict.
302
+ const framework = args.framework;
303
+ if (framework != null && !isSetup(framework)) {
304
+ return toolError(`Unknown framework "${String(framework)}". One of: ${SNIPPET_SETUPS.join(', ')}.`);
305
+ }
306
+ const input = snippetInput(catalog, design);
307
+ const snippets = framework
308
+ ? { [framework]: buildSnippet(framework, input) }
309
+ : buildSnippets(input);
310
+ // A design that cannot be vectorized gets a command that works.
311
+ const extension = design.svgExport.supported ? 'svg' : 'png';
244
312
  return {
245
313
  content: [
246
314
  json({
247
315
  ...design,
248
316
  usage: {
249
317
  install: catalog.usage.install,
250
- import: snippet(catalog.usage.import),
251
- react: snippet(catalog.usage.react),
252
- core: snippet(catalog.usage.core),
318
+ import: `import { ${design.slug} } from 'tabbied/patterns';`,
319
+ ...snippets,
253
320
  fit: catalog.usage.fit,
254
- cli: `npx tabbied render ${design.slug} --out ${design.slug}.svg`,
321
+ cli: `npx tabbied render ${design.slug} --out ${design.slug}.${extension}`,
322
+ cliSetup: CLI_SETUP,
255
323
  },
256
324
  // The number-one integration mistake, repeated here because an
257
325
  // agent that called get_design may never read llms-full.txt.
@@ -286,7 +354,7 @@ function previewTool(context) {
286
354
  properties: {
287
355
  slugs: {
288
356
  type: 'array',
289
- items: { type: 'string' },
357
+ items: { type: 'string', minLength: 1 },
290
358
  minItems: 1,
291
359
  maxItems: PREVIEW_MAX_SLUGS,
292
360
  description: 'Design slugs to look at, e.g. ["radius", "cleat"].',
@@ -295,6 +363,7 @@ function previewTool(context) {
295
363
  required: ['slugs'],
296
364
  additionalProperties: false,
297
365
  },
366
+ annotations: { readOnlyHint: true, openWorldHint: true },
298
367
  };
299
368
  return {
300
369
  definition,
@@ -306,12 +375,14 @@ function previewTool(context) {
306
375
  const requested = slugs.slice(0, PREVIEW_MAX_SLUGS);
307
376
  const content = [];
308
377
  let spent = 0;
378
+ let found = 0;
309
379
  for (const [index, slug] of requested.entries()) {
310
380
  const design = findDesign(catalog, slug);
311
381
  if (!design) {
312
382
  content.push(text(`${slug}: no such design. Use search_designs to find valid slugs.`));
313
383
  continue;
314
384
  }
385
+ found += 1;
315
386
  if (spent > PREVIEW_BYTE_BUDGET) {
316
387
  content.push(text(`Stopped after ${index} image(s) to stay within a sane response ` +
317
388
  `size. Ask again for the rest: ${requested.slice(index).join(', ')}.`));
@@ -327,7 +398,9 @@ function previewTool(context) {
327
398
  content.push(text(`${design.slug}: preview unavailable (${error instanceof Error ? error.message : String(error)}). The image is also at ${design.preview}.`));
328
399
  }
329
400
  }
330
- return { content };
401
+ // One bad slug among good ones is a note beside the images; a call in
402
+ // which nothing resolved has failed, and says so.
403
+ return found > 0 ? { content } : { content, isError: true };
331
404
  },
332
405
  };
333
406
  }
@@ -340,11 +413,13 @@ function docsTool(context) {
340
413
  definition: {
341
414
  name: 'get_docs',
342
415
  title: 'Get the Tabbied reference',
343
- description: 'The complete agent-facing reference (llms-full.txt): entry points, the ' +
344
- 'React and vanilla APIs, sizing rules that compile but render wrong, ' +
416
+ description: 'The complete agent-facing reference (llms-full.txt): every entry point ' +
417
+ '(React, Vue, Svelte, the web component, plain HTML and the ' +
418
+ 'framework-free core), sizing rules that compile but render wrong, ' +
345
419
  'integration recipes, and the share-link URL scheme. Fetch this once ' +
346
420
  'before writing integration code; the per-design tools do not repeat it.',
347
421
  inputSchema: { type: 'object', additionalProperties: false },
422
+ annotations: { readOnlyHint: true, openWorldHint: true },
348
423
  },
349
424
  async run() {
350
425
  try {
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oEAAoE;AACpE,oBAAoB;AACpB,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,6EAA6E;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAW/C,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAChC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAE5B,6EAA6E;AAC7E,6EAA6E;AAC7E,qCAAqC;AACrC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAEtC,+EAA+E;AAE/E,MAAM,IAAI,GAAG,CAAC,KAAa,EAAe,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAE7E,MAAM,IAAI,GAAG,CAAC,KAAc,EAAe,EAAE,CAC3C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAEvC,MAAM,SAAS,GAAG,CAAC,OAAe,EAAc,EAAE,CAAC,CAAC;IAClD,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,OAAO,EAAE,IAAI;CACd,CAAC,CAAC;AAEH,oFAAoF;AACpF,SAAS,UAAU,CACjB,OAAwB,EACxB,KAAkC;IAElC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,SAAS,CAAC,OAAwB;IACzC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACtE,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;IACnF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAED,uFAAuF;AACvF,SAAS,SAAS,CAAC,MAAqB;IACtC,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,SAAS;QACrC,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB,EAAE,IAAa;IACjD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACxE,CAAC;AAED,sFAAsF;AACtF,SAAS,YAAY,CAAC,OAAgB,EAAE,IAAY;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAClC,OAAO,OAAO,CAAC,OAAO;SACnB,MAAM,CACL,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC7C;SACA,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,+EAA+E;AAE/E,SAAS,UAAU,CAAC,OAAgB;IAClC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvD,MAAM,aAAa,GAAG,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEjD,MAAM,UAAU,GAAmB;QACjC,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EACT,0EAA0E;YAC1E,4EAA4E;YAC5E,wEAAwE;YACxE,2EAA2E;YAC3E,yEAAyE;YACzE,2BAA2B,OAAO,CAAC,KAAK,oBAAoB;QAC9D,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,+DAA+D;wBAC/D,+DAA+D;iBAClE;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE;oBACrC,WAAW,EAAE,uDAAuD;iBACrE;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE;oBACtC,WAAW,EAAE,qCAAqC;iBACnD;gBACD,OAAO,EAAE;oBACP,IAAI,EAAE,QAAQ;oBACd,IAAI,EAAE,aAAa;oBACnB,WAAW,EACT,qEAAqE;wBACrE,gBAAgB;iBACnB;gBACD,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE;oBACxC,WAAW,EAAE,8CAA8C;iBAC5D;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,SAAS;oBACf,WAAW,EACT,oEAAoE;wBACpE,mEAAmE;wBACnE,sEAAsE;iBACzE;gBACD,KAAK,EAAE;oBACL,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,CAAC;oBACV,OAAO,EAAE,gBAAgB;oBACzB,WAAW,EAAE,mCAAmC,oBAAoB,IAAI;iBACzE;aACF;YACD,oBAAoB,EAAE,KAAK;SAC5B;KACF,CAAC;IAEF,OAAO;QACL,UAAU;QACV,GAAG,CAAC,IAAI;YACN,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;YACvE,MAAM,SAAS,GACb,OAAO,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;YAC9D,MAAM,KAAK,GACT,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ;gBAC5B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;gBACvD,CAAC,CAAC,EAAE,CAAC;YAET,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CACpB,IAAI,CAAC,GAAG,CACN,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC3D,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;gBACxB,CAAC,CAAC,oBAAoB,EACxB,CAAC,CACF,EACD,gBAAgB,CACjB,CAAC;YAEF,MAAM,OAAO,GAAG,CAAC,MAAqB,EAAE,EAAE;gBACxC,MAAM,QAAQ,GACZ,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC5E,OAAO,CACL,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBAC9C,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;oBACjE,CAAC,CAAC,QAAQ,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;oBACnE,CAAC,CAAC,WAAW;wBACX,WAAW,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC3D,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,CAAC;oBACxC,CAAC,SAAS,KAAK,IAAI,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,KAAK,SAAS,CAAC,CACjE,CAAC;YACJ,CAAC,CAAC;YAEF,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAE7C,qEAAqE;YACrE,yEAAyE;YACzE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,SAAwC,EAAE,EAAE,CAAC,CAAC;oBAC1E,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM;iBAClD,CAAC,CAAC;gBACH,MAAM,SAAS,GAAG;oBAChB,GAAG,CAAC,KAAK,CAAC,MAAM;wBACd,CAAC,CAAC;4BACE,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CACxC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CACnB,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,WAAW,IAAI,EAAE,EAAE;iCACxD,WAAW,EAAE;iCACb,QAAQ,CAAC,IAAI,CAAC,CAClB,CACF;yBACF;wBACH,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAC9B,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAC7D;oBACD,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAChE;oBACD,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACjC,KAAK,CAAC,YAAY,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CACpE;oBACD,GAAG,CAAC,OAAO;wBACT,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,OAAO,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;wBACzE,CAAC,CAAC,EAAE,CAAC;oBACP,GAAG,CAAC,SAAS,KAAK,IAAI;wBACpB,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,SAAS,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;wBACzF,CAAC,CAAC,EAAE,CAAC;iBACR,CAAC;gBAEF,OAAO;oBACL,OAAO,EAAE;wBACP,IAAI,CAAC;4BACH,OAAO,EAAE,CAAC;4BACV,KAAK,EAAE,OAAO,CAAC,KAAK;4BACpB,IAAI,EACF,+DAA+D;gCAC/D,iEAAiE;gCACjE,wCAAwC;4BAC1C,oBAAoB,EAAE,SAAS;4BAC/B,UAAU,EAAE;gCACV,IAAI;gCACJ,IAAI,EAAE,KAAK;gCACX,OAAO,EAAE,aAAa;gCACtB,OAAO;6BACR;yBACF,CAAC;qBACH;iBACF,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,OAAO,EAAE;oBACP,IAAI,CAAC;wBACH,OAAO,EAAE,IAAI,CAAC,MAAM;wBACpB,KAAK,EAAE,OAAO,CAAC,KAAK;wBACpB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;wBACtC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;qBAC7C,CAAC;iBACH;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,SAAS,aAAa,CAAC,OAAgB;IACrC,MAAM,UAAU,GAAmB;QACjC,IAAI,EAAE,YAAY;QAClB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,0EAA0E;YAC1E,2EAA2E;YAC3E,+DAA+D;YAC/D,qDAAqD;QACvD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,0DAA0D;iBACxE;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;YAClB,oBAAoB,EAAE,KAAK;SAC5B;KACF,CAAC;IAEF,OAAO;QACL,UAAU;QACV,GAAG,CAAC,IAAI;YACN,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC5D,OAAO,SAAS,CACd,wBAAwB,IAAI,CAAC,IAAI,KAAK;oBACpC,CAAC,IAAI,CAAC,MAAM;wBACV,CAAC,CAAC,kBAAkB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;wBACvC,CAAC,CAAC,EAAE,CAAC;oBACP,0EAA0E,CAC7E,CAAC;YACJ,CAAC;YAED,MAAM,OAAO,GAAG,CAAC,QAAgB,EAAE,EAAE,CACnC,QAAQ,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YAE7C,OAAO;gBACL,OAAO,EAAE;oBACP,IAAI,CAAC;wBACH,GAAG,MAAM;wBACT,KAAK,EAAE;4BACL,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO;4BAC9B,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC;4BACrC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;4BACnC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;4BACjC,GAAG,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG;4BACtB,GAAG,EAAE,sBAAsB,MAAM,CAAC,IAAI,UAAU,MAAM,CAAC,IAAI,MAAM;yBAClE;wBACD,+DAA+D;wBAC/D,6DAA6D;wBAC7D,MAAM,EACJ,6DAA6D;4BAC7D,+DAA+D;4BAC/D,sCAAsC;wBACxC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS;4BAC5B,CAAC,CAAC,EAAE;4BACJ,CAAC,CAAC;gCACE,gBAAgB,EACd,yEAAyE;6BAC5E,CAAC;qBACP,CAAC;iBACH;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,SAAS,WAAW,CAAC,OAAoB;IACvC,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAC1C,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAE/B,MAAM,UAAU,GAAmB;QACjC,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,iBAAiB;QACxB,WAAW,EACT,+CAA+C,iBAAiB,YAAY;YAC5E,0EAA0E;YAC1E,4EAA4E;YAC5E,wEAAwE;QAC1E,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,QAAQ,EAAE,CAAC;oBACX,QAAQ,EAAE,iBAAiB;oBAC3B,WAAW,EAAE,oDAAoD;iBAClE;aACF;YACD,QAAQ,EAAE,CAAC,OAAO,CAAC;YACnB,oBAAoB,EAAE,KAAK;SAC5B;KACF,CAAC;IAEF,OAAO;QACL,UAAU;QACV,KAAK,CAAC,GAAG,CAAC,IAAI;YACZ,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,SAAS,CAAC,oDAAoD,CAAC,CAAC;YACzE,CAAC;YAED,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;YACpD,MAAM,OAAO,GAAkB,EAAE,CAAC;YAClC,IAAI,KAAK,GAAG,CAAC,CAAC;YAEd,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;gBAChD,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBACzC,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,CAAC,IAAI,CACV,IAAI,CAAC,GAAG,IAAI,2DAA2D,CAAC,CACzE,CAAC;oBACF,SAAS;gBACX,CAAC;gBAED,IAAI,KAAK,GAAG,mBAAmB,EAAE,CAAC;oBAChC,OAAO,CAAC,IAAI,CACV,IAAI,CACF,iBAAiB,KAAK,2CAA2C;wBAC/D,iCAAiC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACxE,CACF,CAAC;oBACF,MAAM;gBACR,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;oBACtD,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;oBACrB,OAAO,CAAC,IAAI,CACV,IAAI,CACF,GAAG,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,IAAI,EAAE;wBAC/B,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CACxD,EACD,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAClC,CAAC;gBACJ,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,IAAI,CACV,IAAI,CACF,GAAG,MAAM,CAAC,IAAI,0BACZ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,2BAA2B,MAAM,CAAC,OAAO,GAAG,CAC7C,CACF,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,EAAE,OAAO,EAAE,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E,SAAS,QAAQ,CAAC,OAAoB;IACpC,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAC9B,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,OAAO;QACL,UAAU,EAAE;YACV,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,2BAA2B;YAClC,WAAW,EACT,yEAAyE;gBACzE,sEAAsE;gBACtE,sEAAsE;gBACtE,yEAAyE;YAC3E,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,KAAK,EAAE;SAC7D;QACD,KAAK,CAAC,GAAG;YACP,IAAI,CAAC;gBACH,OAAO,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC;YAChD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,SAAS,CACd,iCACE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,4DAA4D,CAC7D,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,OAAoB;IAC/C,OAAO;QACL,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC;QAC3B,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9B,WAAW,CAAC,OAAO,CAAC;QACpB,QAAQ,CAAC,OAAO,CAAC;QACjB,GAAG,aAAa,CAAC,OAAO,CAAC;KAC1B,CAAC,MAAM,CAAC,CAAC,IAAI,EAAgB,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAClD,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAE1E,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC;QAChD,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI;YACnB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,SAAS,CACd,iBAAiB,IAAI,iBAAiB,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACvE,CAAC;YACJ,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACpC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,SAAS,CACd,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["// The Tabbied toolset: four thin wrappers over catalog.json, the template tools\n// (templates.ts), and whatever the host adds (the stdio server adds\n// `render_design`).\n//\n// Nothing here touches node or the Worker runtime; the host injects what\n// differs via ToolContext, so both transports describe a design identically.\n//\n// The point is design *selection*. Slugs are opaque (\"cleat\", \"karst\") and\n// these are pictures, so search narrows on closed-vocabulary enums, then\n// preview_design puts the shortlist in front of the model before it commits.\nimport { templateTools } from './templates.js';\nimport type {\n Catalog,\n CatalogDesign,\n Tool,\n ToolContent,\n ToolContext,\n ToolDefinition,\n ToolResult,\n} from './types.js';\n\nconst SEARCH_LIMIT_DEFAULT = 20;\nconst SEARCH_LIMIT_MAX = 50;\nconst PREVIEW_MAX_SLUGS = 6;\n\n// Previews reach a few hundred KB and base64 adds a third, so six large ones\n// would spend megabytes of context. The batch stops early and says so rather\n// than silently truncating the list.\nconst PREVIEW_BYTE_BUDGET = 1_500_000;\n\n// ---- helpers ---------------------------------------------------------------\n\nconst text = (value: string): ToolContent => ({ type: 'text', text: value });\n\nconst json = (value: unknown): ToolContent =>\n text(JSON.stringify(value, null, 2));\n\nconst toolError = (message: string): ToolResult => ({\n content: [text(message)],\n isError: true,\n});\n\n/** Distinct values of a repeated field, sorted - the vocabulary actually in use. */\nfunction vocabulary(\n designs: CatalogDesign[],\n field: 'tags' | 'mood' | 'goodFor'\n): string[] {\n return [...new Set(designs.flatMap((design) => design[field]))].sort();\n}\n\nfunction densities(designs: CatalogDesign[]): string[] {\n return [...new Set(designs.map((design) => design.density))].sort();\n}\n\nfunction asStringArray(value: unknown): string[] | null {\n if (value == null) return null;\n const list = Array.isArray(value) ? value : [value];\n const strings = list.filter((entry): entry is string => typeof entry === 'string');\n return strings.length ? strings : null;\n}\n\n/** The compact form used in search hits - enough to shortlist, not the full record. */\nfunction summarize(design: CatalogDesign) {\n return {\n slug: design.slug,\n name: design.name,\n ...(design.description ? { description: design.description } : {}),\n tags: design.tags,\n mood: design.mood,\n density: design.density,\n goodFor: design.goodFor,\n svgExport: design.svgExport.supported,\n preview: design.preview,\n };\n}\n\nfunction findDesign(catalog: Catalog, slug: unknown): CatalogDesign | null {\n if (typeof slug !== 'string') return null;\n return catalog.designs.find((design) => design.slug === slug) ?? null;\n}\n\n/** Near misses for an unknown slug: a shared three-letter prefix, or a name match. */\nfunction suggestSlugs(catalog: Catalog, slug: string): string[] {\n const needle = slug.toLowerCase();\n return catalog.designs\n .filter(\n (design) =>\n design.slug.startsWith(needle.slice(0, 3)) ||\n design.name.toLowerCase().includes(needle)\n )\n .slice(0, 8)\n .map((design) => design.slug);\n}\n\n// ---- search_designs --------------------------------------------------------\n\nfunction searchTool(catalog: Catalog): Tool {\n const tags = vocabulary(catalog.designs, 'tags');\n const moods = vocabulary(catalog.designs, 'mood');\n const goodFor = vocabulary(catalog.designs, 'goodFor');\n const densityValues = densities(catalog.designs);\n\n const definition: ToolDefinition = {\n name: 'search_designs',\n title: 'Search designs',\n description:\n `Find Tabbied designs by visible motif, feel, busyness, or intended use. ` +\n `Filters combine with AND - listing two tags returns only designs carrying ` +\n `both - so start broad and narrow. Returns compact entries including a ` +\n `preview image URL; follow up with preview_design to actually look at the ` +\n `shortlist before choosing, because these are pictures and the metadata ` +\n `only narrows the field. ${catalog.count} designs in total.`,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description:\n 'Free text matched against slug, name, and description. Every ' +\n 'whitespace-separated word must appear somewhere in the entry.',\n },\n tags: {\n type: 'array',\n items: { type: 'string', enum: tags },\n description: 'Visible motifs. A design must carry every tag listed.',\n },\n mood: {\n type: 'array',\n items: { type: 'string', enum: moods },\n description: 'The feel of the design at a glance.',\n },\n density: {\n type: 'string',\n enum: densityValues,\n description:\n 'How busy the surface is. sparse takes overlaid text well; dense is ' +\n 'ink-dominated.',\n },\n goodFor: {\n type: 'array',\n items: { type: 'string', enum: goodFor },\n description: 'Uses the design suits with no customization.',\n },\n svgExport: {\n type: 'boolean',\n description:\n 'Restrict to designs that can (true) or cannot (false) be exported ' +\n 'as true vector SVG. Some use CSS (smooth conic sweeps, double or ' +\n 'dashed borders, 3D transforms) that SVG cannot represent faithfully.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: SEARCH_LIMIT_MAX,\n description: `Maximum hits to return (default ${SEARCH_LIMIT_DEFAULT}).`,\n },\n },\n additionalProperties: false,\n },\n };\n\n return {\n definition,\n run(args) {\n const wantTags = asStringArray(args.tags);\n const wantMood = asStringArray(args.mood);\n const wantGoodFor = asStringArray(args.goodFor);\n const density = typeof args.density === 'string' ? args.density : null;\n const svgExport =\n typeof args.svgExport === 'boolean' ? args.svgExport : null;\n const terms =\n typeof args.query === 'string'\n ? args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n : [];\n\n const limit = Math.min(\n Math.max(\n typeof args.limit === 'number' && Number.isFinite(args.limit)\n ? Math.trunc(args.limit)\n : SEARCH_LIMIT_DEFAULT,\n 1\n ),\n SEARCH_LIMIT_MAX\n );\n\n const matches = (design: CatalogDesign) => {\n const haystack =\n `${design.slug} ${design.name} ${design.description ?? ''}`.toLowerCase();\n return (\n terms.every((term) => haystack.includes(term)) &&\n (!wantTags || wantTags.every((tag) => design.tags.includes(tag))) &&\n (!wantMood || wantMood.every((mood) => design.mood.includes(mood))) &&\n (!wantGoodFor ||\n wantGoodFor.every((use) => design.goodFor.includes(use))) &&\n (!density || design.density === density) &&\n (svgExport === null || design.svgExport.supported === svgExport)\n );\n };\n\n const hits = catalog.designs.filter(matches);\n\n // From an empty result an agent can't tell an over-narrow AND from a\n // wrongly guessed vocabulary, so report how each filter does on its own.\n if (hits.length === 0) {\n const alone = (label: string, predicate: (d: CatalogDesign) => boolean) => ({\n filter: label,\n matches: catalog.designs.filter(predicate).length,\n });\n const breakdown = [\n ...(terms.length\n ? [\n alone(`query \"${args.query}\"`, (design) =>\n terms.every((term) =>\n `${design.slug} ${design.name} ${design.description ?? ''}`\n .toLowerCase()\n .includes(term)\n )\n ),\n ]\n : []),\n ...(wantTags ?? []).map((tag) =>\n alone(`tag \"${tag}\"`, (design) => design.tags.includes(tag))\n ),\n ...(wantMood ?? []).map((mood) =>\n alone(`mood \"${mood}\"`, (design) => design.mood.includes(mood))\n ),\n ...(wantGoodFor ?? []).map((use) =>\n alone(`goodFor \"${use}\"`, (design) => design.goodFor.includes(use))\n ),\n ...(density\n ? [alone(`density \"${density}\"`, (design) => design.density === density)]\n : []),\n ...(svgExport !== null\n ? [alone(`svgExport ${svgExport}`, (design) => design.svgExport.supported === svgExport)]\n : []),\n ];\n\n return {\n content: [\n json({\n matched: 0,\n total: catalog.count,\n note:\n 'No design satisfies every filter at once. Each filter on its ' +\n 'own matches the counts below - drop the ones scoring 0 (likely ' +\n 'out of vocabulary) and relax the rest.',\n filtersIndependently: breakdown,\n vocabulary: {\n tags,\n mood: moods,\n density: densityValues,\n goodFor,\n },\n }),\n ],\n };\n }\n\n return {\n content: [\n json({\n matched: hits.length,\n total: catalog.count,\n returned: Math.min(hits.length, limit),\n designs: hits.slice(0, limit).map(summarize),\n }),\n ],\n };\n },\n };\n}\n\n// ---- get_design ------------------------------------------------------------\n\nfunction getDesignTool(catalog: Catalog): Tool {\n const definition: ToolDefinition = {\n name: 'get_design',\n title: 'Get design details',\n description:\n 'The full catalog record for one design - palette, every option with its ' +\n 'range and default, SVG-export support, preview URL - plus ready-to-paste ' +\n 'React and vanilla snippets. Call this once a slug is chosen; ' +\n 'search_designs already returns enough to shortlist.',\n inputSchema: {\n type: 'object',\n properties: {\n slug: {\n type: 'string',\n description: 'The design slug, e.g. \"radius\". Slugs are not guessable.',\n },\n },\n required: ['slug'],\n additionalProperties: false,\n },\n };\n\n return {\n definition,\n run(args) {\n const design = findDesign(catalog, args.slug);\n if (!design) {\n const near = suggestSlugs(catalog, String(args.slug ?? ''));\n return toolError(\n `No design with slug \"${args.slug}\". ` +\n (near.length\n ? `Closest slugs: ${near.join(', ')}. `\n : '') +\n 'Use search_designs to find one - slugs are opaque and cannot be guessed.'\n );\n }\n\n const snippet = (template: string) =>\n template.replaceAll('<slug>', design.slug);\n\n return {\n content: [\n json({\n ...design,\n usage: {\n install: catalog.usage.install,\n import: snippet(catalog.usage.import),\n react: snippet(catalog.usage.react),\n core: snippet(catalog.usage.core),\n fit: catalog.usage.fit,\n cli: `npx tabbied render ${design.slug} --out ${design.slug}.svg`,\n },\n // The number-one integration mistake, repeated here because an\n // agent that called get_design may never read llms-full.txt.\n sizing:\n 'A pattern has no intrinsic size - it fills its parent, and ' +\n 'collapses to nothing in a parent that sizes to content. Pass ' +\n 'height or aspectRatio when in doubt.',\n ...(design.svgExport.supported\n ? {}\n : {\n svgExportWarning:\n 'This design cannot be exported as vector SVG. Render it as PNG instead.',\n }),\n }),\n ],\n };\n },\n };\n}\n\n// ---- preview_design --------------------------------------------------------\n\nfunction previewTool(context: ToolContext): Tool | null {\n const { catalog, fetchPreview } = context;\n if (!fetchPreview) return null;\n\n const definition: ToolDefinition = {\n name: 'preview_design',\n title: 'Look at designs',\n description:\n `Return the rendered preview image for up to ${PREVIEW_MAX_SLUGS} designs, ` +\n 'so you can see them rather than infer them from tags. Previews use each ' +\n \"design's authored palette, default options, and a fixed seed. This is the \" +\n 'step that makes a choice reliable - do it before committing to a slug.',\n inputSchema: {\n type: 'object',\n properties: {\n slugs: {\n type: 'array',\n items: { type: 'string' },\n minItems: 1,\n maxItems: PREVIEW_MAX_SLUGS,\n description: 'Design slugs to look at, e.g. [\"radius\", \"cleat\"].',\n },\n },\n required: ['slugs'],\n additionalProperties: false,\n },\n };\n\n return {\n definition,\n async run(args) {\n const slugs = asStringArray(args.slugs);\n if (!slugs) {\n return toolError('Pass \"slugs\" as a non-empty array of design slugs.');\n }\n\n const requested = slugs.slice(0, PREVIEW_MAX_SLUGS);\n const content: ToolContent[] = [];\n let spent = 0;\n\n for (const [index, slug] of requested.entries()) {\n const design = findDesign(catalog, slug);\n if (!design) {\n content.push(\n text(`${slug}: no such design. Use search_designs to find valid slugs.`)\n );\n continue;\n }\n\n if (spent > PREVIEW_BYTE_BUDGET) {\n content.push(\n text(\n `Stopped after ${index} image(s) to stay within a sane response ` +\n `size. Ask again for the rest: ${requested.slice(index).join(', ')}.`\n )\n );\n break;\n }\n\n try {\n const { data, mimeType } = await fetchPreview(design);\n spent += data.length;\n content.push(\n text(\n `${design.slug} - ${design.name}` +\n (design.description ? `: ${design.description}` : '')\n ),\n { type: 'image', data, mimeType }\n );\n } catch (error) {\n content.push(\n text(\n `${design.slug}: preview unavailable (${\n error instanceof Error ? error.message : String(error)\n }). The image is also at ${design.preview}.`\n )\n );\n }\n }\n\n return { content };\n },\n };\n}\n\n// ---- get_docs --------------------------------------------------------------\n\nfunction docsTool(context: ToolContext): Tool | null {\n const { fetchDocs } = context;\n if (!fetchDocs) return null;\n\n return {\n definition: {\n name: 'get_docs',\n title: 'Get the Tabbied reference',\n description:\n 'The complete agent-facing reference (llms-full.txt): entry points, the ' +\n 'React and vanilla APIs, sizing rules that compile but render wrong, ' +\n 'integration recipes, and the share-link URL scheme. Fetch this once ' +\n 'before writing integration code; the per-design tools do not repeat it.',\n inputSchema: { type: 'object', additionalProperties: false },\n },\n async run() {\n try {\n return { content: [text(await fetchDocs())] };\n } catch (error) {\n return toolError(\n `Could not load the reference (${\n error instanceof Error ? error.message : String(error)\n }). It is also served at https://tabbied.com/llms-full.txt.`\n );\n }\n },\n };\n}\n\n// ---- assembly --------------------------------------------------------------\n\n/**\n * The catalog-backed tools available in every runtime, template tools\n * included. A host opts into a tool by supplying its fetcher in the one\n * context, so the tool list follows from what the host can resolve.\n */\nexport function catalogTools(context: ToolContext): Tool[] {\n return [\n searchTool(context.catalog),\n getDesignTool(context.catalog),\n previewTool(context),\n docsTool(context),\n ...templateTools(context),\n ].filter((tool): tool is Tool => tool !== null);\n}\n\nexport type Toolset = {\n list(): ToolDefinition[];\n call(name: string, args: Record<string, unknown>): Promise<ToolResult>;\n has(name: string): boolean;\n};\n\n/**\n * Bundle tools into a name lookup. Handler throws become `isError` results\n * rather than JSON-RPC errors: the spec reserves protocol errors for malformed\n * requests and sends execution failures back to the model to retry.\n */\nexport function createToolset(tools: Tool[]): Toolset {\n const byName = new Map(tools.map((tool) => [tool.definition.name, tool]));\n\n return {\n list: () => tools.map((tool) => tool.definition),\n has: (name) => byName.has(name),\n async call(name, args) {\n const tool = byName.get(name);\n if (!tool) {\n return toolError(\n `Unknown tool \"${name}\". Available: ${[...byName.keys()].join(', ')}.`\n );\n }\n try {\n return await tool.run(args ?? {});\n } catch (error) {\n return toolError(\n error instanceof Error ? error.message : String(error)\n );\n }\n },\n };\n}\n"]}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["// The Tabbied toolset: four thin wrappers over catalog.json, the template tools\n// (templates.ts), and whatever the host adds (the stdio server adds\n// `render_design`).\n//\n// Nothing here touches node or the Worker runtime; the host injects what\n// differs via ToolContext, so both transports describe a design identically.\n//\n// The point is design *selection*. Slugs are opaque (\"cleat\", \"karst\") and\n// these are pictures, so search narrows on closed-vocabulary enums, then\n// preview_design puts the shortlist in front of the model before it commits.\nimport {\n SNIPPET_SETUPS,\n buildSnippet,\n buildSnippets,\n parseRatio,\n type SnippetInput,\n type SnippetSetup,\n} from 'tabbied/snippets';\n\nimport { templateTools } from './templates.js';\nimport type {\n Catalog,\n CatalogDesign,\n Tool,\n ToolContent,\n ToolContext,\n ToolDefinition,\n ToolResult,\n} from './types.js';\n\nconst SEARCH_LIMIT_DEFAULT = 20;\nconst SEARCH_LIMIT_MAX = 50;\nconst PREVIEW_MAX_SLUGS = 6;\n\n// Previews reach a few hundred KB and base64 adds a third, so six large ones\n// would spend megabytes of context. The batch stops early and says so rather\n// than silently truncating the list.\nconst PREVIEW_BYTE_BUDGET = 1_500_000;\n\n// ---- helpers ---------------------------------------------------------------\n\nconst text = (value: string): ToolContent => ({ type: 'text', text: value });\n\nconst json = (value: unknown): ToolContent =>\n text(JSON.stringify(value, null, 2));\n\nconst toolError = (message: string): ToolResult => ({\n content: [text(message)],\n isError: true,\n});\n\n/** Distinct values of a repeated field, sorted - the vocabulary actually in use. */\nfunction vocabulary(\n designs: CatalogDesign[],\n field: 'tags' | 'mood' | 'goodFor'\n): string[] {\n return [...new Set(designs.flatMap((design) => design[field]))].sort();\n}\n\nfunction densities(designs: CatalogDesign[]): string[] {\n return [...new Set(designs.map((design) => design.density))].sort();\n}\n\nfunction asStringArray(value: unknown): string[] | null {\n if (value == null) return null;\n const list = Array.isArray(value) ? value : [value];\n const strings = list.filter((entry): entry is string => typeof entry === 'string');\n return strings.length ? strings : null;\n}\n\n/** The compact form used in search hits - enough to shortlist, not the full record. */\nfunction summarize(design: CatalogDesign) {\n return {\n slug: design.slug,\n name: design.name,\n ...(design.description ? { description: design.description } : {}),\n tags: design.tags,\n mood: design.mood,\n density: design.density,\n goodFor: design.goodFor,\n svgExport: design.svgExport.supported,\n preview: design.preview,\n };\n}\n\n/**\n * What a free-text query is matched against: slug, name, the closed-vocabulary\n * tags, moods and uses, and the description. The site's gallery search\n * (lib/catalogSearch.ts) reads the same fields, so \"dots\" finds the designs\n * tagged dots in both, not only one whose name says it.\n */\nfunction designKeywords(design: CatalogDesign): string {\n return [\n design.slug,\n design.name,\n ...design.tags,\n ...design.mood,\n ...design.goodFor,\n design.description ?? '',\n ]\n .join(' ')\n .toLowerCase();\n}\n\nfunction findDesign(catalog: Catalog, slug: unknown): CatalogDesign | null {\n if (typeof slug !== 'string') return null;\n return catalog.designs.find((design) => design.slug === slug) ?? null;\n}\n\n/**\n * The snippets' input for one design as the catalog describes it: its own\n * ratio (the catalog's `2:3` id, which the builders write as CSS's `2 / 3`)\n * and its own ground color, with nothing else chosen, so each snippet draws\n * the design as authored. The builders are tabbied/snippets, the same ones\n * the site's editor copies from, so the two agree.\n */\nfunction snippetInput(catalog: Catalog, design: CatalogDesign): SnippetInput {\n return {\n slug: design.slug,\n ratio: parseRatio(design.defaultAspectRatio),\n ...(design.palette[0] ? { ground: design.palette[0] } : {}),\n version: catalog.version,\n };\n}\n\nconst isSetup = (value: unknown): value is SnippetSetup =>\n typeof value === 'string' && (SNIPPET_SETUPS as readonly string[]).includes(value);\n\n/** What the CLI command in `usage.cli` needs where it runs. */\nconst CLI_SETUP =\n 'Rendering needs Playwright, found from the current directory first: ' +\n '`npm i -D playwright && npx playwright install chromium`. With nothing ' +\n 'installed, run `npx -y -p tabbied -p playwright tabbied render ...` ' +\n 'instead (and `npx playwright install chromium` once).';\n\n/** Near misses for an unknown slug: a shared three-letter prefix, or a name match. */\nfunction suggestSlugs(catalog: Catalog, slug: string): string[] {\n const needle = slug.trim().toLowerCase();\n // An empty needle is a prefix of every slug, so it would \"suggest\" the first\n // eight in the catalog.\n if (!needle) return [];\n return catalog.designs\n .filter(\n (design) =>\n design.slug.startsWith(needle.slice(0, 3)) ||\n design.name.toLowerCase().includes(needle)\n )\n .slice(0, 8)\n .map((design) => design.slug);\n}\n\n// ---- search_designs --------------------------------------------------------\n\nfunction searchTool(catalog: Catalog): Tool {\n const tags = vocabulary(catalog.designs, 'tags');\n const moods = vocabulary(catalog.designs, 'mood');\n const goodFor = vocabulary(catalog.designs, 'goodFor');\n const densityValues = densities(catalog.designs);\n\n const definition: ToolDefinition = {\n name: 'search_designs',\n title: 'Search designs',\n description:\n `Find Tabbied designs by visible motif, feel, busyness, or intended use. ` +\n `Filters combine with AND - listing two tags returns only designs carrying ` +\n `both - so start broad and narrow. Returns compact entries including a ` +\n `preview image URL; follow up with preview_design to actually look at the ` +\n `shortlist before choosing, because these are pictures and the metadata ` +\n `only narrows the field. ${catalog.count} designs in total.`,\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description:\n 'Free text matched against slug, name, tags, mood, goodFor and ' +\n 'description. Every whitespace-separated word must appear ' +\n 'somewhere in the entry.',\n },\n tags: {\n type: 'array',\n items: { type: 'string', enum: tags },\n description: 'Visible motifs. A design must carry every tag listed.',\n },\n mood: {\n type: 'array',\n items: { type: 'string', enum: moods },\n description: 'The feel of the design at a glance.',\n },\n density: {\n type: 'string',\n enum: densityValues,\n description:\n 'How busy the surface is. sparse takes overlaid text well; dense is ' +\n 'ink-dominated.',\n },\n goodFor: {\n type: 'array',\n items: { type: 'string', enum: goodFor },\n description: 'Uses the design suits with no customization.',\n },\n svgExport: {\n type: 'boolean',\n description:\n 'Restrict to designs that can (true) or cannot (false) be exported ' +\n 'as true vector SVG. Some use CSS (smooth conic sweeps, double or ' +\n 'dashed borders, 3D transforms) that SVG cannot represent faithfully.',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: SEARCH_LIMIT_MAX,\n description: `Maximum hits to return (default ${SEARCH_LIMIT_DEFAULT}).`,\n },\n },\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, openWorldHint: false },\n };\n\n return {\n definition,\n run(args) {\n const wantTags = asStringArray(args.tags);\n const wantMood = asStringArray(args.mood);\n const wantGoodFor = asStringArray(args.goodFor);\n const density = typeof args.density === 'string' ? args.density : null;\n const svgExport =\n typeof args.svgExport === 'boolean' ? args.svgExport : null;\n const terms =\n typeof args.query === 'string'\n ? args.query.toLowerCase().split(/\\s+/).filter(Boolean)\n : [];\n\n const limit = Math.min(\n Math.max(\n typeof args.limit === 'number' && Number.isFinite(args.limit)\n ? Math.trunc(args.limit)\n : SEARCH_LIMIT_DEFAULT,\n 1\n ),\n SEARCH_LIMIT_MAX\n );\n\n const matches = (design: CatalogDesign) => {\n const haystack = designKeywords(design);\n return (\n terms.every((term) => haystack.includes(term)) &&\n (!wantTags || wantTags.every((tag) => design.tags.includes(tag))) &&\n (!wantMood || wantMood.every((mood) => design.mood.includes(mood))) &&\n (!wantGoodFor ||\n wantGoodFor.every((use) => design.goodFor.includes(use))) &&\n (!density || design.density === density) &&\n (svgExport === null || design.svgExport.supported === svgExport)\n );\n };\n\n const hits = catalog.designs.filter(matches);\n\n // From an empty result an agent can't tell an over-narrow AND from a\n // wrongly guessed vocabulary, so report how each filter does on its own.\n if (hits.length === 0) {\n const alone = (label: string, predicate: (d: CatalogDesign) => boolean) => ({\n filter: label,\n matches: catalog.designs.filter(predicate).length,\n });\n const breakdown = [\n ...(terms.length\n ? [\n alone(`query \"${args.query}\"`, (design) =>\n terms.every((term) => designKeywords(design).includes(term))\n ),\n ]\n : []),\n ...(wantTags ?? []).map((tag) =>\n alone(`tag \"${tag}\"`, (design) => design.tags.includes(tag))\n ),\n ...(wantMood ?? []).map((mood) =>\n alone(`mood \"${mood}\"`, (design) => design.mood.includes(mood))\n ),\n ...(wantGoodFor ?? []).map((use) =>\n alone(`goodFor \"${use}\"`, (design) => design.goodFor.includes(use))\n ),\n ...(density\n ? [alone(`density \"${density}\"`, (design) => design.density === density)]\n : []),\n ...(svgExport !== null\n ? [alone(`svgExport ${svgExport}`, (design) => design.svgExport.supported === svgExport)]\n : []),\n ];\n\n return {\n content: [\n json({\n matched: 0,\n total: catalog.count,\n note:\n 'No design satisfies every filter at once. Each filter on its ' +\n 'own matches the counts below - drop the ones scoring 0 (likely ' +\n 'out of vocabulary) and relax the rest.',\n filtersIndependently: breakdown,\n vocabulary: {\n tags,\n mood: moods,\n density: densityValues,\n goodFor,\n },\n }),\n ],\n };\n }\n\n return {\n content: [\n json({\n matched: hits.length,\n total: catalog.count,\n returned: Math.min(hits.length, limit),\n designs: hits.slice(0, limit).map(summarize),\n }),\n ],\n };\n },\n };\n}\n\n// ---- get_design ------------------------------------------------------------\n\nfunction getDesignTool(catalog: Catalog): Tool {\n const definition: ToolDefinition = {\n name: 'get_design',\n title: 'Get design details',\n description:\n 'The full catalog record for one design - palette, every option with its ' +\n 'range and default, SVG-export support, preview URL - plus ready-to-paste ' +\n 'code for every setup: React, Vue, a Svelte action, the <tabbied-pattern> ' +\n 'web component, plain HTML with no build step, and framework-free ' +\n 'JavaScript (createPattern). Pass `framework` to get only the one the ' +\n 'project uses. Call this once a slug is chosen; search_designs already ' +\n 'returns enough to shortlist.',\n inputSchema: {\n type: 'object',\n properties: {\n slug: {\n type: 'string',\n minLength: 1,\n description: 'The design slug, e.g. \"radius\". Slugs are not guessable.',\n },\n framework: {\n type: 'string',\n enum: [...SNIPPET_SETUPS],\n description:\n 'Only this setup\\'s snippet: react, vue, svelte (the action, ' +\n 'SvelteKit included), element (the web component, for any page or ' +\n 'CMS), html (data attributes and one script, no build step) or core ' +\n '(createPattern on any element). Omit for all six.',\n },\n },\n required: ['slug'],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, openWorldHint: false },\n };\n\n return {\n definition,\n run(args) {\n const design = findDesign(catalog, args.slug);\n if (!design) {\n const near = suggestSlugs(catalog, String(args.slug ?? ''));\n return toolError(\n `No design with slug \"${args.slug}\". ` +\n (near.length\n ? `Closest slugs: ${near.join(', ')}. `\n : '') +\n 'Use search_designs to find one - slugs are opaque and cannot be guessed.'\n );\n }\n\n // The schema rejects an unknown setup before this runs; this keeps a\n // direct call (the toolset, without the SDK in front) as strict.\n const framework = args.framework;\n if (framework != null && !isSetup(framework)) {\n return toolError(\n `Unknown framework \"${String(framework)}\". One of: ${SNIPPET_SETUPS.join(', ')}.`\n );\n }\n\n const input = snippetInput(catalog, design);\n const snippets = framework\n ? { [framework]: buildSnippet(framework, input) }\n : buildSnippets(input);\n\n // A design that cannot be vectorized gets a command that works.\n const extension = design.svgExport.supported ? 'svg' : 'png';\n\n return {\n content: [\n json({\n ...design,\n usage: {\n install: catalog.usage.install,\n import: `import { ${design.slug} } from 'tabbied/patterns';`,\n ...snippets,\n fit: catalog.usage.fit,\n cli: `npx tabbied render ${design.slug} --out ${design.slug}.${extension}`,\n cliSetup: CLI_SETUP,\n },\n // The number-one integration mistake, repeated here because an\n // agent that called get_design may never read llms-full.txt.\n sizing:\n 'A pattern has no intrinsic size - it fills its parent, and ' +\n 'collapses to nothing in a parent that sizes to content. Pass ' +\n 'height or aspectRatio when in doubt.',\n ...(design.svgExport.supported\n ? {}\n : {\n svgExportWarning:\n 'This design cannot be exported as vector SVG. Render it as PNG instead.',\n }),\n }),\n ],\n };\n },\n };\n}\n\n// ---- preview_design --------------------------------------------------------\n\nfunction previewTool(context: ToolContext): Tool | null {\n const { catalog, fetchPreview } = context;\n if (!fetchPreview) return null;\n\n const definition: ToolDefinition = {\n name: 'preview_design',\n title: 'Look at designs',\n description:\n `Return the rendered preview image for up to ${PREVIEW_MAX_SLUGS} designs, ` +\n 'so you can see them rather than infer them from tags. Previews use each ' +\n \"design's authored palette, default options, and a fixed seed. This is the \" +\n 'step that makes a choice reliable - do it before committing to a slug.',\n inputSchema: {\n type: 'object',\n properties: {\n slugs: {\n type: 'array',\n items: { type: 'string', minLength: 1 },\n minItems: 1,\n maxItems: PREVIEW_MAX_SLUGS,\n description: 'Design slugs to look at, e.g. [\"radius\", \"cleat\"].',\n },\n },\n required: ['slugs'],\n additionalProperties: false,\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n };\n\n return {\n definition,\n async run(args) {\n const slugs = asStringArray(args.slugs);\n if (!slugs) {\n return toolError('Pass \"slugs\" as a non-empty array of design slugs.');\n }\n\n const requested = slugs.slice(0, PREVIEW_MAX_SLUGS);\n const content: ToolContent[] = [];\n let spent = 0;\n let found = 0;\n\n for (const [index, slug] of requested.entries()) {\n const design = findDesign(catalog, slug);\n if (!design) {\n content.push(\n text(`${slug}: no such design. Use search_designs to find valid slugs.`)\n );\n continue;\n }\n found += 1;\n\n if (spent > PREVIEW_BYTE_BUDGET) {\n content.push(\n text(\n `Stopped after ${index} image(s) to stay within a sane response ` +\n `size. Ask again for the rest: ${requested.slice(index).join(', ')}.`\n )\n );\n break;\n }\n\n try {\n const { data, mimeType } = await fetchPreview(design);\n spent += data.length;\n content.push(\n text(\n `${design.slug} - ${design.name}` +\n (design.description ? `: ${design.description}` : '')\n ),\n { type: 'image', data, mimeType }\n );\n } catch (error) {\n content.push(\n text(\n `${design.slug}: preview unavailable (${\n error instanceof Error ? error.message : String(error)\n }). The image is also at ${design.preview}.`\n )\n );\n }\n }\n\n // One bad slug among good ones is a note beside the images; a call in\n // which nothing resolved has failed, and says so.\n return found > 0 ? { content } : { content, isError: true };\n },\n };\n}\n\n// ---- get_docs --------------------------------------------------------------\n\nfunction docsTool(context: ToolContext): Tool | null {\n const { fetchDocs } = context;\n if (!fetchDocs) return null;\n\n return {\n definition: {\n name: 'get_docs',\n title: 'Get the Tabbied reference',\n description:\n 'The complete agent-facing reference (llms-full.txt): every entry point ' +\n '(React, Vue, Svelte, the web component, plain HTML and the ' +\n 'framework-free core), sizing rules that compile but render wrong, ' +\n 'integration recipes, and the share-link URL scheme. Fetch this once ' +\n 'before writing integration code; the per-design tools do not repeat it.',\n inputSchema: { type: 'object', additionalProperties: false },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async run() {\n try {\n return { content: [text(await fetchDocs())] };\n } catch (error) {\n return toolError(\n `Could not load the reference (${\n error instanceof Error ? error.message : String(error)\n }). It is also served at https://tabbied.com/llms-full.txt.`\n );\n }\n },\n };\n}\n\n// ---- assembly --------------------------------------------------------------\n\n/**\n * The catalog-backed tools available in every runtime, template tools\n * included. A host opts into a tool by supplying its fetcher in the one\n * context, so the tool list follows from what the host can resolve.\n */\nexport function catalogTools(context: ToolContext): Tool[] {\n return [\n searchTool(context.catalog),\n getDesignTool(context.catalog),\n previewTool(context),\n docsTool(context),\n ...templateTools(context),\n ].filter((tool): tool is Tool => tool !== null);\n}\n\nexport type Toolset = {\n list(): ToolDefinition[];\n call(name: string, args: Record<string, unknown>): Promise<ToolResult>;\n has(name: string): boolean;\n};\n\n/**\n * Bundle tools into a name lookup. Handler throws become `isError` results\n * rather than JSON-RPC errors: the spec reserves protocol errors for malformed\n * requests and sends execution failures back to the model to retry.\n */\nexport function createToolset(tools: Tool[]): Toolset {\n const byName = new Map(tools.map((tool) => [tool.definition.name, tool]));\n\n return {\n list: () => tools.map((tool) => tool.definition),\n has: (name) => byName.has(name),\n async call(name, args) {\n const tool = byName.get(name);\n if (!tool) {\n return toolError(\n `Unknown tool \"${name}\". Available: ${[...byName.keys()].join(', ')}.`\n );\n }\n try {\n return await tool.run(args ?? {});\n } catch (error) {\n return toolError(\n error instanceof Error ? error.message : String(error)\n );\n }\n },\n };\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { JsonSchemaType } from '@modelcontextprotocol/server';
1
+ import type { JsonSchemaType, ToolAnnotations } from '@modelcontextprotocol/server';
2
2
  /** One design, as it appears in catalog.json. */
3
3
  export type CatalogDesign = {
4
4
  slug: string;
@@ -41,6 +41,10 @@ export type Catalog = {
41
41
  export type TemplateCatalogEntry = {
42
42
  slug: string;
43
43
  name: string;
44
+ /** The gallery's category, e.g. "Food & drink". Absent from older indexes. */
45
+ category?: string;
46
+ /** One line on what the business is, e.g. "Wood-fire restaurant". */
47
+ topic?: string;
44
48
  href: string;
45
49
  spec: string;
46
50
  palette: string[];
@@ -109,6 +113,12 @@ export type ToolDefinition = {
109
113
  * rather than at the first `tools/list`.
110
114
  */
111
115
  inputSchema: JsonSchemaType;
116
+ /**
117
+ * Hints a client may show or act on (read-only, reaches the network, writes
118
+ * files). Hints, not guarantees: the spec tells clients not to trust them
119
+ * from an untrusted server, so nothing here relies on them being read.
120
+ */
121
+ annotations?: ToolAnnotations;
112
122
  };
113
123
  export type Tool = {
114
124
  definition: ToolDefinition;
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["// The shapes this server reads and writes.\nimport type { JsonSchemaType } from '@modelcontextprotocol/server';\n\n// The catalog types mirror what packages/tabbied/scripts/codegen.mjs emits into\n// catalog.json. They are structural rather than imported so the server can\n// read a catalog fetched from an older or newer version of the site.\n\n/** One design, as it appears in catalog.json. */\nexport type CatalogDesign = {\n slug: string;\n name: string;\n description?: string;\n tags: string[];\n mood: string[];\n density: string;\n goodFor: string[];\n preview: string;\n palette: string[];\n colors?: unknown;\n options: CatalogOption[];\n /** An aspect-ratio id such as `2:3`, as codegen emits it. */\n defaultAspectRatio?: string;\n svgExport: { supported: boolean; note?: string };\n};\n\nexport type CatalogOption = {\n id: string;\n label: string;\n type: string;\n default: unknown;\n values?: string[];\n min?: number;\n max?: number;\n step?: number;\n svgExportNote?: string;\n};\n\nexport type Catalog = {\n package: string;\n version: string;\n count: number;\n docs: string;\n usage: Record<string, string>;\n designs: CatalogDesign[];\n};\n\n// ---- template sites --------------------------------------------------------\n//\n// Mirrors what scripts/generate-editable.mjs emits into\n// public/editable-catalog.json and public/editable/<slug>.json. Structural\n// rather than imported from `tabbied-templates`, for the same reason as the\n// design catalog.\n\nexport type TemplateCatalogEntry = {\n slug: string;\n name: string;\n href: string;\n spec: string;\n palette: string[];\n patterns: string[];\n slots: { text: number; image: number; pattern: number };\n downloads: { html: string; react: string };\n};\n\nexport type TemplateCatalog = {\n specVersion: number;\n generated: number;\n templates: TemplateCatalogEntry[];\n};\n\n/** One site's editable-section spec. Slots stay opaque here - the tool passes\n * them through, and their shape is owned by `tabbied-templates`. */\nexport type TemplateSpec = {\n specVersion: number;\n site: { slug: string; name: string; topic?: string };\n palette: { colors: string[]; derivation: string; flatSections?: boolean };\n fonts?: Record<string, string>;\n slots: unknown[];\n};\n\n// ---- tools -----------------------------------------------------------------\n\n/**\n * A content block in a tool result. This is the subset of MCP's content types\n * the Tabbied tools actually produce; the shape is identical in both protocol\n * eras (see docs/mcp-server.md).\n */\nexport type ToolContent =\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string };\n\nexport type ToolResult = {\n content: ToolContent[];\n /**\n * A *tool execution* error (bad slug, unrenderable design), not a JSON-RPC\n * protocol error: the spec asks clients to feed these back to the model so\n * it can self-correct.\n */\n isError?: boolean;\n};\n\nexport type ToolDefinition = {\n name: string;\n title: string;\n description: string;\n /**\n * Plain JSON Schema, not Zod (see server.ts). Typed as the SDK's own type\n * (a type-only import) so a schema the SDK cannot register fails at `tsc`\n * rather than at the first `tools/list`.\n */\n inputSchema: JsonSchemaType;\n};\n\nexport type Tool = {\n definition: ToolDefinition;\n run(args: Record<string, unknown>): ToolResult | Promise<ToolResult>;\n};\n\n/**\n * Everything the catalog tools need from their host. The fetchers are injected\n * rather than imported because they differ per runtime: the Worker reads its\n * own static assets, the stdio server the local package and the network.\n */\nexport type ToolContext = {\n catalog: Catalog;\n /** Resolves a design's preview image to base64. Omit to drop `preview_design`. */\n fetchPreview?: (\n design: CatalogDesign\n ) => Promise<{ data: string; mimeType: string }>;\n /** Resolves the llms-full.txt reference. Omit to drop `get_docs`. */\n fetchDocs?: () => Promise<string>;\n /** Resolves the editable-template index. Omit to drop both template tools. */\n fetchTemplateCatalog?: () => Promise<TemplateCatalog>;\n /** Resolves one template's spec. Omit (with the above) to drop `get_template`. */\n fetchTemplate?: (slug: string) => Promise<TemplateSpec>;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["// The shapes this server reads and writes.\nimport type { JsonSchemaType, ToolAnnotations } from '@modelcontextprotocol/server';\n\n// The catalog types mirror what packages/tabbied/scripts/codegen.mjs emits into\n// catalog.json. They are structural rather than imported so the server can\n// read a catalog fetched from an older or newer version of the site.\n\n/** One design, as it appears in catalog.json. */\nexport type CatalogDesign = {\n slug: string;\n name: string;\n description?: string;\n tags: string[];\n mood: string[];\n density: string;\n goodFor: string[];\n preview: string;\n palette: string[];\n colors?: unknown;\n options: CatalogOption[];\n /** An aspect-ratio id such as `2:3`, as codegen emits it. */\n defaultAspectRatio?: string;\n svgExport: { supported: boolean; note?: string };\n};\n\nexport type CatalogOption = {\n id: string;\n label: string;\n type: string;\n default: unknown;\n values?: string[];\n min?: number;\n max?: number;\n step?: number;\n svgExportNote?: string;\n};\n\nexport type Catalog = {\n package: string;\n version: string;\n count: number;\n docs: string;\n usage: Record<string, string>;\n designs: CatalogDesign[];\n};\n\n// ---- template sites --------------------------------------------------------\n//\n// Mirrors what scripts/generate-editable.mjs emits into\n// public/editable-catalog.json and public/editable/<slug>.json. Structural\n// rather than imported from `tabbied-templates`, for the same reason as the\n// design catalog.\n\nexport type TemplateCatalogEntry = {\n slug: string;\n name: string;\n /** The gallery's category, e.g. \"Food & drink\". Absent from older indexes. */\n category?: string;\n /** One line on what the business is, e.g. \"Wood-fire restaurant\". */\n topic?: string;\n href: string;\n spec: string;\n palette: string[];\n patterns: string[];\n slots: { text: number; image: number; pattern: number };\n downloads: { html: string; react: string };\n};\n\nexport type TemplateCatalog = {\n specVersion: number;\n generated: number;\n templates: TemplateCatalogEntry[];\n};\n\n/** One site's editable-section spec. Slots stay opaque here - the tool passes\n * them through, and their shape is owned by `tabbied-templates`. */\nexport type TemplateSpec = {\n specVersion: number;\n site: { slug: string; name: string; topic?: string };\n palette: { colors: string[]; derivation: string; flatSections?: boolean };\n fonts?: Record<string, string>;\n slots: unknown[];\n};\n\n// ---- tools -----------------------------------------------------------------\n\n/**\n * A content block in a tool result. This is the subset of MCP's content types\n * the Tabbied tools actually produce; the shape is identical in both protocol\n * eras (see docs/mcp-server.md).\n */\nexport type ToolContent =\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string };\n\nexport type ToolResult = {\n content: ToolContent[];\n /**\n * A *tool execution* error (bad slug, unrenderable design), not a JSON-RPC\n * protocol error: the spec asks clients to feed these back to the model so\n * it can self-correct.\n */\n isError?: boolean;\n};\n\nexport type ToolDefinition = {\n name: string;\n title: string;\n description: string;\n /**\n * Plain JSON Schema, not Zod (see server.ts). Typed as the SDK's own type\n * (a type-only import) so a schema the SDK cannot register fails at `tsc`\n * rather than at the first `tools/list`.\n */\n inputSchema: JsonSchemaType;\n /**\n * Hints a client may show or act on (read-only, reaches the network, writes\n * files). Hints, not guarantees: the spec tells clients not to trust them\n * from an untrusted server, so nothing here relies on them being read.\n */\n annotations?: ToolAnnotations;\n};\n\nexport type Tool = {\n definition: ToolDefinition;\n run(args: Record<string, unknown>): ToolResult | Promise<ToolResult>;\n};\n\n/**\n * Everything the catalog tools need from their host. The fetchers are injected\n * rather than imported because they differ per runtime: the Worker reads its\n * own static assets, the stdio server the local package and the network.\n */\nexport type ToolContext = {\n catalog: Catalog;\n /** Resolves a design's preview image to base64. Omit to drop `preview_design`. */\n fetchPreview?: (\n design: CatalogDesign\n ) => Promise<{ data: string; mimeType: string }>;\n /** Resolves the llms-full.txt reference. Omit to drop `get_docs`. */\n fetchDocs?: () => Promise<string>;\n /** Resolves the editable-template index. Omit to drop both template tools. */\n fetchTemplateCatalog?: () => Promise<TemplateCatalog>;\n /** Resolves one template's spec. Omit (with the above) to drop `get_template`. */\n fetchTemplate?: (slug: string) => Promise<TemplateSpec>;\n};\n"]}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "tabbied-mcp",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for Tabbied: search, look at, and render its generative pattern designs from any MCP client.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "license": "MIT",
8
8
  "author": "Sy Hong and Ye Joo Park",
9
- "homepage": "https://tabbied.com",
9
+ "homepage": "https://tabbied.com/docs/mcp/",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/tabbied-design/tabbied.git",
@@ -26,6 +26,11 @@
26
26
  "import": "./dist/index.js",
27
27
  "default": "./dist/index.js"
28
28
  },
29
+ "./node": {
30
+ "types": "./dist/node/index.d.ts",
31
+ "import": "./dist/node/index.js",
32
+ "default": "./dist/node/index.js"
33
+ },
29
34
  "./package.json": "./package.json"
30
35
  },
31
36
  "main": "./dist/index.js",
@@ -44,13 +49,13 @@
44
49
  },
45
50
  "dependencies": {
46
51
  "@modelcontextprotocol/server": "^2.2.0",
47
- "tabbied": "^0.7.0"
52
+ "tabbied": "^0.9.0"
48
53
  },
49
54
  "devDependencies": {
50
55
  "@types/node": "^26.6.3",
51
56
  "typescript": "^7.0.2"
52
57
  },
53
58
  "engines": {
54
- "node": ">=18"
59
+ "node": ">=20"
55
60
  }
56
61
  }