Public log

@swebsy/mcp

MCP server that lets a coding agent (Claude Code, Codex, Cursor, Windsurf, …) drive an open Swebsy Studio tab over a local 127.0.0.1 WebSocket relay.

npm · fingerprint 7f8d98011a52772fe7fe0f9e · repository · RSS

44tools
2recorded versions
24dtracked
changed 2d ago

What changed

Changed 2026-10-28 13:06 UTC

8 descriptions rewritten, 1 schema or annotation change with the description left byte-identical.

--- pinned/swebsy_add_section
+++ observed/swebsy_add_section
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Insert a new section into the current page. Use for adding discrete sections (hero, features, pricing, CTA) to an existing page. Appends to the end by default; to place it somewhere specific, pass `position` plus the `targetPath` of the component to place it next to.",
+   "description": "Insert a new section into the current page. Use for adding discrete sections (hero, features, pricing, CTA) to an existing page. Supply exactly one of `component` (a component tree) or `html` (one root element). Appends to the end by default; to place it somewhere specific, pass `position` plus the `targetPath` of the component to place it next to.",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
-         "description": "The section component to insert.",
+         "description": "The section component to insert. Omit when sending `html`.",
          "properties": {
            "a": {
              "additionalProperties": {
+       "html": {
+         "description": "The section as HTML with a single root element (e.g. one <section>). No data-gjs-* attributes. Omit when sending `component`.",
+         "type": "string"
+       },
        "position": {
          "description": "Where to insert. Defaults to 'end' if omitted. 'before-selected'/'after-selected' are measured against `targetPath` when you pass one, otherwise against the canvas selection — so pass `targetPath` with them unless the user has selected something.",
          "enum": [
-       "summary",
-       "component"
+       "summary"
      ],
      "type": "object"
    },
--- pinned/swebsy_delete_section
+++ observed/swebsy_delete_section
  {
    "annotations": {
      "destructiveHint": true,
-   "description": "Remove a component from the current page entirely. Use this instead of emptying a section's contents — an emptied section leaves a stray blank div on the page. Deletes the selected component by default; pass `targetPath` (from `read_page`) to delete a component you did not select.",
+   "description": "Remove a component from the current page entirely. Use this instead of emptying a section's contents — an emptied section leaves a stray blank div on the page. Deletes the selected component by default; pass `targetId` or `targetPath` (from `read_page`) to delete a component you did not select.",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
+       "targetId": {
+         "description": "The component to delete by its id, as `read_page` shows it after `#` (e.g. \"ieyubib\", or \"call-2-2\" in a symbol instance). Stays valid when other components move or are added, unlike a path. Pass this OR the path, not both.",
+         "minLength": 1,
+         "type": "string"
+       },
        "targetPath": {
          "description": "Zero-based component indexes from the current page root, exactly as returned by the most recent `read_page` call. Omit to delete the selected component.",
          "items": {
--- pinned/swebsy_duplicate_section
+++ observed/swebsy_duplicate_section
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Copy a component (with its classes and styles) and insert the copy right after it. Paths after it shift by one — call `read_page` before the next path-based edit.",
+   "description": "Copy a component (with its classes and styles) and insert the copy right after it. Paths after it shift by one — call `read_page` before the next path-based edit (ids don't shift).",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
+       "targetId": {
+         "description": "The component to copy by its id, as `read_page` shows it after `#` (e.g. \"ieyubib\", or \"call-2-2\" in a symbol instance). Stays valid when other components move or are added, unlike a path. Pass this OR the path, not both.",
+         "minLength": 1,
+         "type": "string"
+       },
        "targetPath": {
          "description": "Path (from the most recent `read_page`) of the component to copy.",
          "items": {
-     "required": [
-       "targetPath"
-     ],
      "type": "object"
    },
    "name": "swebsy_duplicate_section",
--- pinned/swebsy_edit_section
+++ observed/swebsy_edit_section
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Modify an existing component. By default this edits the selected component. To edit a component identified by `read_page`, provide its `targetPath`; an explicit path overrides the canvas selection. Preferred: provide `component` — the full replacement component tree (compact DSL OK), best for structural changes. Alternative: provide `html` — the complete modified HTML string. Fallback: provide `patch` — JSON Patch (RFC 6902) for tiny single-property tweaks. Supply exactly one. Preserve existing IDs: an id kept in the edit keeps the styles attached to it; an id you drop loses them.",
+   "description": "Modify an existing component. By default this edits the selected component. To edit a component identified by `read_page`, provide its `targetId` (preferred — survives other edits) or `targetPath`; an explicit target overrides the canvas selection. Preferred: provide `component` — the full replacement component tree (compact DSL OK), best for structural changes. Alternative: provide `html` — the complete modified HTML string. Fallback: provide `patch` — JSON Patch (RFC 6902) for tiny single-property tweaks. Supply exactly one. Preserve existing IDs: an id kept in the edit keeps the styles attached to it; an id you drop loses them. The result's `id` is the component's id after the edit (a replacing edit can give it a new one). `changed: false` means the page's HTML/CSS/JS came out identical, so the edit did not show up; `success: false` means a patched attribute is not what the page exports.",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
-         "description": "JSON Patch (RFC 6902) operations applied to the target component JSON. Only for tiny single-property tweaks.",
+         "description": "JSON Patch (RFC 6902) operations applied to the target component JSON. Only for tiny single-property tweaks. HTML attributes live under /attributes (an image's src is /attributes/src), classes at /classes, text at /content.",
          "items": {
            "additionalProperties": false,
            "properties": {
+       "targetId": {
+         "description": "The component to edit by its id, as `read_page` shows it after `#` (e.g. \"ieyubib\", or \"call-2-2\" in a symbol instance). Stays valid when other components move or are added, unlike a path. Pass this OR the path, not both.",
+         "minLength": 1,
+         "type": "string"
+       },
        "targetPath": {
          "description": "Zero-based component indexes from the current page root, exactly as returned by the most recent `read_page` call (for example [0] for the first top-level section or [0, 2] for its third child). Use this when the user names or relatively identifies a component instead of selecting it.",
          "items": {
--- pinned/swebsy_link_page
+++ observed/swebsy_link_page
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Point an existing link at a project page — the same wiring the UI's 'Link to' picker does. Pass the link's `targetPath` (from `read_page`) and the target `page` (id or name). For a navbar link this sets its page target so the href and active state stay in sync; for a plain link or button it sets href to the page's ./slug.html. Use after `create_page` to wire a navbar to the new page.",
+   "description": "Point an existing link at a project page — the same wiring the UI's 'Link to' picker does. Pass the link's `targetId` or `targetPath` (from `read_page`) and the target `page` (id or name). For a navbar link this sets its page target so the href and active state stay in sync; for a plain link or button it sets href to the page's ./slug.html. Use after `create_page` to wire a navbar to the new page.",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
+       "targetId": {
+         "description": "The link by its id, as `read_page` shows it after `#` (e.g. \"ieyubib\", or \"call-2-2\" in a symbol instance). Stays valid when other components move or are added, unlike a path. Pass this OR the path, not both.",
+         "minLength": 1,
+         "type": "string"
+       },
        "targetPath": {
          "description": "Zero-based component indexes from the current page root, exactly as returned by `read_page` (e.g. the nav link inside the navbar).",
          "items": {
-       "targetPath",
        "page"
      ],
      "type": "object"
--- pinned/swebsy_move_section
+++ observed/swebsy_move_section
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Move a component to just before or after another component on the current page — reorder sections, swap columns, lift a CTA above the footer. The component keeps its id, classes and id-styles. ALWAYS use this to reorder; delete + re-add loses the component's styles.",
+   "description": "Move a component to just before or after another component on the current page — reorder sections, swap columns, lift a CTA above the footer. The component keeps its id, classes and id-styles. ALWAYS use this to reorder; delete + re-add loses the component's styles. Name each side by id (`targetId`/`anchorId`) or by path (`targetPath`/`anchorPath`).",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
+       "anchorId": {
+         "description": "The component to place it next to by its id, as `read_page` shows it after `#` (e.g. \"ieyubib\", or \"call-2-2\" in a symbol instance). Stays valid when other components move or are added, unlike a path. Pass this OR the path, not both.",
+         "minLength": 1,
+         "type": "string"
+       },
        "anchorPath": {
          "description": "Path (from the same `read_page`) of the component to place it next to.",
          "items": {
+       "targetId": {
+         "description": "The component to move by its id, as `read_page` shows it after `#` (e.g. \"ieyubib\", or \"call-2-2\" in a symbol instance). Stays valid when other components move or are added, unlike a path. Pass this OR the path, not both.",
+         "minLength": 1,
+         "type": "string"
+       },
        "targetPath": {
          "description": "Path (from the most recent `read_page`) of the component to move.",
          "items": {
-     "required": [
-       "targetPath",
-       "anchorPath"
-     ],
      "type": "object"
    },
    "name": "swebsy_move_section",
--- pinned/swebsy_read_page
+++ observed/swebsy_read_page
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Read the current page structure and addressable component target paths. Call this BEFORE editing a named or relative page component that is not the current selection. The default `sections` read lists every top-level section with its target path — enough to pick the one to edit. When copying content between components, use `detail: full` WITH the `targetPath` of the one section you're copying, not a whole-page read. Also call it AFTER applying edits to verify the result.",
+   "description": "Read the current page structure: each line shows a component's target path and its `#id`. edit_section, delete_section, move_section, duplicate_section and link_page take either — prefer the id (`targetId`), which survives moves and inserts; other tools take the path. Use the id only to target; don't copy auto ids into markup. Call this BEFORE editing a named or relative page component that is not the current selection. The default `sections` read lists every top-level section with its target path — enough to pick the one to edit. To find a nested element (a link, button, card) pass that section's `targetPath` plus `depth` — one call instead of drilling down level by level. When copying content between components, use `detail: full` WITH the `targetPath` of the one section you're copying, not a whole-page read. Also call it AFTER applying edits to verify the result.",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
+       "depth": {
+         "description": "Optional. How many levels of the indexed tree to print below the section layer (default 1 = each section only, deeper nodes summarised as '(N descendants)'). Every printed line keeps its target path. Pair with `targetPath` to see a whole section in one call — e.g. `targetPath: [7], depth: 6` lists every link in the footer with its path for link_page/edit_section. Above 1 the read is capped at ~24 KB and ends with a '…(truncated: … next is [path])' marker if it doesn't fit; values above 12 are clamped.",
+         "maximum": 12,
+         "minimum": 1,
+         "type": "integer"
+       },
        "detail": {
-         "description": "'sections' (default): every section at the read root with its target path, an ~80-char text preview, and its descendant count — bounded and complete (~1-2 KB). 'outline': a deeper type/tag/class tree, character-capped. 'full': the complete cleaned JSON tree, for copying content between components (scope it with targetPath).",
+         "description": "'sections' (default): every section at the read root with its target path, an ~80-char text preview, and its descendant count — bounded and complete (~1-2 KB). 'outline': a deeper type/tag/class tree, character-capped (~2 KB; prefer `sections` + `depth`). 'full': the complete cleaned JSON tree, for copying content between components (scope it with targetPath).",
          "enum": [
            "sections",
            "outline",
--- pinned/swebsy_update_settings
+++ observed/swebsy_update_settings
  {
    "annotations": {
      "destructiveHint": false,
-             "description": "Raw CSS appended to the compiled stylesheet (canvas + export). Use for SITE-WIDE rules that utilities/tokens can't express — restyling a whole component type (e.g. make every `.btn` neobrutalist), custom box-shadows, border-width/radius. Colors MUST use theme vars (var(--color-neutral), var(--color-secondary)) so they stay dark-mode aware. This REPLACES the previous value — send the full CSS you want, not a fragment. This CSS is document-scoped: it does NOT travel into a harvested block, so it locks styled sections to this template. NEVER use it to build a repeated custom component (card, link block, tile) — make that a SYMBOL styled with theme classes so it stays harvestable. Reserve custom CSS for genuinely global polish only.",
+             "description": "Raw CSS appended to the compiled stylesheet (canvas + export). Use for SITE-WIDE rules that utilities/tokens can't express — restyling a whole component type (e.g. make every `.btn` neobrutalist), custom box-shadows, border-width/radius. Colors MUST use theme vars (var(--color-neutral), var(--color-secondary)) so they stay dark-mode aware. This REPLACES the previous value — send the full CSS you want, not a fragment. This CSS is document-scoped: it does NOT travel into a harvested block, so it locks styled sections to this template. NEVER use it to build a repeated custom component (card, link block, tile) — make that a SYMBOL styled with theme classes so it stays harvestable. Reserve custom CSS for genuinely global polish only. NEVER set type here (font-family, font-size, font-weight, font-style, letter-spacing, line-height, text-transform) on a class: that text's type disappears from the style panel. Fonts go in typography.fontFamily roles (a second display face in the free role, e.g. mono, applied per element with font-mono); size/weight/italic/tracking/case are utility classes on the element. Only bare-element defaults (body, h1-h6, p) inside @layer components may set type.",
              "type": "string"
            },
            "darkMode": {
--- pinned/swebsy_upload_asset
+++ observed/swebsy_upload_asset
  {
    "annotations": {
      "destructiveHint": false,
-   "description": "Upload a local image, font, video, audio or PDF file into the open site and return its `assetId`, `src` and a `usage` snippet. Images go in the asset library for `image` components, video for `video` components (`attributes.src`), audio for `audio`, PDFs as a link `href`; fonts are stored for `update_settings` fonts.custom. This is the ONLY way to get your own imagery (screenshots, logos, textures, mockups) into a site — an `image` with an empty src gets a random stock photo instead. Reads the file from the agent's own filesystem, so pass a path you can see (for example a screenshot you just captured). The asset is stored in the project and travels with static export and project JSON.",
+   "description": "Upload a local image, font, video, audio or PDF file into the open site and return its `assetId`, `src` and a `usage` snippet. Images go in the asset library for `image` components, video for `video` components (`attributes.src`), audio for `audio`, PDFs as a link `href`; fonts are stored for `update_settings` fonts.custom. This is the ONLY way to get your own imagery (screenshots, logos, textures, mockups) into a site — an `image` with an empty src gets a random stock photo instead. Reads the file from the agent's own filesystem, so pass a path you can see (for example a screenshot you just captured). The asset is stored in the project and travels with static export and project JSON. Pass `path` or `paths` (exactly one). Several files: pass `paths` (up to 25) instead of `path` for one call that returns a compact `{assets: {basename: src}, failed: {basename: reason}}` map.",
    "inputSchema": {
      "additionalProperties": false,
      "properties": {
-         "description": "Optional display name in the asset library. Defaults to the file's own basename.",
+         "description": "Optional display name in the asset library, single `path` only. Defaults to the file's own basename.",
          "type": "string"
        },
        "path": {
-         "description": "Absolute path to the file on the agent's machine. Images: .png, .jpg, .jpeg, .webp, .gif, .avif, .svg. Fonts (for `update_settings` fonts.custom, pass the returned assetId): .woff2, .woff, .ttf, .otf. Video: .mp4, .m4v, .webm, .mov, .ogv. Audio: .mp3, .m4a, .wav, .ogg. Documents: .pdf. Max 10 MB (the hosting per-file cap) — re-encode larger files first (for video: H.264 .mp4, 1080p or less, `-movflags +faststart`). Prefer .svg for logos and other flat vector art so it stays crisp at any size; uploaded SVG is sanitized (scripts and external references are stripped).",
+         "description": "Absolute path to the file on the agent's machine. Pass `path` or `paths` (exactly one). Images: .png, .jpg, .jpeg, .webp, .gif, .avif, .svg. Fonts (for `update_settings` fonts.custom, pass the returned assetId): .woff2, .woff, .ttf, .otf. Video: .mp4, .m4v, .webm, .mov, .ogv. Audio: .mp3, .m4a, .wav, .ogg. Documents: .pdf. Max 10 MB (the hosting per-file cap) — re-encode larger files first (for video: H.264 .mp4, 1080p or less, `-movflags +faststart`). Prefer .svg for logos and other flat vector art so it stays crisp at any size; uploaded SVG is sanitized (scripts and external references are stripped).",
          "minLength": 1,
          "type": "string"
+       },
+       "paths": {
+         "description": "Batch form: absolute paths of up to 25 files (same types and 10 MB cap as `path`). Pass `path` or `paths` (exactly one). Each file is uploaded on its own, so one bad file lands in `failed` without stopping the rest. Display names are the basenames; `filename` is not used.",
+         "items": {
+           "minLength": 1,
+           "type": "string"
+         },
+         "maxItems": 25,
+         "minItems": 1,
+         "type": "array"
        }
      },
-     "required": [
-       "path"
-     ],
      "type": "object"
    },
    "name": "swebsy_upload_asset",

Current tools

Show all 44 tool fingerprints
swebsy_add_section
74ee9dd330ecc293
swebsy_add_symbol_instance
eea8483e7df27668
swebsy_animate_page
0381c10010be8806
swebsy_capture
139f3215ba59c42a
swebsy_commit_design_direction
badf54d00934c8c8
swebsy_create_page
d1de94af870540eb
swebsy_create_site
7e4b1c6ab335d341
swebsy_delete_asset
48a31c1e18786cdc
swebsy_delete_page
c7c05543c045da60
swebsy_delete_section
ebb8550c18f608de
swebsy_delete_symbol
8ad32c0f2c444985
swebsy_detach_symbol
aac0f468e36a20dc
swebsy_duplicate_page
1bc45cf8766d7e4e
swebsy_duplicate_section
663a2c19369c7c2f
swebsy_edit_section
9e712ebfbe548135
swebsy_export
78364f183b12ac07
swebsy_get_builder_guide
6536967ef58fc815
swebsy_get_skill
a5a0037e590a4fce
swebsy_insert_block
62442c52fcc0ca7b
swebsy_link_page
1fe9e5e43cf0ad91
swebsy_list_assets
c806e9b6366b99f6
swebsy_list_blocks
c78e46cc60781878
swebsy_list_pages
3768803b1c59565f
swebsy_list_sites
757b1e6180817514
swebsy_list_skills
1e94627a212116ab
swebsy_list_symbols
480cc5d8b866b152
swebsy_list_templates
46a65c1e628e8b94
swebsy_move_section
e778e4806075161a
swebsy_open_site
02abfa27014ca8b9
swebsy_promote_to_symbol
c2d85d5e4568bce8
swebsy_read_page
9614cbfb9b8b43c3
swebsy_read_selection
fd8d0e6e973de5d0
swebsy_redo
416d1dd013e58bf0
swebsy_rename_site
593ccf87f638e3c4
swebsy_rename_symbol
1cc966caa542fd6d
swebsy_replace_page_content
78b84ee7cdc8053c
swebsy_select_page
b35bbc471eacf529
swebsy_start_pairing
3e5d3b04141e1868
swebsy_status
aa0cd5ec357948cc
swebsy_undo
e03d6da639eab50a
swebsy_update_page
337c8257eac8248e
swebsy_update_settings
3b4df4a6997537c4
swebsy_upload_asset
378feccd79fe163b
swebsy_wrap_section
1f18e3d7ee0dde3c

Watch this server yourself

If you run this server, put the proxy in front of it. It pins these exact fingerprints on first connect and stops the session if they move.

npx -y mcp-pin@0.2.3 -- <your @swebsy/mcp command>

Or subscribe to this page's RSS feed to be told when it changes.

Badge

mcp-pin status badge for @swebsy/mcp

The badge states one fact about time and nothing else. It never claims a server is safe.

[![mcp-pin](https://mcp-pin.gautamkhosla.com/badge/104320eec91b17b8.svg)](https://mcp-pin.gautamkhosla.com/servers/104320eec91b17b8.html)