Skip to main content
Glama
patrickdeanfox

Zuar Portal Blocks MCP Server

Zuar Portal — MCP Server

Let Claude operate your Zuar Portal (zPortal) for you — author HTML blocks, build pages, manage data sources, queries, themes and users, explore real data, and keep a git-versioned, revertible history of every change, all through natural language.

release MCP Zuar Portal node one-click install license


An MCP server that exposes a Zuar Portal's REST + auth APIs to any MCP client (Claude Desktop, Claude Code, …). It turns "build me a sales dashboard" into the right sequence of authenticated calls — discover data sources → write a saved query → author a validated HTML block → bind it → place it on a page — with bundled authoring guidance, layered write-safety, and a revertible history.

TIP

Install (Claude Code) — clone, build, register:

git clone https://github.com/zuarbase/cust-zuar-portal-mcp.git ~/zuar-portal-mcp
cd ~/zuar-portal-mcp && npm install && npm run build
claude mcp add zuar-portal --scope user -- node ~/zuar-portal-mcp/dist/index.js

Then cd to a project folder and run /portal-setup to connect it to a portal. Details ↓

No terminal (Claude Desktop): download zuar-portal-mcp.mcpb from the latest release and double-click it. Details ↓

At a glance

flowchart TB
    CD["<b>MCP Client</b><br/>Claude Desktop · Claude Code · any MCP client"]
    CD -- "JSON-RPC / stdio" --> S

    subgraph server["zuar-portal-mcp server"]
        direction TB
        S["index.ts → buildServer()"]
        S --> BT["🧱 <b>Block tools</b><br/>typed authoring + place_blocks"]
        S --> RT["📦 <b>Resource tools</b><br/>generic CRUD · 18 kinds (blocks included)"]
        S --> AT["⚡ <b>Action tools</b><br/>query · profile · users · config"]
        S --> VC["🕓 <b>Version-control tools</b><br/>snapshot · history · diff · restore"]
        S --> EL["🪄 <b>Setup &amp; design</b><br/>configure_project · synthesize_theme"]
        G{{"🛡️ <b>Safety &amp; integrity gates</b><br/>write-domain · structure · refs · impact · SQL"}}
        BT & RT & AT & VC & EL --> G
    end

    G --> HTTP["portalClient.ts<br/>login · X-Api-Key · retry · circuit breaker"]
    HTTP -- "/api + /auth · HTTPS" --> P[("Zuar Portal")]
    BT -. "mirrors every content write" .-> GIT[("git VC repo<br/>revertible")]

    classDef gate fill:#fde68a,stroke:#b45309,color:#000;
    class G gate

Every write is tagged with a risk domain and passes the safety gates before anything reaches the portal; every successful content write is mirrored to a git repo so it can be reverted.

Related MCP server: AutoWP MCP Server

Contents

Working on the server rather than with it? See CONTRIBUTING.md.

NOTE

📚 Full documentation lives in docs/ — a 5-minute quickstart, install & config, a generated reference for all 40 tools, block authoring, the design system, version control, the in-block zPortal API, the agent ecosystem & model routing, tool gating, safety gates, and troubleshooting.

Highlights

🧰 One uniform surface (v3.0.0)

One model for everything — blocks are a validated registry kind, one declarative place_blocks, uniform dry_run on every write, paged lists, and new reads (find_resource, get_references, vc_diff). Breaking; PORTAL_COMPAT_TOOLS=1 bridges the old v2 names.

⌨️ Install once, use everywhere

Clone + build + claude mcp add … -- node …/dist/index.js, then /portal-setup per project. Or a one-click .mcpb for Claude Desktop — no terminal at all.

🧱 Validated authoring

HTML blocks go through rule-checked tools (create_block/update_block/validate_block) — and create_resource (block) passes the same per-kind validator at the registry chokepoint, so footguns are caught before the portal is touched.

🏢 Multi-portal, multi-repo (v2.4.0)

One install drives a different portal + git repo per folder via ./.zuar-portal/config.json.

🪄 Browser setup, no JSON, no key in the model

configure_project serves a local loopback web form — you type the API key in your browser, so it never passes through the model; it validates live and writes a 0600 config. Falls back to elicitation / arguments. The design_intake prompt walks theming and drives synthesize_theme.

🤝 A team of agents

In Claude Code, a gated build → style → responsive → debug → adversary → advisor pipeline of specialist subagents builds blocks for you — each on a right-sized model.

🔒 Enterprise safety (v2.5–2.6)

Risk-domain write gating, least-privilege tool scoping, structural + referential integrity gates, pre-delete impact analysis, and an opt-in audit log.

🕓 Revertible history (v2.2.0)

Every content write mirrors to a git repo — revert any change with restore_resource.


What Claude can do with it

