Skip to main content
Glama
patrickdeanfox

Zuar Portal Blocks MCP Server

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
PORTAL_URLYesThe base URL of your Zuar Portal, e.g. https://your-portal.zuarbase.net
PORTAL_API_KEYYesYour portal API key from Admin > Auth > API Keys
PORTAL_USER_IDYesYour portal user UUID from Admin > Users > your user

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": true
}
prompts
{
  "listChanged": true
}
resources
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
create_block

Create a new HTML block. Accepts the full block payload (name, css, json_data, ui_queries, tags, access). Type is always html. Read the zportal://guide/* resources first to produce a correct two-field block. Equivalent to create_resource resource="block" with a typed schema; the authoring rules, structural and referential gates run either way. dry_run:true previews.

update_block

Update an existing HTML block by UUID. Only the fields you pass change — the server merges them over the current record, so an omitted ui_queries keeps the existing data binding. Authoring rules validate only your delta; violations the stored block already has are grandfathered to warnings, never blockers. dry_run:true previews the exact write.

bind_block_query

Give an existing block its data binding so currentBlock.queryResults[0] is populated. Pass an existing query_id, OR a datasource_id (a SELECT * query is auto-created against it and linked). Sets the block's ui_queries while preserving its html/css/name. The bound query must have a datasource (auto-created ones do) or the portal rejects it.

place_blocks

Declaratively manage which blocks a page (layout) shows, in ONE atomic read-modify-write. mode=merge (default): upsert the blocks entries (re-placing updates position) and remove any ids in remove; other blocks on the page are untouched. mode=replace: the page becomes EXACTLY the blocks list (requires confirm:true; customized grid.layouts, hidden flags and geometry of surviving blocks are preserved). Each entry: { block_id, position?, height?, hidden? }. An entry with NEITHER position NOR height keeps an already-placed block's current geometry (safe for hidden-only toggles); a NEW block with no position auto-stacks full-width below existing content in array order; pass position (or height) to re-place explicitly. Blocks are placed, not created (create_block first) and never deleted (delete_resource does that). dry_run:true previews the final grid without writing. GRID BUDGET: every box is a % of the page's grid.layouts.{bp}.height (default 100), and blocks stack by accumulating top — so Σ(heights) must stay <= the grid height or the page CLIPS silently. The auto-place default is 50%, so THREE defaulted blocks already overflow: pass an explicit height for each. Budget guide at 100: filter bar 8-10, KPI band 18-22, chart card 30-35, table 35-40. Pair blocks side by side (same top, width 50 each) to buy vertical room before making the page taller, and aim to land everything above the fold at lg (the portal's header/footer/sidebars already take their share). If it truly won't fit, raise the grid height in increments of 25 (100 -> 125 -> 150) via update_resource. The response reports overflow when content exceeds the grid.

validate_blockA

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).

describe_resourceA

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.

list_resourceA

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.

cleanup_scratchA

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.

score_portalA

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.

find_resourceA

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.

get_references

Read-only dependency query for a record, both directions: dependents = records that would break if this one were deleted (the same analysis the delete gate runs — pages placing a block, queries reading a datasource, blocks bound to a query, system defaults), and references = records THIS one points at, each flagged exists:false when dangling. Use it before a delete/rename, or to trace where a datasource/query is used.

get_resource

Fetch a single record of a resource by its id (or name, for tags). Control the token cost: summary:true returns an overview (bulk fields become size stats — a block drops ~2,750 → ~150 tokens); fields:[…] returns just those top-level fields (+ id). Omit both for the full record.

create_resource

Create a record. body carries the resource's fields (see describe_resource). Writes are gated by risk domain: content is on by default; data (datasources/db_modifications) needs PORTAL_ALLOW_DATA_WRITES; admin (users/groups/etc.) needs PORTAL_ALLOW_ADMIN_WRITES.

update_resource

Update a record by id. Only the fields in body change; the server merges them over the current record (portal PUT is full-replace) so untouched fields are preserved. Same write gating as create_resource. For loop/parallel safety, pass expected_updated_at (the updated_at you saw when reading) — the write is then refused with a conflict if anyone changed the record in between, instead of silently overwriting their work.

delete_resource

Delete a record by id (or name, for tags). Requires confirm:true. Same write gating as create_resource — the record's risk domain must be enabled. Refuses if other records depend on the target (pass force=true to override) and refuses to remove the last admin / your own account. Content records can be reverted with restore_resource when version control is on.

validate_portalA

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.

get_rulesA

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.

namingA

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.

get_versionA

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.

profile_datasourceA

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).

execute_queryA

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.

run_db_modificationA

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.

change_passwordA

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.

get_user_accessA

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).

set_user_access

Replace a user's group membership and/or permission set — pass groups, permissions, or both; each provided list is a FULL REPLACE (not additive), an omitted one is untouched. Requires admin writes enabled (PORTAL_ALLOW_ADMIN_WRITES=1) and confirm:true. dry_run:true reports the current access next to the proposed lists without writing. Stripping YOUR OWN admin access is refused unless allow_self_lockout=true.

get_configA

Fetch the portal's configuration document.

update_config

Set a value at a config path. path is the key path (array of strings); value is the new value; merge merges into an existing object instead of replacing. Portal-wide setting: requires admin writes enabled (PORTAL_ALLOW_ADMIN_WRITES=1) AND confirm=true; dry_run=true previews the current value without writing. The response returns previous_at_path — pass it back to revert. Pointing default_dashboard/theme_id at a missing record is refused.

check_connection

START HERE — confirm in ONE authenticated round-trip which Zuar Portal this folder is on, its version, who you're signed in as, what you may write, and whether browser assist is on. Proves the credentials actually work, and on failure reports the reason and the fix. Returns a few short fields rather than a JSON wall — call get_capabilities only if you then need the full tool/group, VC and audit posture.

get_capabilities

Report what this server can currently do AND which portal it's pointed at: enabled/disabled tool groups and tools, the write-safety posture (read-only / data / admin), the resolved project config (portal URL + user, config file path), version-control status, browser-assist flag, and whether audit logging is on. THE orientation call — run it before acting; it stays available even when other tool groups are gated off. All secrets are redacted.

get_metrics

Report in-memory observability metrics for THIS server process: per-tool call count, error count/rate, and latency (avg/max/last ms), plus rolled-up totals, uptime, and the upstream circuit-breaker state. Metadata only — no payloads or secrets. Resets when the process restarts. Use it to spot a failing tool or a degraded portal upstream.

configure_project

Connect THIS folder to a Zuar Portal — one install can serve many portals, each folder gets its own ./.zuar-portal/config.json. Collects the portal credentials (URL, API key, user ID), a name for the portal, the design intake (brand accent, light/dark, density) and — optionally — GitHub version-control settings (repo URL, PAT, local mirror path) and the Claude-for-Chrome preference, PROMPTING field-by-field via MCP elicitation when the client supports it (interactive:false forces headless argument-only mode; any field passed as an argument is used as-is and not prompted for). Validates the portal with a live login and the GitHub token/repo against the GitHub API, then writes: the config + a .gitignore (so secrets never get committed), a project design.md every block here inherits, and a managed CLAUDE.md block naming the bound portal. PINS the folder to the portal's live identity, so later writes to a DIFFERENT portal are refused instead of silently landing there. Refuses to overwrite an existing config unless overwrite=true. Secrets are stored locally and never echoed or logged.

reload_config

Drop every in-process config cache and re-read config from disk (project .zuar-portal/config.json, bundle config, env-derived settings), resetting the portal HTTP session. Use after MANUALLY editing a config file — edits to the already-loaded project file are also picked up automatically (mtime-aware), but a NEWLY CREATED config file or bundle/env change needs this. Note: tool-surface gating is registered at startup and still needs a server restart to change.

synthesize_theme

Turn brand preferences into a portal theme spec — a customProperties token map + css — WITHOUT creating anything. Give a website_url and it fetches the homepage (SSRF-guarded) to suggest brand colors when primary_color is omitted. Create the returned theme with create_resource resource="theme" body={ name, json_data: { customProperties, css } } (a normal VC-tracked content write); the response includes that exact call as create_with. The design_intake PROMPT walks a user through these choices conversationally.

vc_status

Report whether portal version control is enabled (set PORTAL_VC_DIR to a git repo path), the repo location, and push config (PORTAL_VC_PUSH / PORTAL_VC_REMOTE). When enabled, every content write (blocks, layouts, queries, themes, partials, snippets, translations, dashboards, tags) is auto-committed so it can be reverted with restore_resource.

snapshot_portalA

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.

vc_log

List recent commits, optionally scoped to one record. Use a returned hash with restore_resource to revert to that version. Requires PORTAL_VC_DIR.

vc_diff

Unified diff between two committed versions — of one record (resource + id) or the whole repo. Defaults answer "what did the last change change?": ref_b = HEAD, ref_a = the previous commit touching the record (or HEAD's parent repo-wide). Pass hashes from vc_log for any pair. Inspect the diff BEFORE restore_resource so a revert is never blind. Requires PORTAL_VC_DIR.

restore_resource

Revert a content record — or a datasource — to a prior committed version and write it back through the same gated write path as any other update (a DELETED block is recreated under its old id; a datasource restore needs data writes enabled). Omit ref to undo the most recent change, or pass a commit hash from vc_log; inspect it first with vc_diff. The restore is itself committed. Requires PORTAL_VC_DIR. NOT covered (never snapshotted): users, groups, permissions, credentials, api_keys, system records, db_modifications.

migration_preflight

Empirical migration audit of the connected portal — run BEFORE writing any conversion pattern. Covers every block by FUNCTION, not just scripted ones: classifies each placed block (scripted/template/markup/native-widget), scans EVERY code- and config-bearing field (not just the html body) for legacy patterns — comment/string-aware so a commented-out call doesn't drive scope — flags hardcoded portal origins (environment portability), maps placement through layouts AND partials AND snippets (an 'orphan' is unreachable through all three), and preflights every bound query: does it execute, does it return column metadata, are numeric columns delivered as strings, is the STORED column metadata stale (repair_query_metadata fixes that). Also returns the browser-probe checklist for the runtime questions a server-side scan cannot answer. Read-only.

repair_query_metadata

Re-introspect and store a saved query's COLUMN METADATA without touching its SQL or datasource binding — safe by construction (name/datasources/raw_sql/sql_form are re-sent verbatim; only columns is refreshed from a live execution). Stale stored metadata makes bound blocks render blank/wrong after schema or version moves; migration_preflight flags it as stored_metadata_stale. Dry-run by default: reports the stored-vs-live diff; pass apply:true to write. Round-trip verifies the write and mirrors it to version control.

Prompts

Interactive templates invoked by user choice

NameDescription
zuar_portal_startConfirm the portal connection in one call, then ask what to do. The cheap way to open a session.
zuar_portal_quickstartConfirm the connection + posture and route to the right next step (build a block, explore data, theme, audit, configure).
create_zportal_blockGuided workflow to build a correct Zuar Portal HTML block: discover data, follow the two-field structure, then create the block.
setup_zuar_projectGuided first-time setup via the configure_project tool: prompt for the portal URL, API key, user ID and a name for THIS folder's portal, the design intake (brand accent, theme modes, density), and optionally a GitHub version-control repo — then write ./.zuar-portal/config.json + design.md + a managed CLAUDE.md block, verify the portal login and the GitHub token/repo, and PIN the folder to the portal so writes can never land on a different one.
migrate_block_to_118Guided migration of a legacy block (async fetchResults + loadedCallback in the fetch chain) to the 1.18+ synchronous queryResults lifecycle: QUERY_INDEX slots, loadedCallback in a finally, and the columns.length readiness gate that fixes blank hidden/deferred blocks.
add_loading_overlayAdd the sanctioned spinner + fade-out overlay to a block already on the 1.18 queryResults pattern, so slow-query or hidden/deferred blocks show a spinner instead of blank white space while data loads.
block_perf_passAudit and fix a slow / large-data block: measure the real payload, trim a SELECT * query to the columns the block reads, prefetch the chart library in parallel with the data, and make every data-wait timeout 60 s with an honest expiry message.
design_intakeGuided theme design intake: interview the user about their brand, colors and feel, synthesize a portal theme with the synthesize_theme tool, and (on approval) create it with create_resource.
migration_kickoffSettle a version-migration's scope decisions up front and batched — chart-widget policy, test pages, hardcoded origins, cadence, rollback home — then record them and run the preflight.

Resources

Contextual data attached and managed by the client

NameDescription
block-structureTwo-field HTML/CSS structure and theme variables for an HTML block.
zportal-apiFilters, modals, cross-block visibility, resources.load, user/page APIs, and native block types.
currentblockHow to read query results inside a block and react to filters.
chartingWhich charting library to use by complexity, and how to load each via zPortal.resources.load (incl. the amCharts 2-step core-then-modules load — no AMCHARTS_LOADER).
migration-1.18Move a legacy block (async fetchResults + loadedCallback in the fetch chain) to the 1.18+ synchronous queryResults lifecycle — QUERY_INDEX slots, loadedCallback in finally, the columns.length readiness gate for hidden/deferred blocks, and the confirmed migration pitfalls.
migration-playbookThe version-agnostic migration METHOD, distilled from a real 1.17→1.19 run: scope by function not code, elicit scope decisions once at kickoff (the migration_kickoff prompt), treat docs as hypothesis and probe the target build (migration_preflight + browser probes), convert in minimal-diff batches with round-trip verification, verify reference-first, and the silently-blank-block diagnosis ladder.
loading-overlayThe sanctioned loading-state exception: add a spinner overlay to a 1.18-pattern block whose data can lag the script (slow queries, hidden/deferred reveal) — pre-flight checks, HTML/CSS, the three JS touch points, and the fade-out that removes itself.
block-performancePerf playbook for slow / large-data blocks, verified in production: measure the payload first (curl timings), trim SELECT * queries to the columns the block actually reads (get_references before touching a shared query), prefetch the chart library in parallel with the data, 60 s data-wait timeouts with an honest expiry message, and change-safety discipline for production portals.
visual-verificationHow agents SEE a rendered block (screenshot, console, network) for visual debugging and the final visual gate — when it applies, the sign-in caveat, the workflow, and graceful code-only fallback.
conventionsThe always/never rules and code-style conventions enforced by create_block/update_block.
design-systemThe portal's house style — palette, typography, spacing, chart styling, and component patterns. Apply it when authoring or restyling blocks so surfaces stay consistent.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/patrickdeanfox/zuar-portal-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server