40 tools — one uniform surface ("a resource is a resource") across 11 capability groups. The full per-tool reference is generated from the live server (npm run gen:docs, so it can't drift): docs/03 · Tools Reference.

IMPORTANT

Upgrading from 2.x? v3.0.0 is a breaking redesign (48 → 37 tools). Every removed v2 name maps to a v3 primitive — see CHANGELOG.md. Set PORTAL_COMPAT_TOOLS=1 to temporarily register the old names as deprecated aliases that forward to the same gated v3 handlers.

🧱 Block tools — typed + validated

Blocks are now a first-class registry kind: list, fetch, and delete them with the generic resource tools (resource: "block"), and the block-authoring rules run as a per-kind validator at the registry write chokepoint — create_resource (block) is validated exactly like create_block. The typed fronts remain for ergonomic authoring:

Tool

What it does

validate_block

Run the authoring rules against a block payload without writing — iterate until clean.

create_block

Create an HTML block (validated against authoring rules).

update_block

Update an HTML block — merged over the current block so untouched fields survive.

File input [4.2.0]create_block, update_block, and validate_block take html_file and css_file. The server reads the bytes, so a model never carries them. Retyping a large block into a tool call is not a copy but a re-transcription: it burns tokens and silently normalizes characters (an em dash inside a regex character class arriving as a hyphen changes what the pattern matches — and passes review). Pass a path instead, edit the file in place, and only the delta is ever transcribed. Every gate runs identically either way. Reads are contained to the CWD, the VC dir, and any PORTAL_FILE_ROOTS entry. | bind_block_query | Bind a block to a datasource/query (auto-creates the query); sets ui_queries. | | place_blocks | One declarative placement primitive — add, update, hide, or remove blocks on a page grid in a single atomic write. mode: "merge" appends/updates (and honours remove: [...]); mode: "replace" + confirm: true makes the page exactly the given list while preserving survivors' customized grid.layouts and hidden flags. |

Pass resource plus a body/id. Call describe_resource to see each resource's fields, required-to-create fields, supported verbs, and risk domain.

Tool

What it does

describe_resource

List resources, or describe one (fields, verbs, domain).

list_resource

List records — always returns the paged envelope {total, offset, limit, returned, truncated, records} (default limit 100, max 500).

get_resource

Get one record by id — e.g. resource: "user", id: "me" for the current user's profile.

find_resource

Search by name (case-insensitive substring, or exact id) across kinds — optional kinds, tag, limit/offset; defaults to all non-admin kinds.

get_references

Read-only dependency query, both directions: dependents (who breaks if this is deleted — the same analysis the delete gate runs) and references (what this record points at, each flagged exists: false when dangling).

create_resource

Create a record (write-gated by domain; per-kind validators — blocks get the full authoring rules).

update_resource

Update a record (merged over current; write-gated).

delete_resource

Delete a record (write-gated; pre-delete impact analysis; confirm/force).

validate_portal

Read-only sweep for malformed records, dangling refs, and risky SQL.

Covered resources: block, layout (pages), datasource, query, db_modification, partial, theme, snippet, translation, dashboard, tag, user, group, permission, access_policy, api_key, credential, system.

Every write tool accepts dry_run: true — every gate runs (domain, structure, per-kind rules, references, impact), nothing is written, and the response carries applied: false plus what would have been written.

Tool

What it does

Domain

profile_datasource

Per-column stats (type, distinct values, min/max) plus raw sample rows (sample.columns / sample.rows; sample_rows default 10, max 50) to design filters + charts against real columns.

read

execute_query

Run a saved query by id and return results (optional row limit).

read

run_db_modification

Run a saved DB write by name. Needs confirm: true.

data

change_password

Change the current user's password.

admin

get_user_access

Read a user's group membership and permissions in one call.

read

set_user_access

Replace a user's groups and/or permissions — each provided list is a full replace; needs confirm: true, supports dry_run.

admin

get_config / update_config

Read / set portal config by path.

read / admin

get_version

Portal version + about (capability check).

read

get_rules

Show active block-authoring rules.

read

naming

The scope · kind · subject naming grammar — action: "suggest" proposes names, action: "parse" decomposes them.

read

check_connection

Start here — confirm the connection in one authenticated round-trip: portal, version, who you're signed in as, binding state and write posture, in ~160 chars. Bad credentials return the reason and the fix, not a false success (always available).

read

get_capabilities

Report the current posture — enabled/disabled tool groups, write-safety, VC + audit status, and the active config (portal / VC repo, secrets redacted) under its config key (always available).

read

get_metrics

Per-tool call count, error rate, latency, uptime, breaker state (always available).

read

configure_project

Connect this folder to a portal. By default it opens a local loopback setup page (API key typed in the browser, never through the model), validates live, writes a 0600 config; falls back to elicitation then arguments (pass ui:false, or portal_url + api_key + user_id). Also collects write-safety toggles + access scope + optional GitHub VC. Pins the folder to the portal so writes can't cross portals; writes ./.zuar-portal/config.json + design.md + a managed CLAUDE.md block.

setup

reload_config

Re-read config from disk (project/bundle/env) without a restart; resets the portal session.

setup

synthesize_theme

Pure theme synthesis — preferences (± an SSRF-guarded website color fetch) → a token map plus the exact create_resource call (create_with); creates nothing itself. Orchestrated by the design_intake prompt.

design

migration_preflight

Read-only migration audit — every block classified by function, all code-bearing fields scanned (comment/string-aware), placement via layouts/partials/snippets, hardcoded-origin sweep, bound-query preflight, browser-probe checklist.

migration

repair_query_metadata

Refresh a saved query's stored column metadata from a live execution — SQL untouched, dry-run by default, round-trip verified.

migration

The current user's profile is plain resource CRUD now: get_resource / update_resource with resource: "user", id: "me". Guided migration scoping is the migration_kickoff prompt (see Prompts below).

Tool

What it does

vc_status

Show whether VC is configured and the repo state.

snapshot_portal

Commit the full current portal state to the git repo — a durable checkpoint.

vc_log

Show the commit history of content changes.

vc_diff

Unified diff between two committed versions — record-scoped (resource + id) or repo-wide; defaults to the previous commit touching the record vs HEAD. Inspect before restore_resource — a revert is never blind.

restore_resource

Restore a resource to a previous committed version.

See docs/07 · Version Control.

Resources (zportal://guide/*) — authoring guidance Claude reads before building, so blocks follow zPortal conventions even on a fresh machine: block-structure, currentblock, zportal-api, charting, conventions, design-system, visual-verification, migration-1.18, loading-overlay, migration-playbook, and block-performance.

Prompts — guided workflows now live here rather than in the tool surface (9): zuar_portal_start (the cheapest session opener — confirm the connection in one call, report one line, ask what's next), zuar_portal_quickstart (orient → route), create_zportal_block (discover → build → create), setup_zuar_project (connect this folder, routes to configure_project), migrate_block_to_118 (one legacy block → the 1.18 lifecycle), add_loading_overlay (the sanctioned spinner + fade-out), block_perf_pass (the large-dataset perf audit: measure → trim SELECT * → chart-lib prefetch → 60 s honest timeouts), design_intake (guided theming — walks brand/website/density/radius, drives synthesize_theme, then creates the theme via create_resource), and migration_kickoff (guided migration scoping — batches the scope decisions, writes .zuar-portal/migration-scope.json, then runs migration_preflight).


The Claude Code agent ecosystem

When this repo is your Claude Code working directory, the MCP tools come with a team of specialists in .claude/. You don't drive create_block/bind_block_query by hand — you describe what you want, and a gated pipeline builds, styles, hardens, and reviews it. Full guide: docs/13 · Agents & Workflows.

The block pipeline

Blocks are never shipped raw. A spec flows through quality gates, each a focused subagent:

flowchart LR
    spec([spec]) --> B["🏗️ builder"] --> St["🎨 stylist"] --> R["📱 responsive"] --> D["🔧 debugger"]
    D --> A{"🚨 adversary<br/><b>CODE GATE</b>"}
    A -- "blocking (≤2 rounds)" --> D
    A -- "clean" --> V{"👁️ visual<br/><b>GATE</b>"}
    V -- "blocking (≤2 rounds)" --> D
    V -- "clean / skipped" --> Ad["🧭 advisor"] --> ship([ship ✅])

    classDef gate fill:#fde68a,stroke:#b45309,color:#000;
    classDef ro fill:#dbeafe,stroke:#1d4ed8,color:#000;
    class A,V gate
    class Ad ro

The adversary (gate) red-teams the block and proves each finding with evidence; while it returns blocking findings the pipeline loops back to the debugger. The visual gate (the adversary with browser eyes) then opens the rendered block in Claude for Chrome — screenshot, console, network — and a blank render, console error, sample-not-live data, or overflow loops back to the debugger too; it's best-effort and skips with a note when the extension isn't connected or the block isn't on a page. The advisor asks "is this the right block?" All gates are read-only — they carry no write tools and physically cannot mutate the portal (browsing/screenshotting is read-only). Beyond the six pipeline agents, four specialists handle broader jobs: portal-data-expert, portal-theme-designer, portal-bulk-operator (snapshot-first), and portal-onboarding.

Seeing the portal (Claude for Chrome)

Every gate above reasons about a block from its code and query rows — but a block can validate, bind, and still render blank, throw a runtime console error, overflow its grid cell, or silently show its hardcoded sample fallback instead of live data. With the Claude for Chrome extension connected, the agents can see the portal: open the page, screenshot the block, and read the browser console/network — for visual debugging and a final visual sign-off.

  • Recorded at setup. configure_project asks whether you use Claude for Chrome and stores browser.claudeInChrome in ./.zuar-portal/config.json; get_capabilities reports it.

  • Used where it pays. The debugger looks before it guesses; the adversary owns the visual gate; the stylist and responsive-specialist screenshot their work (the latter steps widths with resize_window); the advisor checks it reads at a glance.

  • Sign-in caveat. The MCP authenticates with an API key, but the browser needs a logged-in session — to view private pages you must be signed into your portal in Chrome. The MCP can't log you in.

  • Graceful by design. No extension, or the block isn't on a page? Every agent falls back to code-only review and says so. The doctrine lives in the zportal://guide/visual-verification resource.

Slash commands

Command

What it runs

/portal-setup

First-time per-folder setup + alignment Q&A → config + project brief.

/portal-build <spec>

The full build→style→responsive→debug→adversary→advisor pipeline for one block.

/portal-theme <goal>

Design or apply a portal-wide theme.

/portal-bulk <change>

A guarded bulk change across many blocks/pages (snapshot → dry-run → atomic apply).

/portal-audit [filter]

Read-only audit of existing blocks — bugs, a11y, responsiveness, design fit.

/portal-improve

One bounded improvement pass: score → fix worst blocks → verify → sweep → provable delta. Safe to schedule nightly.

/portal-align

Run the alignment Q&A on its own.

Model & effort routing

Each agent runs on the model and reasoning effort that fit its job — sharp where judgment matters, cheap where the work is mechanical. Three composing layers:

1 · Agent defaults (model:/effort: frontmatter) — for a direct call (a fast surgical edit, or one agent dispatched from a command):

Tier

Agents

Model · effort

🧠 Judgment / data

data-expert, adversary, advisor

opus · high

🛠️ Authoring

builder, stylist, debugger, bulk-operator, theme-designer, onboarding

sonnet · medium

Mechanical

responsive-specialist

haiku · low

2 · Workflow tier toggleportal-block-pipeline.js and portal-audit.js take args:{ …, tier } and set each stage's model/effort explicitly:

tier

For…

Builders

Judgment gates

fast

cheap iteration, throwaway drafts, triage

sonnet/haiku · low

sonnet · medium

standard (default)

a normal build / audit

sonnet · medium

opus · high

max

production / executive build, pre-release audit

opus · high

opus · xhigh

3 · Commands pin to sonnet · medium — they only orchestrate (pre-flight → dispatch → synthesize); quality lives in the agents/workflow they call. /portal-build and /portal-audit infer the tier from your phrasing.

The MCP server never selects a model — only the agents, commands, and workflows that drive it do. Re-tier via agent frontmatter or a workflow's ROUTING table; see .claude/README.md.


Guided onboarding & theming

By default configure_project serves a tiny loopback web form (http://127.0.0.1:<random-port>) from the MCP process, best-effort opens your browser, and returns the link immediately — you type the URL, API key, write-safety toggles, access scope, and version control in the browser, so the API key never passes through the model. On Save it validates live, writes the gitignored 0600 config, and applies the change live. It falls back to MCP elicitation (field-by-field prompts) and then to arguments when a browser can't be used — pass ui:false, or pass portal_url + api_key + user_id as arguments. Guided theming is the design_intake MCP prompt, which orchestrates the pure synthesize_theme tool. See The browser setup form.

flowchart TB
    subgraph setup["🔌 configure_project — connect a portal"]
        direction TB
        s1["Portal URL"] --> s2["API key 🔒"] --> s3["User ID"] --> s4{"add GitHub VC?<br/>(optional)"}
        s4 --> s4b{"use Claude for Chrome?<br/>👁️ visual checks"}
        s4b --> s5["✓ live portal login<br/>✓ GitHub token + repo (API)"] --> s6[["writes .zuar-portal/config.json<br/>+ .gitignore"]]
    end
    subgraph intake["🎨 design_intake prompt — theme the portal"]
        direction TB
        d1["brand + website"] --> d2["fetch site 🛡️ SSRF-guarded<br/>→ suggest brand colors"] --> d3["palette · density · radius"]
        d3 --> d4["header + sidebar style"] --> d5{"confirm?"} --> d6[["synthesize_theme →<br/>create_resource (theme)"]]
    end
  • configure_project refuses to clobber an existing config, validates with a real login, and writes a gitignored ./.zuar-portal/. It also asks whether you use Claude for Chrome (stored as browser.claudeInChrome) so the build pipeline can see your blocks render — visual debugging + a final visual gate (see Seeing the portal). The setup_zuar_project prompt and /portal-setup route to it; pass interactive: false for the direct, no-prompt path. (Replaces v2's setup_portal and init_project_config.)

  • The design_intake prompt fetches the brand's website through synthesize_theme's SSRF-guarded fetch to suggest a palette, then walks density/radius/header/sidebar and, on your confirmation, creates a theme resource via create_resource. synthesize_theme itself is pure — it returns the token map and the exact create_with call, and never writes.


Requirements

  • A Zuar Portal reachable over HTTPS, with an account that can manage blocks (admin recommended).

  • Node.js 18+ — for Claude Code and any other MCP client. (Claude Desktop's one-click .mcpb bundles its own Node, so you need nothing.)

Getting your portal credentials

You need three values, entered once during install.

#

Value

Where

1

Portal URL

The base URL, no trailing path — e.g. https://your-portal.zuarbase.net.

2

Portal API Key

Admin → Auth → API Keys → create/copy a key. It inherits its user's permissions — that user must be able to create/edit/delete blocks.

3

Portal User ID

Admin → Users → your user → copy the UUID from the page URL.

IMPORTANT

Keep the API Key and User ID private. In the Claude Desktop bundle they're declaredsensitive (masked, stored securely) and never leave the machine running the server.


Install — Claude Desktop (one-click)

  1. Download zuar-portal-mcp.mcpb from the latest release.

  2. Double-click it, or drag it onto the Claude Desktop window. An install dialog appears.

  3. Fill in Portal URL, Portal API Key, Portal User ID (and optionally the write-safety toggles).

  4. Confirm. The tools, resources, and prompts are now available to Claude.

To update later, install a newer .mcpb over the old one.

Install — Claude Code & other MCP clients

This server speaks MCP over stdio, so any MCP-capable client can use it. Clone it, build it once, and register the built entry point — you need Node ≥ 18 and git.

Register it once, for every project:

git clone https://github.com/zuarbase/cust-zuar-portal-mcp.git ~/zuar-portal-mcp
cd ~/zuar-portal-mcp
npm install
npm run build
claude mcp add zuar-portal --scope user -- node ~/zuar-portal-mcp/dist/index.js
IMPORTANT

Keep the clone where it is. claude mcp add records the absolute path to dist/index.js, so moving or deleting the folder breaks the server with a spawn ENOENT. Pick a permanent home for it — not /tmp, not ~/Downloads.

To update: pull and rebuild in place. The path doesn't change, so there's nothing to re-register — but restart your client, since the tool list is only fetched at handshake.

cd ~/zuar-portal-mcp && git pull && npm install && npm run build

Then, in each portal project folder, connect it to that portal:

mkdir ~/work/acme-portal && cd ~/work/acme-portal
claude
> /portal-setup

/portal-setup asks for your three values, checks them with a real login, and writes a gitignored ./.zuar-portal/config.json. Every folder can point at a different portal — see Per-project configuration ↓.

.mcp.json in the project (or claude_desktop_config.json) — point args at your clone's built entry point, as an absolute path (~ is not expanded here):

{
  "mcpServers": {
    "zuar-portal": {
      "command": "node",
      "args": ["/Users/you/zuar-portal-mcp/dist/index.js"]
    }
  }
}
IMPORTANT

Leave env empty. Don't put PORTAL_URL / PORTAL_API_KEY / PORTAL_USER_ID in your client config. Credentials in the client's env map create one global portal that every project silently inherits — so a folder you believe is pointed at staging quietly publishes to production. Let /portal-setup write credentials per project instead. Each project then carries a binding fingerprint, and the server refuses a write aimed at a portal the folder isn't bound to. Env vars still work (useful for CI, or a single-portal install), but the project file is the path that can't surprise you.


Per-project configuration (multiple portals)

One MCP install can drive a different portal — and a different git state-repo — in every folder. At startup the server resolves credentials in layers, highest priority first:

flowchart LR
    A["1 · Project config<br/><code>./.zuar-portal/config.json</code><br/>(walks up from cwd)"] --> R{{"resolved<br/>credentials"}}
    B["2 · Environment<br/><code>PORTAL_*</code> env vars<br/>(Desktop / MCPB)"] --> R
    C["3 · Bundle config<br/><code>config.json</code> beside bundle"] --> R

Most settings resolve per field, so a project file can set just vc.dir and inherit the rest. Empty values are ignored, so a blank Desktop field never shadows a project value.

WARNING

Portal credentials are the exception — they resolve all-or-nothing from ONE layer (v4.0.0). A layer naming any of url / apiKey / userId must supply all three, or startup fails with an explicit error. Per-field layering here was a cross-portal hazard: a project with a url but no apiKey silently borrowed PORTAL_API_KEY from the environment — one portal's address paired with another's key.

The file uses one schema for both the portal and its VC repo:

{
  "portal": { "url": "https://team-a.zuarbase.net", "apiKey": "…", "userId": "…" },
  "vc":     { "dir": "/path/to/team-a-state", "push": true,
              "remote_url": "https://github.com/you/team-a-portal-state.git", "token": "…" }
}

Set it up without hand-editing JSON: ask Claude to run configure_project (see Guided onboarding ↑). get_capabilities shows which portal/repo is in effect under its config key (secrets redacted). ./.zuar-portal/ is gitignored, so credentials are never committed.


Getting started

New portal? Start here. From the folder you want to work in:

cd ~/work/acme-portal
claude
> /portal-setup

One command. It connects the folder to the portal (writing gitignored credentials + a binding fingerprint), profiles your datasources, interviews you about the business, and writes a project brief the other agents read. Everything below assumes it's done.

Then just talk to Claude:

  1. Confirm the connection"List the datasources on my portal."list_resource (datasource).

  2. Look at real data"Show me a few sample rows from the Sales datasource."profile_datasource (per-column stats + raw sample rows, so Claude sees the real column names first).

  3. Create a block"Create a stat-card block 'Total Orders' showing the order count from Sales." → reads zportal://guide/*, builds the two-field block, create_block, reports the UUID.

  4. Iterate"Make the number bigger and use the portal's primary color." / "Turn it into a bar chart of orders by state."update_block.

TIP

InClaude Code, run /portal-build "a stat card of total orders from Sales" to push the spec through the whole gated pipeline, or invoke the create_zportal_block prompt for a structured discover → build → create flow.


Write safety & tool gating

Every write is tagged with a risk domain, gated independently:

Domain

Covers

Default

Enable with

content

blocks, layouts, partials, themes, queries, snippets, translations, dashboards, tags

on

(on unless read-only)

data

datasources, db_modifications, run_db_modification

off

PORTAL_ALLOW_DATA_WRITES=1

admin

users, groups, permissions, access policies, API keys, credentials, system, config, passwords

off

PORTAL_ALLOW_ADMIN_WRITES=1

  • PORTAL_READONLY=1 disables every write — reads and discovery still work.

  • A blocked write returns a clear message naming the flag to set; nothing reaches the portal.

  • run_db_modification additionally requires confirm: true on every call.

  • Deletes and user/password mutations are marked destructive to MCP clients.

  • Uniform dry_run: true (v3.0.0) on every write tool — all gates run (domain, structure, per-kind rules, references, impact) but nothing is written; the response reports applied: false and what would have changed. A dry run never bypasses a gate.

Least-privilege tool scoping (v2.5.0) — disable whole capability groups with PORTAL_DISABLE_TOOLS=users,config, or stand up a build-only allowlist with PORTAL_ENABLE_TOOLS=blocks,resources,data (deny wins).

Upgrading from 2.xPORTAL_COMPAT_TOOLS=1 (off by default) registers the removed v2 tool names as deprecated aliases in a compat group; each alias forwards to the same gated v3 handler, so aliases confer no extra capability.

Integrity gates (v2.5–2.6, server-side, cannot be bypassed) — every content write is checked for portal-compatible structure (a page missing grid.layouts is repaired or rejected) and dangling references; deletes run pre-delete impact analysis and refuse to orphan dependents unless force=true; user deletes refuse to remove the last admin; unscoped mass SQL (a destructive verb without a real WHERE — 1=1 doesn't count — plus MERGE/TRUNCATE/DROP/ALTER…DROP/GRANT) needs allow_unfiltered=true, and the check fails closed when the SQL can't be inspected. Run the read-only validate_portal anytime to sweep for problems. Full guide: docs/16 · Safety & Integrity.

Unattended-loop safety (unreleased) — the guarantees that make parallel/overnight agent loops safe to leave alone:

  • No duplicate creates: a create that fails ambiguously (network died after the request may have landed) is verified by name and adopted if it already exists — never blindly resent. run_db_modification is never auto-retried at all.

  • No lost updates: pass expected_updated_at (from your read) to update_resource / update_block / place_blocks and a mid-flight change by anyone else refuses with a conflict instead of silently overwriting.

  • No stranded cruft: name loop temporaries TMP · <purpose> (or tag them scratch), then sweep with cleanup_scratch — dry-run by default, deletes only unreferenced non-recent candidates through the gated path, content stays VC-restorable.

  • Provable improvement: score_portal scores every block/page 0–100 on the mechanically checkable and diffs against a saved baseline — a loop's regression is a per-record delta, not an anecdote.

  • Admin can't self-destruct: stripping your own admin access needs allow_self_lockout=true; update_config and change_password require confirm (config edits return previous_at_path for one-call revert).

In the Claude Desktop bundle these are install-dialog toggles; for other clients set them as env vars. Deeper dive: docs/14 · Tool Gating & Guidance.


Resilience, observability & hardening

Production-grade behaviour for a local, single-user server — safe defaults, no configuration required.

Resilience (the portal HTTP client every tool calls through):

Behaviour

Default

Tune with

Per-attempt timeout

30 s

PORTAL_TIMEOUT_MS

Retries on transient failure (network, 408/425/429/5xx), exp. backoff + jitter, honouring Retry-After

2

PORTAL_MAX_RETRIES, PORTAL_BACKOFF_BASE_MS, PORTAL_BACKOFF_MAX_MS

Circuit breaker — fail fast while the upstream is down

opens after 5 failures, 15 s cooldown

PORTAL_BREAKER_THRESHOLD, PORTAL_BREAKER_COOLDOWN_MS

Max request body size

5 MB

PORTAL_MAX_BODY_BYTES

Max tool input size (rejected at the MCP boundary)

2 MB

PORTAL_MAX_INPUT_BYTES

execute_query returned-row cap

1,000 rows (limit:0 = all)

per-call limit

list_resource page byte cap (auto-projects to {id,name} + note)

60k chars

PORTAL_LIST_BYTE_CAP (0 disables)

Result serialization

compact JSON

PORTAL_PRETTY_JSON=1 for pretty

Binding re-verification cadence

10 min + on config reload

PORTAL_BINDING_REVERIFY_MS

Read cache (GETs; any write clears it; write-critical reads bypass it)

5 s TTL

PORTAL_READ_CACHE_MS (0 disables)

Retry safety: GET retries on any transient signal; writes retry only on explicit 429/503 back-pressure or a network failure that provably never connected — never on an ambiguous 502/504, and a create whose network error may have delivered the request is verified by name and adopted rather than resent (no silent duplicates).

Observability — every call gets a request id, latency, and an error tally. get_metrics (always-on) reports per-tool counts, error rate, latency, uptime, and the breaker state — metadata only, no payloads or secrets. Set PORTAL_LOG_FORMAT=json for structured stderr logs; PORTAL_AUDIT_LOG appends metadata-only JSONL for every content/data/admin write.

Output secret redaction — secret-bearing fields (password, secret, token, api_key, …) are masked as [redacted] on resource reads, so they never flow into the model's context, and secrets are also caught by shape inside any value or name — connection-string passwords (postgresql://user:[redacted]@host), JWTs, PEM private keys, AWS key ids — including in execute_query / profile_datasource results. Generic hex and key=value shapes are deliberately not masked (uuids, git shas and SQL params are legitimate content that round-trips back into writes). Identifier *_id fields are never masked; create/update responses are intact (so a freshly generated secret can be seen once). Disable with PORTAL_REDACT_SECRETS=0. Portal-authored content additionally returns inside an untrusted-data envelope, so a poisoned record name reads as data, not instructions.


Troubleshooting

Symptom

Likely cause / fix

"failed to connect" / spawn ENOENT

The client couldn't spawn the command. Either (a) the clone moved or was deleted — claude mcp add stored an absolute path to dist/index.js; re-check it with claude mcp list; (b) you registered it before running npm run build, so dist/index.js doesn't exist — build, then restart the client; or (c) node isn't on the PATH your client sees (common with nvm + Claude Desktop, which doesn't load your shell profile — use the .mcpb there, it bundles its own runtime).

An old guide says to run npx -y zuar-portal-mcp-server

That package is not on npm — the command can't work and fails as ENOENT. Install from a clone: Install — Claude Code ↓.

Reads work, but every write is refused as unbound

This folder isn't bound to a portal. Run /portal-setup (or configure_project) in it. Binding is what stops one project's block being published to another's portal, so it's deliberate — see Per-project configuration.

"Missing portal credentials: …"

One of PORTAL_URL / PORTAL_API_KEY / PORTAL_USER_ID is blank. Re-enter it.

"…must supply all three of url/apiKey/userId"

A config layer names some credentials but not all. That's refused on purpose — a url without an apiKey used to silently borrow the key from your environment, pairing one portal's address with another's key. Supply all three in one place.

"Portal login failed: HTTP 401/403"

Wrong API key or user ID, or the user lacks permission. Regenerate the key; confirm the user can manage blocks.

list_resource (query) says the endpoint isn't available

Your portal predates the saved-queries API (1.18+). Use resource: "datasource" — expected, not an error.

Tools don't appear in Claude

Restart the client — the tool list is fetched once at handshake, so a newly added server (or a new version) won't show up in a running session. For the .mcpb, reinstall it.

You upgraded but the new tools/rules aren't there

Same cause: the client is still running the old process. Restart it.

A v2 tool name is missing (list_blocks, setup_portal, …)

v3.0.0 removed/renamed it — see CHANGELOG.md for the replacement, or set PORTAL_COMPAT_TOOLS=1 for deprecated forwarding aliases.

Want to see what it's doing

Set PORTAL_DEBUG=1 (or PORTAL_LOG_FORMAT=json). Logs go to stderr only.

"circuit breaker is open"

The upstream failed repeatedly; it auto-recovers after a short cooldown. get_metrics shows breaker state.

A stored secret returns [redacted]

Read redaction is on. Set PORTAL_REDACT_SECRETS=0 for that session.

More: docs/12 · Troubleshooting.


Security

  • Credentials are never logged. Debug output (gated by PORTAL_DEBUG=1) goes to stderr only, so it never corrupts the MCP stdio stream.

  • The API Key and User ID are declared sensitive in the bundle manifest.

  • The server talks only to the Portal URL you configure; the base URL is validated as a well-formed http(s) origin at startup. synthesize_theme's website fetch (driven by the design_intake prompt) is SSRF-guarded.

  • create_block / update_block are restricted to type: "html" and reject other types before any portal call.

  • Secret-bearing fields are redacted from reads; tool inputs and request bodies are size-capped.

See SECURITY.md for the full posture: the per-tool-group data-touch matrix, credential handling, network egress, and data retention.

License

MIT.

Available Tools

17 tools
change_passwordChange the current user's passwordA
Destructive

Change the authenticated user's password. Requires admin writes enabled (PORTAL_ALLOW_ADMIN_WRITES=1) and confirm=true — this rotates the credential the current session signs in with. Passwords are never logged or echoed back.

ParametersJSON Schema
NameRequiredDescriptionDefault
confirmNoMust be true — this rotates YOUR OWN login credential; the old password stops working immediately.
new_passwordYesNew password.
old_passwordYesCurrent password.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint=true and readOnlyHint=false, and the description adds valuable behavioral context: it rotates the current session's credential, requires admin writes, and notes that passwords are never logged or echoed. This goes well beyond the structured annotations and warns the agent about irreversible side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. The core action and prerequisites are front-loaded, and the security note about logging is a meaningful addition that earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the full parameter schema, destructive/read-only annotations, and the description's coverage of prerequisites, side effects, and security behavior, an agent has everything needed to invoke this tool correctly. No output schema exists, but the description does not need to explain return values for this mutation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents old_password, new_password, and confirm. The description reiterates that confirm=true is required and explains the credential rotation, but it does not add new parameter-level details beyond what the schema's confirm description already states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Change the authenticated user's password.' This clearly distinguishes it from sibling tools like update_config or set_user_access, which operate on different resources. The title reinforces the same precise scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit prerequisites: admin writes must be enabled (PORTAL_ALLOW_ADMIN_WRITES=1) and confirm must be true. It does not name alternatives, but no sibling tool appears to be a password-change alternative, so the context is sufficient for an agent to know when to invoke it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cleanup_scratchSweep scratch leftovers (loop hygiene)A
DestructiveIdempotent

Find records that scratch conventions mark as disposable — a name starting 'TMP ·', a 'scratch'/'tmp' tag, or '(delete me)' in the name; optionally '(diagnostic)' records and unnamed ones. DRY-RUN BY DEFAULT: reports every match, why it matched, and what (if anything) references it. confirm=true deletes only the safe candidates — unreferenced, not recently touched — through the same gated delete path as delete_resource. Content deletes are VC-recorded (restorable); db_modification deletes are NOT restorable and only ever match the explicit TMP/scratch markers.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindsNoKinds to sweep (default: query, block, db_modification).
confirmNotrue = delete the safe candidates. Omit for the dry-run report.
max_deleteNoCap deletions per run (default 50); the remainder is reported.
include_unnamedNoAlso flag null/empty-named records — content kinds only, never db_modifications. Default false.
older_than_minutesNoOnly records untouched this long are candidates — a running loop may still need newer ones (default 60; 0 disables).
include_diagnosticsNoAlso flag '(diagnostic)'-suffixed records (e.g. 'SYS · Block Inspect (diagnostic)'). Default false.

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and readOnlyHint=false, so the description doesn't need to restate that this can delete. However, it adds valuable context beyond annotations: the dry-run default, the distinction between VC-recorded (restorable) content deletes and non-restorable db_modification deletes, and the safety criteria (unreferenced, not recently touched). This is rich behavioral disclosure that goes beyond what annotations provide. The only minor gap is not explicitly stating that the tool is idempotent (though annotations declare idempotentHint=true), but the description's detail on safety and restorability is strong.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but well-structured. It front-loads the matching criteria and the dry-run default, then explains the deletion behavior and restorability. Every sentence adds meaningful information — no filler. The use of em-dashes and parentheticals packs a lot of detail without becoming unwieldy. It's appropriately sized for a tool with 6 parameters and complex safety semantics.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 6 parameters, no output schema, and destructive potential, the description is remarkably complete. It covers what matches, what gets deleted, safety criteria, restorability, and defaults. The only thing an agent might want is an example output format for the dry-run report, but the description already explains what the report contains (every match, why it matched, what references it). Given the complexity and the annotations covering safety, this is complete enough for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all 6 parameters. The description adds value by explaining the default behavior (kinds default to query/block/db_modification, max_delete default 50, older_than_minutes default 60) and the safety semantics (older_than_minutes protects running loops, include_unnamed never applies to db_modifications). This goes beyond the schema's basic descriptions, providing operational context that helps an agent choose correct parameter values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: finding and optionally deleting records that scratch conventions mark as disposable. It specifies the matching criteria (name starting 'TMP ·', 'scratch'/'tmp' tag, '(delete me)' in name) and distinguishes it from the general delete_resource sibling by emphasizing the dry-run default and the gated delete path. This is a specific verb+resource with clear scope.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly explains when to use this tool (for cleaning up scratch leftovers) and contrasts it with delete_resource, noting that deletions go through the same gated path. It also explains the dry-run default and the confirm=true flag for actual deletion, giving clear guidance on when to use the tool versus alternatives. The mention of 'loop hygiene' in the title further clarifies the intended use case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

describe_resourceDescribe a portal resourceA
Read-only

Show a resource's path, write fields, required-to-create fields, supported verbs, and risk domain. Omit resource to list every resource this server manages. Call this before create_resource/update_resource so the body matches the portal schema.

ParametersJSON Schema
NameRequiredDescriptionDefault
resourceNoResource type. Call describe_resource for fields and supported verbs.

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint=true already provided, the description adds context about optional parameter behavior (omitting resource lists all resources) and the nature of the returned data (schema fields, verbs, risk domain). While it doesn't detail error handling, it enriches the behavioral understanding beyond the annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, consisting of two sentences. The first sentence states the primary function, and the second provides an alternative usage and a recommendation. It is well-structured, front-loaded, and contains no fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simplicity of the tool and the absence of an output schema, the description is complete. It covers the main purpose, the optional parameter behavior, and provides a clear usage context. It does not leave significant gaps for an agent to infer.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool description adds crucial meaning to the 'resource' parameter by explaining that omitting it lists all resources, which is not mentioned in the schema description. This clarifies the optionality and the effect of not providing the parameter, making the tool's behavior fully understandable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function with a specific verb ('Show') and enumerates the information it provides (path, write fields, required-to-create fields, supported verbs, risk domain). It also distinguishes itself by naming a secondary mode (listing all resources) and explicitly positioning it as a precursor to create/update operations, setting it apart from sibling tools like get_resource or create_resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit usage guidance: 'Call this before create_resource/update_resource' and notes the behavior when omitting the resource parameter. This clearly tells the agent when to use this tool versus alternatives, providing both a typical workflow and an alternative use case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

execute_queryExecute a saved queryA
Read-only

Run a saved query by id and return its results. Pass params as a { name: value } map for parameterized queries. Rows RETURNED are capped at 1,000 by default (a SELECT * on a big table otherwise blows the context budget); the response notes when it truncated. Pass limit for a different cap, or limit:0 for ALL rows (know your table size first). Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax rows to return (default 1000, truncation is noted). 0 = unlimited — all rows.
paramsNoQuery parameters as a { name: value } map.
query_idYesSaved query UUID.

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already include readOnlyHint=true, and the description reinforces it. It adds genuinely useful behavioral context about a default 1,000-row cap, truncation notification, and the risk of unlimited queries. This goes beyond generic read-only labeling and explains important edge behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficient and well-structured: first the core action, then query parameters, then the critical row-limit behavior and caveat. Every sentence adds operational knowledge and the read-only note is prudently repeated despite the annotation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter read-only query executor with no output schema, the description covers what an agent needs to invoke it correctly: the identifier, parameters, limit behavior, truncation behavior, and safety profile. It does not describe the exact response structure, but that is minimized by the absence of an output schema and the simplicity of the resource.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already documents all 3 parameters with rich descriptions, so the baseline is 3. The description adds meaningful usage details above the schema: the default limit of 1,000, the effect of limit=0, and the shape of params as a name/value map. This justifies a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the exact action ('Run a saved query by id'), the resource type (saved query), and the outcome (return its results). Distinguished from sibling tools like run_db_modification and get_resource by emphasizing saved, read-only query execution.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear practical usage guidance: how to pass query parameters, the default row cap, why it exists, how to override it, and the warning about limit:0. It does not explicitly name alternatives or state when not to use this tool, but the context suggests this is the canonical read query executor.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_resourceFind resources by nameA
Read-only

Search records across resource kinds by name — "find the query named X" without listing whole collections client-side. Case-insensitive substring match on name/title, plus exact-id match. Defaults to every non-admin kind (block, layout, partial, theme, query, snippet, translation, dashboard, tag, datasource, db_modification); pass kinds to narrow or to include admin kinds. Optional tag filters to records carrying that tag.

ParametersJSON Schema
NameRequiredDescriptionDefault
tagNoOnly records carrying this tag.
kindsNoResource kinds to search (default: all non-admin kinds).
limitNoMax results (default 50, max 500).
queryYesName substring (case-insensitive) or an exact record id.
offsetNoResults to skip (pagination).

Output Schema

ParametersJSON Schema
NameRequiredDescription
queryYes
errorsYes
offsetYes
resultsYes
returnedYes
truncatedYes
total_matchesYes
kinds_searchedYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the description correctly implies no side effects. It adds behavioral details beyond the annotation: case-insensitive substring matching, exact-id support, default non-admin kinds, and the effect of `kinds` to include admin kinds. These are useful runtime behaviors not captured by structured metadata.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the core purpose, then packs matching behavior, defaults, and filtering into a logically structured sentence. It avoids redundancy with schema descriptions and uses compact phrasing. Slightly dense but not verbose; every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (multiple kinds, filters, pagination via offset/limit), the description covers key usage aspects: matching semantics, defaults, and filtering. The output schema exists, so return values don't need explanation. It lacks explicit mention of pagination behavior, but that is documented in the schema (offset/limit parameters). Satisfactory for a search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 100% description coverage, so the baseline is 3. The description adds value by explaining the default behavior of `kinds` (all non-admin kinds) and how to include admin kinds, which is not explicit in the schema. It also clarifies that `query` can be a substring or exact id. This enriches parameter meaning beyond the raw schema definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches records across resource kinds by name/title, with a specific verb ('Search records') and resource scope ('across resource kinds'). It distinguishes from siblings like get_resource (which fetches a specific resource) and list_resource (which lists whole collections) by explicitly mentioning the search capability and avoiding full client-side listing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides context on when to use it: for finding records by name without listing whole collections, which implies it's preferred over list_resource for name lookups. It also explains defaults and how to narrow with `kinds` and `tag`, but does not explicitly state when *not* to use it (e.g., when you need exact resource retrieval via get_resource). The guidance is implicit rather than explicit exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_configGet portal configA
Read-only

Fetch the portal's configuration document.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=true, covering the safety profile and external variability. The description adds no additional behavioral context beyond restating the fetch operation, but it does not contradict the annotations and the annotations carry the burden well.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single, front-loaded sentence with no filler. It states the action and the target resource economically, which is ideal for such a simple tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a parameterless read-only tool, the description is largely sufficient. With no output schema, it could briefly mention the return format or that it returns the full configuration document, but the phrase 'configuration document' already implies the core outcome.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4. The description does not need to explain parameter behavior because there are none to document.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Fetch') and a precise resource ('the portal's configuration document'), clearly distinguishing it from siblings like update_config and reload_config. Even without reading the schema, an agent knows exactly what this tool retrieves.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is for retrieving configuration, which is clear context. However, it does not explicitly mention that users should prefer this over get_capabilities or describe when not to use it. For a simple getter this is acceptable but not explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_rulesGet active authoring rulesA
Read-only

Return the active block-authoring rules: per-rule enforcement severities and the conventions text. Read this to see what create_block/update_block will enforce.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the read-only nature is covered. The description adds value by specifying the exact content (severities and conventions text) and relates it to the enforcement behavior of mutation siblings, beyond what annotations convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with zero redundancy. The first sentence states what is returned; the second directly tells the agent when to use it. All information is front-loaded and purposeful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple, parameterless read tool with readOnlyHint=true, the description fully covers what an agent needs to know: the tool returns the active rules and their enforcement context. No output schema exists, but the description outlines the return contents adequately.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, and the schema coverage is trivially 100% with no properties. Per the rubric, a score of 4 is the baseline for no-parameter tools; the description cannot add parameter meaning since none exist, but it does clarify the output content instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Return'), a clear resource ('active block-authoring rules'), and enumerates the content ('per-rule enforcement severities and the conventions text'). It further ties the tool to create_block/update_block enforcement, differentiating it from sibling config and access tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs to read this tool to understand what create_block/update_block will enforce, giving a concrete call-to-action. It does not mention when to avoid this tool or use alternatives, but the provided usage context is clear and sufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_user_accessGet a user's access (groups + permissions)A
Read-only

Return the groups a user belongs to and the permissions granted to them, in one call. The user record itself is get_resource resource="user" (id "me" = the authenticated user).

ParametersJSON Schema
NameRequiredDescriptionDefault
user_idYesUser UUID.

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide readOnlyHint=true and openWorldHint=true, so the description is not expected to repeat safety information. The description adds that this is a combined lookup, but does not disclose error behavior, whether permissions are direct or effective, or any special values the tool accepts. This is acceptable but not particularly rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loaded with the main purpose. The second sentence provides useful routing context but is slightly ambiguous because the parenthetical about 'me' could be read as applying to get_user_access when it likely applies to get_resource. Overall it is efficient with minimal waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter read tool, the description gives a reasonable high-level summary of the return content (groups and permissions) and points to the correct sibling for raw user records. However, with no output schema, it does not specify the shape, whether permissions are resolved/inherited, or behavior for invalid user IDs, leaving some gaps for an agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema fully describes user_id as 'User UUID' with 100% coverage, so the baseline is 3. The description does not add significant parameter-level detail; the mention of 'me' as the authenticated user appears to refer to get_resource, not necessarily to this tool's user_id parameter. Thus the description adds no meaningful semantic value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns a user's groups and permissions in one call, with a specific verb ('Return') and resource ('a user's access'). It distinguishes itself from simple user-record retrieval by mentioning get_resource for the user record itself, making the tool's specific purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use this tool: when you need both groups and permissions together. It also points to get_resource for the raw user record, which is a helpful alternative. However, it does not explicitly state exclusions or when not to use this tool, leaving some inference required.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_versionGet portal version / capabilitiesA
Read-only

Fetch the portal version and about info — use it to gate version-specific endpoints (e.g. saved queries are 1.18+). To confirm the connection, call check_connection instead.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the description only needs to add behavioral context. It does so by noting the tool returns version and about info and is meant for gating. It doesn't contradict annotations and provides useful purpose beyond what annotations state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the primary purpose. The second sentence adds necessary usage guidance. No redundancy or fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is sufficient for a zero-parameter, read-only info tool. It explains the use case (gating) and distinguishes from check_connection. It could mention what 'about info' contains, but that's minor. Overall, it's complete enough.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so the schema is trivially 100% covered. The description adds no param semantics but none are needed. Baseline for zero-parameter tools is 4; it doesn't lose points.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Fetch the portal version and about info') and clearly distinguishes it from check_connection by noting it gates version-specific endpoints. The resource is unambiguous and the tool's role is clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use (gate version-specific endpoints) and when not to (to confirm connection, call check_connection instead). This gives the agent clear decision criteria and an alternative tool name.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_resourceList a resource collectionA
Read-only

List records of a resource (block, datasource, layout, query, theme, partial, user, group, db_modification, etc.). Use this for discovery — e.g. find a datasource UUID before authoring a block. Always returns the paged envelope { total, offset, limit, returned, truncated, records } (default page size 100, max 500) — check truncated and walk offset for the rest. Optional query adds URL query params. First pages (offset 0) fetch SERVER-SIDE when the portal honors ?limit — total is then null while truncated=true (walk until truncated=false; the portal didn't send a count). Pages are also BYTE-capped (PORTAL_LIST_BYTE_CAP, default 60k chars): an oversized page auto-projects to { id, name } and says so in note — prefer only_names/fields for discovery and get_resource (summary/fields) for detail.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoMax records to return (default 100, max 500; paginates client-side).
queryNoOptional query-string params passed to the portal, e.g. { only_names: true }.
fieldsNoProject each record to these top-level fields (+ id) — e.g. ['name','updated_at'] to grab concurrency tokens cheaply. Takes precedence over only_names.
offsetNoRecords to skip (pagination).
resourceYesResource type. Call describe_resource for fields and supported verbs.
only_namesNoProject each record to just { id, name } — cheap discovery, works regardless of portal support.

Output Schema

ParametersJSON Schema
NameRequiredDescription
noteNo
limitYes
totalYesnull = unknown (server-side page; the portal sent no count)
offsetYes
recordsYes
resourceYes
returnedYes
truncatedYes
auto_projectedNo

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations carry only readOnlyHint and openWorldHint; the description carries the real burden and nails it, disclosing non-obvious runtime behavior the annotations can't: nullable `total` when first pages fetch server-side, the walk-until-truncated=false protocol, byte-capping at PORTAL_LIST_BYTE_CAP with silent auto-projection to { id, name } plus a `note` field. These are exactly the pagination and envelope edge cases that would cause silent agent bugs, and they go well beyond what readOnlyHint/openWorldHint convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but purpose-built: a single paragraph that moves logically from what → when → return contract → edge cases → routing, with the most important fact (purpose + example) front-loaded. The one blemish is that the sentence 'Optional `query` adds URL query params' largely restates the schema, and the wall of nested parentheses is heavy on the eyes. For a tool this complex (6 params, byte caps, portal variability), the density is earned.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity — open-world portals, client- vs server-side pagination, byte caps, an 18-value enum, and a self-described output envelope — the description covers every call-relevant dimension: the exact envelope { total, offset, limit, returned, truncated, records }, how to iterate, what total:null means, how to detect truncation, and when to route elsewhere. No return-format gap exists because the description dually serves as de facto output documentation (the envelope is described inline even though an output schema is flagged). An agent could implement correct pagination and projection entirely from this text.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description clears that bar by adding behavioral consequences tied to parameters that the schema doesn't state: when to prefer only_names/fields for discovery, the interplay between byte caps and field projection, and the fact that `query` lets the portal (not just the client) do the work. It stops short of adding a 5 because the schema already carries its weight with examples like { only_names: true } and ['name','updated_at']; the description refines rather than rescues the parameter docs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific transitive verb plus explicit resource scope ('List records of a resource (block, datasource, layout, ...)') with the 18-value enum in the schema to back it up. It differentiates from siblings by naming its role — 'Use this for discovery — e.g. find a datasource UUID before authoring a block' — and explicitly distinguishes itself from get_resource ('...for detail') and describe_resource ('Call describe_resource for fields and supported verbs'). An agent can select this tool for listing/discovery and route detail work elsewhere.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use ('Use this for discovery') with a concrete workflow example, and names the alternative with the condition that selects it: 'prefer only_names/fields for discovery and get_resource (summary/fields) for detail.' It also documents how to fully consume the result set ('walk offset for the rest'), so the agent knows the shape of the interaction, not just when to fire it. This is explicit routing guidance, not implicit inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

namingNaming convention (suggest / parse)A
Read-only

The SCOPE · Kind Subject naming convention as a pure function. action=suggest generates a conforming display name, stable slug and facet tags from parts (kind required; scope accepts a code or facet tag: HC=healthcare, FIN=financial, SC=supply-chain, RT=retail, IOT=iot, CRM=crm, MKT=marketing, EXEC=executive, SYS=system, DW=data-warehouse, TMP=scratch; kind is one of: kpi, chart, table, filter, hero, navigation, map, text, page, partial, query, datasource, theme — resource kinds like datasource/query/page omit the kind word from the display name). action=parse decomposes an existing display name (name required) and grades conformance. Prefer suggest over hand-naming so names — and the slugs/tags derived from them — stay consistent.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNosuggest (required): content kind — kpi, chart, table, filter, hero, navigation, map, text, page, query, datasource, theme…
nameNoparse: the display name to decompose, e.g. 'HC · KPI Band'.
scopeNosuggest: scope code (e.g. HC, DW) or facet tag (e.g. healthcare).
actionYessuggest = build a name from parts; parse = decompose/grade a name.
sourceNosuggest: data-asset source facet for datasources/queries — sample, live, telemetry, curated, or reference. Adds a '— <Source>' marker + tag; omit to leave a name unmarked (the 'live' default).
subjectNosuggest: human subject phrase, e.g. 'Revenue by Department'.
qualifierNosuggest: optional variant/grain, e.g. 'YTD' or 'by-region'.

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description calls the tool a pure function, which adds determinism/no-side-effect context beyond the readOnlyHint annotation. It also discloses useful behaviors: stable slugs, facet-tag generation, resource kinds omitting the kind word, source markers with a 'live' default, and conformance grading. No contradiction with the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but front-loaded with the core convention and both actions. Every sentence earns its place, and the long enumerations are necessary because the schema does not provide them.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-mode tool with seven parameters and no output schema, the description provides enough to call it correctly: required fields per action, all allowed vocabularies, source default behavior, and what each mode returns at a conceptual level. The exact parse grading rubric is not needed for selection or invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers parameter names, but the description supplies the full controlled vocabularies it omits: scope code mappings, the complete kind list, source facets, and the conditional omission rule for resource kinds. It also clarifies requiredness per action, which goes meaningfully beyond the input schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the exact function: the `SCOPE · Kind Subject` naming convention as a pure function, with suggest generating a display name, slug, and tags, and parse decomposing and grading a name. It clearly distinguishes the two modes and is unlike any of the resource/config sibling tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says to prefer suggest over hand-naming and explains why: consistency of derived slugs and tags. It also makes the parse mode's purpose obvious for existing display names, so an agent can select the right action without guessing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

profile_datasourceProfile a datasource (stats + sample rows)A
Read-only

Sample a datasource and return per-column statistics — inferred type, non-null/empty counts, distinct value count, sample distinct values for categoricals, and min/max for numerics — PLUS raw sample rows (sample.columns + sample.rows) so you see real column names and literal values in the same call. Exactly what you need to design filters, choose chart dimensions, pick aggregations, and match a block's column constants to reality. Profiles over a sample (default 500 rows); sample_rows caps the raw rows returned (default 10, 0 for stats only).

ParametersJSON Schema
NameRequiredDescriptionDefault
sample_rowsNoRaw rows to return alongside the stats (default 10, max 50, 0 = none).
sample_sizeNoRows to sample for the profile (default 500, max 5000).
distinct_capNoStop counting distinct values past this many per column (default 100).
datasource_idYesDatasource UUID.

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint: true and openWorldHint: true, so the description does not need to reiterate side effects. The description itself is transparent about the operation: it samples and returns statistics and rows, implying no modifications. There is no contradiction between the description and annotations, and the description adds detail about the read-only nature by using the verb 'sample'.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is verbose and contains redundant phrasing. For example, it repeats 'so you see real column names and literal values in the same call' and then again 'match a block's column constants to reality.' The em-dash style and the extra explanatory sentence ('Exactly what you need...') could be condensed. While not overly long, it lacks conciseness and could be streamlined without losing meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description thoroughly explains what the tool returns (per-column stats and sample rows) and its purpose. However, it does not mention error scenarios, limits beyond parameter defaults, or how the results are formatted. Since there is no output schema, the description partially compensates by describing the output structure, but it could be more comprehensive regarding edge cases (e.g., empty datasource, large datasets). Overall, it is sufficient for a typical agent interaction.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

All four parameters are fully described in the schema with clear meanings: datasource_id, sample_size, sample_rows, and distinct_cap each have detailed descriptions. The schema coverage is 100%, and the descriptions provide sufficient context (e.g., 'Stop counting distinct values past this many per column'). No additional clarification is needed in the tool description beyond what is already provided.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: sampling a datasource and returning per-column statistics plus raw sample rows. It explicitly lists what is returned (inferred type, non-null/empty counts, distinct value count, sample values, min/max) and explains the benefit ('so you see real column names and literal values'). This is specific and unambiguous, distinguishing it from generic resource inspection tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description includes a clear use case: 'Exactly what you need to design filters, choose chart dimensions, pick aggregations, and match a block's column constants to reality.' This indicates when to use the tool, though it does not explicitly contrast with sibling tools like describe_resource or list_resource. It implies a data profiling context, which is sufficient guidance for an agent to select it appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

run_db_modificationRun a database modificationA
Destructive

Execute a saved db_modification (INSERT/UPDATE/DELETE) by name. This WRITES to a database — and database writes have NO snapshot/rollback, unlike portal content. Requires data writes enabled (PORTAL_ALLOW_DATA_WRITES=1) and confirm=true. Refuses an unscoped mass write (UPDATE/DELETE/MERGE without a real WHERE — 1=1 doesn't count — TRUNCATE, DROP, ALTER…DROP, GRANT/REVOKE) unless allow_unfiltered=true; the check is a best-effort classifier and fails closed if the SQL can't be inspected. Pass params as a { name: value } map, or params_list for bulk rows.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesdb_modification name to run.
paramsNoSingle-row parameters as a { name: value } map.
confirmYesMust be true to actually run — guards against accidental DB writes.
autocommitNoCommit each statement (default portal behavior).
params_listNoBulk parameters: a list of { name: value } maps, one per row.
allow_unfilteredNoMust be true to run an UNSCOPED mass write (UPDATE/DELETE without WHERE, TRUNCATE, DROP). Default false.
ignore_sql_errorsNoContinue past SQL errors.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description extensively discloses behavioral traits beyond annotations: no snapshot/rollback for database writes, the need for confirm, the guard against unscoped writes, and the best-effort classifier that fails closed. This aligns with annotations (destructiveHint=true, readOnlyHint=false) and adds crucial risk context an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact but dense, with the primary action stated first, followed by critical constraints and parameter usage. Every sentence contributes meaningful information (prerequisites, safety checks, parameter formats). It's not bloated, but slightly longer than necessary; still excellent.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (7 params, safety guards, no output schema), the description covers all essential aspects: the operation, prerequisites, guard behavior, parameter passing, and failure modes. An agent has enough information to invoke it correctly and understand risks without further reference.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with descriptions for all params. The description adds value by explaining the semantics: params is a { name: value } map, params_list is for bulk rows, confirm guards against accidental writes, and allow_unfiltered is required for mass writes. This enriches the structured schema details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb (execute) and resource (saved db_modification), and clarifies it's for INSERT/UPDATE/DELETE operations. It clearly distinguishes from siblings like execute_query (which likely reads) by emphasizing it writes to a database, and the 'by name' scoping differentiates from other modification tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit usage conditions are given: requires data writes enabled (PORTAL_ALLOW_DATA_WRITES=1), confirm=true, and describes when it refuses to run (unscoped mass writes without allow_unfiltered=true). While it doesn't name alternative tools, it provides clear prerequisites and boundary conditions, making it good but not perfect.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

score_portalScore portal quality (blocks + pages, with baseline delta)A
Idempotent

Score every block and page 0–100 on what is MECHANICALLY checkable — authoring-rule violations, dangling query bindings, orphaned blocks, naming-convention fit, SELECT * bindings, oversized HTML, unscoped CSS, page overflow and dangling placements. 100 means 'nothing mechanically wrong', NOT 'well designed' — visual/business judgment stays with review agents. Scratch-marked records are excluded (cleanup_scratch handles them). With version control on, compares against the last saved baseline (improved/regressed/added/removed per record) — the loop discipline: score, improve, re-score, and only update_baseline=true after a verified improvement.

ParametersJSON Schema
NameRequiredDescriptionDefault
detail_belowNoFull deduction detail only for scores below this (default 90); higher scores report one line.
update_baselineNoSave these scores as the new baseline in the VC mirror (requires PORTAL_VC_DIR).

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations (readOnlyHint=false, idempotentHint=true, destructiveHint=false), the description reveals that scratch-marked records are excluded, that it compares against a baseline when version control is on, and that 100 has a specific meaning. These are meaningful behavioral traits not covered by annotations. It does not mention potential side effects beyond baseline updates, but the annotations reduce the burden.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense paragraph that packs essential information without fluff. Every sentence contributes: scope, exclusions, baseline behavior, and the meaning of 100. It is slightly long but well-organized, with the core purpose front-loaded. The structure is functional, though it could be broken into list form for easier scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given there is no output schema, the description implicitly states what the tool returns (scores and baseline deltas). It covers key prerequisites like version control on for baseline, and mentions the detail_below behavior. It doesn't explicitly state the output format or error handling, but for a scoring tool with simple boolean and integer parameters, the information is largely sufficient to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already documents both parameters fully (coverage 100%), so the baseline is 3. The description adds extra meaning for update_baseline by explaining the loop discipline and when to set it to true, which goes beyond the schema's simple 'save as baseline' note. However, it duplicates the detail_below description from the schema, so the net addition is moderate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: 'Score every block and page 0–100 on what is MECHANICALLY checkable' with a specific list of checks. It distinguishes itself from siblings by excluding scratch-marked records and referencing cleanup_scratch, and clarifies that 100 does not mean 'well designed' – a key scope distinction.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides context on when to use the tool (for mechanical scoring) and when not to (scratch records go to cleanup_scratch). It also explains the loop discipline 'score, improve, re-score, and only update_baseline=true after a verified improvement', which guides the agent on when to set the baseline parameter. However, it does not explicitly compare with validate_portal or other similar validation tools, so alternatives are only partially addressed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

snapshot_portalSnapshot portal content to gitA

Export every content resource (blocks + layouts/queries/themes/partials/snippets/translations/dashboards/tags) AND datasources to the version-control repo and commit. Run once to seed history, or anytime to capture a checkpoint. Verifies COVERAGE: staged counts are checked against live listings and any resource kind that failed to export is reported (a rollback snapshot that silently skipped saved queries is not a rollback). Requires PORTAL_VC_DIR.

ParametersJSON Schema
NameRequiredDescriptionDefault
messageNoCommit message (default: 'snapshot').

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds material behavioral detail beyond the annotations: the coverage verification ('staged counts are checked against live listings'), the rollback caveat, and the prerequisite 'Requires PORTAL_VC_DIR.' It aligns with readOnlyHint=false and idempotentHint=false, and does not contradict any annotation. It goes beyond the basics expected for a mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but efficient, front-loading the core action ('Export every content resource... AND commit') before adding usage timing and behavioral caveats. Every sentence carries distinct value, though it could be broken into clearer sentences for readability. It is not verbose enough to warrant a 3, but not as crisp as the two-sentence example for a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex export/commit tool with multiple resource kinds and a coverage check, the description covers the essential facts: what is exported, when to use it, the verification mechanism, and the required environment variable. It doesn't describe return values, but there's no output schema. The missing 'message' parameter usage is covered by the schema, so the description is sufficiently complete for an agent to decide when and how to call it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% for the single optional 'message' parameter, which the schema already documents ('Commit message (default: 'snapshot')'). The description does not add any new meaning for this parameter, so the baseline of 3 is correct. The mention of PORTAL_VC_DIR is an environment variable, not a parameter, and doesn't enhance parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Export'), resource scope ('every content resource... AND datasources'), and destination ('version-control repo and commit'). It clearly distinguishes itself from sibling tools like vc_status or vc_diff by describing a distinct snapshotting action, leaving no ambiguity about what this tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides explicit timing guidance ('Run once to seed history, or anytime to capture a checkpoint'). While it doesn't name alternatives or state when not to use it, the context is clear enough that an agent can choose this tool for snapshot creation without confusion. A 5 requires explicit when-not/alternatives, which is absent, so 4 is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_blockValidate a block (no write)A
Read-only

Run the same authoring rules as create_block/update_block against a block payload WITHOUT writing it. Use it to iterate on HTML/JS/CSS until it's clean — it flags the footguns that only surface in a live browser (literal $ String.replace mangling, {{ }} interpolation, data polling, unscoped CSS, full docs, unsafe JS). Returns { valid, errors, warnings }. Pass against_block_id to preview GRANDFATHERING: errors the stored block already has are reported as pre_existing (update_block flags them as warnings instead of blocking).

ParametersJSON Schema
NameRequiredDescriptionDefault
cssNoCSS section.
dataNoHTML/JS section (the block `data`).
nameNoBlock name (optional for validation).
css_fileNoPath to a file holding the block's CSS; the server reads it into `css`. Same byte-exact guarantee and same root containment as html_file. Mutually exclusive with `css`.
html_fileNoPath to a file holding the block's HTML+JS; the server reads it into json_data.html. PREFER THIS over inlining large content: the bytes are read off disk byte-exact, so nothing is retyped and characters like em dashes or box-drawing cannot silently normalize. Mutually exclusive with json_data.html. Must resolve under the CWD, the VC dir, or a PORTAL_FILE_ROOTS entry.
json_dataNoWidget/extra config.
ui_queriesNoQuery/datasource binding (this is how a block gets data — the portal has no `data` field). Array of { enabled, page_size, query_id, filter_strategy }, e.g. [{ "enabled": true, "page_size": 50, "query_id": "<query-uuid>", "filter_strategy": { "type": "blacklist", "value": [] } }]. The query_id must reference a saved query that already has a datasource attached. On update_block, the existing ui_queries is preserved automatically unless you pass a new value.
against_block_idNoExisting block UUID to compare against — splits errors into new vs pre-existing (grandfathered on update_block).

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, but the description goes far beyond by detailing that it runs the same authoring rules without writing, returns a structured { valid, errors, warnings } object, and previews grandfathering behavior. It also enumerates specific footguns it detects, which is exactly the kind of behavior that annotations alone don't convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but every sentence earns its place: it front-loads the core purpose and then layers on behaviors like returned shape, grandfathering, and file-preference guidance. Formatting with em dashes and line breaks keeps it readable despite the length, and no information is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a validation tool with no output schema, the description covers return shape, side-effect-free guarantee, the difference between validation and update behavior, and even hints at security/correctness concerns. It also guides on resource loading (file paths and root containment). The focus on this tool's specific role in the portal authoring flow makes it complete for an agent to use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds significant value beyond the schema: it explains trade-offs (e.g., html_file 'byte-exact' and 'PREFER THIS over inlining'), clarifies relationships (html_file vs json_data.html), and provides a detailed example for ui_queries with notes on update semantics. This goes well beyond restating the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb-resource pair ('validate a block payload') and immediately distinguishes it from siblings by referencing 'the same authoring rules as create_block/update_block'. It clarifies the key non-write behavior and names the exact sibling operations, making the tool's role unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly tells when to use this tool ('Use it to iterate on HTML/JS/CSS until it's clean') and explains the comparison to update_block via against_block_id, including how it interacts with grandfathering. It sets expectations about what will be flagged and contrasts with update_block's warning behavior, giving clear guidance on tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_portalValidate portal integrity (read-only)A
Read-only

Sweep every page (layout), partial, theme, query, block, db_modification and system record and report anything that would break or render wrong: structurally malformed records (e.g. a page missing grid.layouts — the 'all pages vanished' bug), dangling references (a page/block pointing at a deleted block/query/datasource), unscoped mass-write SQL (UPDATE/DELETE with no WHERE, TRUNCATE, DROP), and datasource hygiene (a name that leaks a connection-string/password, or a datasource reporting a broken connection). Fixes nothing — run it after bulk changes or on a schedule to catch latent breakage before users do.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoCap the number of issues returned (still reports the full counts). Omit for all.

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses the full scope of the sweep (all listed resource types), the categories of issues (structural malformations, dangling references, unscoped mass-write SQL, datasource hygiene), and the 'Fixes nothing' behavior. It also mentions that with the limit param, 'still reports the full counts' (though not in the description text, the behavior is implied by the schema's description). This aligns perfectly with the readOnlyHint annotation and adds substantial context beyond it.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense paragraph that front-loads the action and then enumerates checks. It is information-rich without rambling, but slightly long. Every clause adds value — no filler. It's concise enough given the complexity of the tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the resource types, the issue categories, and the non-destructive nature. It implies a report of issues and mentions that counts are still reported even when limited. There is no output schema, so the description should clarify return format; while it doesn't specify the exact structure, it's clear enough for an agent to expect a list of findings. The breadth of checks is well covered.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, limit, is fully documented in the schema with a clear description ('Cap the number of issues returned (still reports the full counts). Omit for all'). The tool description adds no additional semantic detail beyond what the schema already provides. Since schema coverage is 100%, baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Sweep every page (layout), partial, theme, query, block, db_modification and system record' — clearly a validation sweep. It enumerates the types of issues it catches, distinguishing it from siblings like validate_block (which is narrower) and repair_query_metadata (which fixes). The purpose is unambiguous and non-tautological.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance is given: 'run it after bulk changes or on a schedule to catch latent breakage before users do.' It also states 'Fixes nothing,' implying it's for detection rather than remediation. It doesn't explicitly exclude scenarios or name alternative tools, but the context is clear enough for an agent to decide when to invoke it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 28 tool updatesv4.3.0
    • Removedbind_block_query
    • Changedchange_password1 field changed
      • addedInput schema / properties / confirm
        Added value: +{
        +  "description": "Must be true — this rotates YOUR OWN login credential; the old password stops working immediately.",
        +  "type": "boolean"
        +}
    • Removedcheck_connection
    • Addedcleanup_scratch
    • Removedconfigure_project
    • Removedcreate_block
    • Removedcreate_resource
    • Removeddelete_resource
    • Changedexecute_query3 fields changed
      • changedInput schema / properties / limit / description
        Previous value: -"Max rows to return (truncates the response for cheap exploration; omit for all rows)."New value: +"Max rows to return (default 1000, truncation is noted). 0 = unlimited — all rows."
      • removedInput schema / properties / limit / exclusiveMinimum
        Removed value: -0
      • addedInput schema / properties / limit / minimum
        Added value: +0
    • Removedget_capabilities
    • Removedget_metrics
    • Removedget_references
    • Removedget_resource
    • Changedlist_resource5 fields changed
      • addedInput schema / properties / fields
        Added value: +{
        +  "description": "Project each record to these top-level fields (+ id) — e.g. ['name','updated_at'] to grab concurrency tokens cheaply. Takes precedence over only_names.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "minItems": 1,
        +  "type": "array"
        +}
      • addedOutput schema / properties / auto_projected
        Added value: +{
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / note
        Added value: +{
        +  "type": "string"
        +}
      • addedOutput schema / properties / total / description
        Added value: +"null = unknown (server-side page; the portal sent no count)"
      • changedOutput schema / properties / total / type
        Previous value: -"number"New value: +[
        +  "number",
        +  "null"
        +]
    • Removedmigration_preflight
    • Removedplace_blocks
    • Removedreload_config
    • Removedrepair_query_metadata
    • Removedrestore_resource
    • Addedscore_portal
    • Removedset_user_access
    • Removedsynthesize_theme
    • Removedupdate_block
    • Removedupdate_config
    • Removedupdate_resource
    • Removedvc_diff
    • Removedvc_log
    • Removedvc_status
  2. 44 tool updatesv4.2.0
    • Addedbind_block_query
    • Addedchange_password
    • Addedcheck_connection
    • Addedconfigure_project
    • Changedcreate_block5 fields changed
      • addedInput schema / properties / css_file
        Added value: +{
        +  "description": "Path to a file holding the block's CSS; the server reads it into `css`. Same byte-exact guarantee and same root containment as html_file. Mutually exclusive with `css`.",
        +  "type": "string"
        +}
      • changedInput schema / properties / data / description
        Previous value: -"Query config object, e.g. { \"__source__\": \"<datasource-uuid>\", \"columns\": [\"*\"], \"limit\": 500 }."New value: +"Legacy query-config object. Ignored by current portal versions — bind data via `ui_queries` instead. Kept only for backward compatibility."
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "description": "Run every gate and report the would-be write without creating.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / html_file
        Added value: +{
        +  "description": "Path to a file holding the block's HTML+JS; the server reads it into json_data.html. PREFER THIS over inlining large content: the bytes are read off disk byte-exact, so nothing is retyped and characters like em dashes or box-drawing cannot silently normalize. Mutually exclusive with json_data.html. Must resolve under the CWD, the VC dir, or a PORTAL_FILE_ROOTS entry.",
        +  "type": "string"
        +}
      • addedInput schema / properties / ui_queries
        Added value: +{
        +  "description": "Query/datasource binding (this is how a block gets data — the portal has no `data` field). Array of { enabled, page_size, query_id, filter_strategy }, e.g. [{ \"enabled\": true, \"page_size\": 50, \"query_id\": \"<query-uuid>\", \"filter_strategy\": { \"type\": \"blacklist\", \"value\": [] } }]. The query_id must reference a saved query that already has a datasource attached. On update_block, the existing ui_queries is preserved automatically unless you pass a new value.",
        +  "items": {
        +    "additionalProperties": {},
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
    • Addedcreate_resource
    • Removeddelete_block
    • Addeddelete_resource
    • Addeddescribe_resource
    • Addedexecute_query
    • Removedfetch_sample_rows
    • Addedfind_resource
    • Removedget_block
    • Addedget_capabilities
    • Addedget_config
    • Addedget_metrics
    • Addedget_references
    • Addedget_resource
    • Addedget_rules
    • Addedget_user_access
    • Addedget_version
    • Removedlist_blocks
    • Removedlist_datasources
    • Removedlist_queries
    • Addedlist_resource
    • Addedmigration_preflight
    • Addednaming
    • Addedplace_blocks
    • Addedprofile_datasource
    • Addedreload_config
    • Addedrepair_query_metadata
    • Addedrestore_resource
    • Addedrun_db_modification
    • Addedset_user_access
    • Addedsnapshot_portal
    • Addedsynthesize_theme
    • Changedupdate_block7 fields changed
      • addedInput schema / properties / css_file
        Added value: +{
        +  "description": "Path to a file holding the block's CSS; the server reads it into `css`. Same byte-exact guarantee and same root containment as html_file. Mutually exclusive with `css`.",
        +  "type": "string"
        +}
      • changedInput schema / properties / data / description
        Previous value: -"New query config object."New value: +"Legacy query-config object — ignored by current portals; use ui_queries."
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "description": "Run every gate and report the would-be write without updating.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / html_file
        Added value: +{
        +  "description": "Path to a file holding the block's HTML+JS; the server reads it into json_data.html. PREFER THIS over inlining large content: the bytes are read off disk byte-exact, so nothing is retyped and characters like em dashes or box-drawing cannot silently normalize. Mutually exclusive with json_data.html. Must resolve under the CWD, the VC dir, or a PORTAL_FILE_ROOTS entry.",
        +  "type": "string"
        +}
      • addedInput schema / properties / merge_tags
        Added value: +{
        +  "description": "Merge `tags` into the block's existing tags instead of replacing them — preserves functional tags (e.g. a nav 'Menu' tag). Default false.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / tags / description
        Previous value: -"Replacement tag list."New value: +"Tag list — replaces existing tags, unless merge_tags:true (then these are added)."
      • addedInput schema / properties / ui_queries
        Added value: +{
        +  "description": "Replacement query/datasource binding. Omit to keep the block's existing binding (it is preserved automatically); pass [] to explicitly unbind all queries.",
        +  "items": {
        +    "additionalProperties": {},
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
    • Addedupdate_config
    • Addedupdate_resource
    • Addedvalidate_block
    • Addedvalidate_portal
    • Addedvc_diff
    • Addedvc_log
    • Addedvc_status
  3. 8 tool updatesv1.0.0
    • First observedcreate_block
    • First observeddelete_block
    • First observedfetch_sample_rows
    • First observedget_block
    • First observedlist_blocks
    • First observedlist_datasources
    • First observedlist_queries
    • First observedupdate_block

TDQS

A4/5.0

Scored across 17 tools

Disambiguation4/5

Most tools target distinct resource/action pairs, and list_resource, find_resource, and describe_resource are clearly separated by purpose. The main ambiguity is between score_portal and validate_portal, both of which audit portal health, but their descriptions distinguish scoring from structural validation.

Naming Consistency4/5

Almost every tool follows the imperative verb_noun pattern: validate_block, list_resource, find_resource, execute_query, snapshot_portal, etc. The outlier is 'naming', which is a noun rather than a verb phrase and internally multiplexes suggest/parse actions, breaking the otherwise predictable pattern.

Tool Count3/5

At 17 tools, the server sits in the borderline-heavy range, though the breadth of portal management topics partly justifies it. Each tool has a distinct role, but the count is high enough that an agent must work harder to select among them.

Completeness2/5

The tool descriptions repeatedly reference create_block/update_block, create_resource/update_resource, get_resource, and delete_resource, but none of those tools are actually exposed. This leaves significant gaps: an agent can validate and list resources but cannot perform general create, update, or single-resource get operations.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers