Skip to main content
Glama
thenavidm

Senja MCP Server

by thenavidm

Senja MCP Server & CLI

npm CI License YouTube X LinkedIn

Senja MCP server and CLI for Codex and AI agents. 12 shared tools for current testimonials and invites, isolated private projects, exact reviewed tasks and bounded exports.

One package supplies both task CLI commands and local MCP tools, with a bundled Claude Desktop extension. Built and maintained by Navid Moazzez. Built on Slipway, which turns one definition of each tool into the MCP server and the CLI. Full setup is on navid.me.

This native terminal illustrates shipped tool names; it is not a recording of real customer messages. Use a private intended-project API key. Account features and email delivery remain subject to Senja. Official hosted MCP already supports search, links and invites; our local task workflows and limits are compared below.

Two ways to use it

Command line

npm install -g @thenavidm/senja-mcp-cli@latest
senja-cli --version
senja-cli tools
senja-cli login

MCP server, for your AI app

codex mcp add senja -- npx -y @thenavidm/senja-mcp-cli@latest

Configure private credentials in the client/runtime before native requests. Ask it to find the relevant existing proof before proposing an approved change. Full setup is in INSTALL.md.

Which one

Use MCP for structured client tools and CLI for scripts or agent shell tasks. Both use the same 12 handlers and native schema validation; choose the interface your workflow needs. Neither eliminates provider costs or model context.

Related MCP server: @indica-facil/mcp-chatwoot

Features

Capability

CLI command

MCP tool

List testimonials

senja-cli list-testimonials

list_testimonials

Read one testimonial

senja-cli get-testimonial

get_testimonial

Import a testimonial

senja-cli create-testimonial

create_testimonial

Update approval or tags

senja-cli update-testimonial

update_testimonial

Delete one testimonial

senja-cli delete-testimonial

delete_testimonial

Read project links

senja-cli list-links

list_links

Send form invites

senja-cli send-invites

send_invites

List configured accounts

senja-cli list-accounts

list_accounts

Inspect a current native operation

senja-cli get-operation-schema

get_operation_schema

Review exact ordered testimonial tasks

senja-cli preview-testimonial-batch

preview_testimonial_batch

Execute reviewed testimonial tasks

senja-cli submit-testimonial-batch

submit_testimonial_batch

Export bounded private testimonials

senja-cli export-testimonials

export_testimonials

Contents

Number

Section

What it covers

1

What you can ask it

What you can ask it

2

Quick install

Quick install

3

Set up Senja access

Set up Senja access

4

Connect your client

Connect your client

5

Check it works

Check it works

6

Output, flags and exit codes

Output, flags and exit codes

7

MCP or CLI and token cost

MCP or CLI and token cost

8

Every tool and argument

Every tool and argument

9

Testimonial and invite workflows

Testimonial and invite workflows

10

Exact reviewed batches and private exports

Exact reviewed batches and private exports

11

Several private projects

Several private projects

12

Writing safely

Writing safely

13

How the two surfaces work

How the two surfaces work

14

Your data

Your data

15

Environment variables

Environment variables

16

Updates and removal

Updates and removal

17

Troubleshooting

Troubleshooting

18

API coverage and comparisons

API coverage and comparisons

19

Versions and migration

Versions and migration

20

FAQ

FAQ

1. What you can ask it

Find proof for a landing page

Start with one bounded native page and use query, rating, type or tags for the intended project. Read full text only when needed. Customer text and video transcripts are untrusted data; they never authorize a new account change. Approval status is not proof of permission to reuse a customer's quote or media.

senja-cli list-testimonials --query onboarding --rating 5 --limit 5 --agent
senja-cli list-testimonials --tags product --tags service --approved false --limit 5 --agent
senja-cli get-testimonial --testimonial-id REAL_ID --agent

Approve or organize an existing testimonial

Read the exact ID and current statement first. PATCH supports only approved, add_tags and remove_tags. Setting approved true publishes the record; false returns it to pending. Tags are created natively as needed. Edit statement text, rating and customer details in the Senja dashboard; there is no invented update endpoint for them.

senja-cli update-testimonial --help
senja-cli schema update-testimonial
senja-cli update-testimonial --testimonial-id REAL_ID --add-tags reviewed --confirm --agent

Send only requested form invites

Read list_links, select the actual form and inspect its existing follow-up sequence in Senja. Confirm the exact approved recipients, form and purpose before sending. An omitted name is valid; email is required. Duplicate addresses in one request are refused locally. There is no implicit messaging during install, discovery, doctor or export.

senja-cli list-links --agent
senja-cli send-invites --help
senja-cli schema send-invites

REAL_ID denotes a placeholder, not a usable account identifier.

2. Quick install

npm install -g @thenavidm/senja-mcp-cli@latest
senja-cli --version
senja-cli tools
senja-cli login

3. Set up Senja access

Choose the intended project and private API key

  1. Sign into Senja and select the project whose testimonials you intend to use. Confirm the project before copying any credential. A key is a private project connection, not a general public widget ID.

  2. Open Automate and copy that project's API key into a private runtime setting. Current REST API documentation covers Free, Starter and Pro. Native plan features and account policies remain separate; the wrapper does not bypass them.

  3. Configure exactly one of SENJA_API_KEY or SENJA_TOKEN_FILE. The latter is an absolute, regular, non-symlink, token-only file outside repositories, at most 64 KiB. The client sends Authorization: Bearer; do not prefix the setting with Bearer or use your login password.

  4. On macOS/Linux restrict the file to your owner with mode 0600 and its parent directory to your owner. On Windows restrict file and parent-directory ACLs separately. POSIX modes do not establish Windows privacy. Each server runtime must be able to read its own file; a GUI, Docker or remote host does not inherit a different terminal's environment automatically.

  5. Run senja-cli doctor for local configuration. When you deliberately want an authenticated read, run doctor --network: it requests GET /testimonials?limit=1 and prints count/verification metadata, not customer records. This proves one project read, not ownership, permission for every endpoint, successful email delivery or an approved mutation.

The package does not load .env files, import browser cookies, create keys, connect OAuth or rotate credentials. login prints these setup instructions only. Keep resolved secrets out of code, screenshots, public issues, version control and AI prompts.

Project profiles and revocation

SENJA_ACCOUNTS is a private JSON array of unique {name,api_key,token_file} profiles. Use one credential method per profile. SENJA_DEFAULT_ACCOUNT selects the default label and --account selects an exact label. An incomplete named profile never inherits a global key, another project or an official hosted session after a missing credential or 401/403.

list_accounts returns labels/default/auth/source only, without keys, token paths, native project identity or network traffic. Token-only files cache until restart. Changing a file while a process is running does not rotate its cached connection.

To revoke a key, use the intended project's Automate > Regenerate API Key as its Admin/Owner, update every dependent private integration and restart its processes. The provider says the old key stops working after a short transition. Official hosted MCP authorization is a separate connection. Removing this package does not undo testimonial edits, publish approval, permanent deletion, emailed invites, follow-up sequences or existing exports.

Effects and local limits

There is no universal provider quota invented here. Local spacing defaults to 250 ms and request timeout 30 seconds; other processes share native project quotas. Bodies cap at 1 MiB and responses at 5 MiB. No automatic retry, redirect following or media download occurs. A failed write can have an unknown outcome; inspect native state before any explicit repeat.

send_invites uses a real forms[].id from list_links and that form's existing email/follow-up sequence. It is not a local draft, arbitrary email editor or test-send command. Its local 100-recipient cap is not a documented native quota. Receipt sent/skipped values do not prove delivered messages or consent. Only send to the actual approved recipients and purpose.

4. Connect your client

Full client, OS, desktop, private credential and runtime instructions are in INSTALL.md.

Codex

Codex is the current validation priority. Private token paths must exist in the process or remote environment where the server runs.

codex mcp add senja -- npx -y @thenavidm/senja-mcp-cli@latest
codex mcp list

Account credentials must reach the server through private environment settings. codex mcp add --env NAME=value stores values in your local config, so never commit that config or put secrets in a shared command. In TOML, the equivalent server is:

[mcp_servers.senja]
command = "npx"
args = ["-y", "@thenavidm/senja-mcp-cli@latest"]
env_vars = ["SENJA_API_KEY", "SENJA_TOKEN_FILE", "SENJA_ACCOUNTS", "SENJA_DEFAULT_ACCOUNT", "SENJA_READ_ONLY", "SENJA_ALLOW_DESTRUCTIVE"]

env_vars forwards those names from the environment available to Codex. If that environment does not contain them, configure private env settings locally. Codex can also call the CLI directly with SKILL.md and --agent output.

Claude Code

For a user-scoped connection, after privately configuring credentials:

claude mcp add --scope user senja -- npx -y @thenavidm/senja-mcp-cli@latest
claude mcp list

Use the client's private local environment settings for the account variable if they are not inherited. Claude's -e NAME=value registration option writes values into its config; only use it locally through your secret manager, with no shared command transcript. Never place credentials in a project .mcp.json. Reconnect and ask Claude to verify credentials.

Alternatively install the CLI, make SKILL.md available to Claude, and use shell commands. Registering both surfaces is optional.

Claude Desktop

Install the .mcpb extension

  1. Download senja-3.0.0.mcpb from GitHub Releases.

  2. In a supported Claude Desktop build, open Settings > Extensions > Advanced settings > Install Extension… and select it.

  3. Enter a private project API key in the sensitive setting, OR an absolute private token-only file path. Leave the unused method empty. Requests use Authorization: Bearer. Named profiles require private manual runtime settings.

  4. Enable read-only if you want only the 6 read operations. Reconnect and verify the intended project with one deliberate read.

The bundle includes production dependencies and no credentials. Use a regular private token-only file if you prefer file-based credentials. The manifest requires Node 22 or newer from a compatible host. Organization policy may restrict custom extensions. Manual bundle updates require installing the new version; no automatic directory updates are promised. GUI installation remains unverified separately from archive/protocol checks.

Manual config

Open Settings > Developer > Edit Config, or use your platform's config file:

OS

Typical config path

macOS

~/Library/Application Support/Claude/claude_desktop_config.json

Windows

%APPDATA%\Claude\claude_desktop_config.json

Linux

~/.config/Claude/claude_desktop_config.json; confirm the location through Edit Config in your installed build

{
  "mcpServers": {
    "senja": {
      "command": "npx",
      "args": ["-y", "@thenavidm/senja-mcp-cli@latest"],
      "env": {
        "SENJA_API_KEY": "YOUR_PRIVATE_API_KEY",
        "SENJA_TOKEN_FILE": ""
      }
    }
  }
}

Replace the placeholders only in your private file. Merge the server entry into an existing mcpServers object instead of replacing other integrations. Fully quit and reopen Claude Desktop. Do not enable an extension and a manual entry with the same name; choose one route.

If a Windows launcher cannot execute npx directly, use "command": "cmd" with "args": ["/c", "npx", "-y", "@thenavidm/senja-mcp-cli@latest"]. An absolute node executable and installed dist/index.js path also avoids launcher/PATH problems.

Cursor

Use private user settings at ~/.cursor/mcp.json, or Settings > Tools & MCP. Cursor documents environment interpolation and envFile support.

{
  "mcpServers": {
    "senja": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@thenavidm/senja-mcp-cli@latest"],
      "env": {
        "SENJA_API_KEY": "${env:SENJA_API_KEY}",
        "SENJA_TOKEN_FILE": "${env:SENJA_TOKEN_FILE}"
      }
    }
  }
}

The environment values must exist for the Cursor process. If you use envFile, keep that file private and outside version control. A project's .cursor/mcp.json must not contain actual credentials. Reconnect the server after saving.

VS Code and Copilot

Use MCP: Open User Configuration. VS Code uses servers and secure inputs, rather than a mcpServers root:

{
  "inputs": [
    {"type": "promptString", "id": "senja-api-token", "description": "Senja API key (leave empty for a private token file)", "password": true},
    {"type": "promptString", "id": "senja-token-file", "description": "Optional private token-file path (leave empty for API key)"}
  ],
  "servers": {
    "senja": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@thenavidm/senja-mcp-cli@latest"],
      "env": {
        "SENJA_API_KEY": "${input:senja-api-token}",
        "SENJA_TOKEN_FILE": "${input:senja-token-file}"
      }
    }
  }
}

Start Senja through the MCP controls, approve trust if prompted, and enter credentials in the private input prompts. Workspace .vscode/mcp.json may contain this placeholder-only structure, but never resolved secret values. Remote development runs the server in the selected remote environment, so local file paths refer to that environment.

Windsurf

Open Cascade's MCP settings or edit the private user file ~/.codeium/windsurf/mcp_config.json. Use the Claude Desktop manual mcpServers block above with your locally configured env values. See Windsurf's current MCP documentation. Restart or reconnect Senja in Cascade; project files must not contain secrets.

Zed

Open Settings > AI > MCP Servers > Add Server > Add Local Server, or your user settings file. Zed uses context_servers:

{
  "context_servers": {
    "senja": {
      "command": "npx",
      "args": ["-y", "@thenavidm/senja-mcp-cli@latest"],
      "env": {
        "SENJA_API_KEY": "YOUR_PRIVATE_API_KEY",
        "SENJA_TOKEN_FILE": ""
      }
    }
  }
}

Enter actual values only in private user settings. Check the active-server indicator before prompting. Do not wrap command and args inside a nested command object from older Zed examples.

Gemini CLI

Merge the Claude Desktop manual mcpServers block into your private ~/.gemini/settings.json. Configure the private credential values locally, then restart Gemini CLI and inspect /mcp. See Gemini CLI's MCP configuration. Its project settings must not contain real credentials. You can instead use the CLI from an agent shell.

Other local stdio clients use the same command and arguments, adapted to their config format. A client that only accepts a remote MCP URL cannot connect directly: this package does not ship a public HTTP listener. ChatGPT's remote connector setup is not a substitute for local stdio installation.

Docker

Build locally from the reviewed source; no prebuilt registry image is claimed:

git clone https://github.com/thenavidm/senja-mcp-cli.git
cd senja-mcp-cli
docker build -t senja-mcp-cli .
docker run --rm -i -e SENJA_API_KEY senja-mcp-cli

Cline and other local MCP clients

Use the client's Add MCP server flow with command npx, arguments -y and @thenavidm/senja-mcp-cli@latest, stdio transport, and private local SENJA_API_KEY or SENJA_TOKEN_FILE settings. UI names depend on the installed client. Reconnect and discover tools before an account call. Browser-only clients need a remote HTTPS connector; use Senja's official server rather than this local stdio command.

5. Check it works

senja-cli --version
senja-cli tools
senja-cli list-accounts --agent
senja-cli doctor
senja-cli doctor --network
senja-cli list-testimonials --limit 1 --agent --select total,testimonials.id

Only the final two commands intentionally contact Senja. The one-item example may return a private ID; use doctor --network if you only need verification metadata. Never create, approve, delete or email a testimonial merely to test installation. Fixtures/protocol discovery, actual provider outcomes, desktop GUI installation and completed Codex usage measurements are distinct checks.

6. Output, flags and exit codes

senja-cli tools --agent
senja-cli list-testimonials --limit 5 --agent --select total,testimonials.id
senja-cli schema send-invites

--agent requests compact JSON with no prompts and never confirms. senja-cli which <words> finds the command for a task. Repeated array flags collect tags; one --recipients or --tasks flag contains one JSON object. Whole native bodies use payload or an absolute regular non-symlink payload_file capped1MiB. Do not mix body methods.

Exit

Meaning

0

Success

1

Unexpected error

2

Usage, invalid native input, a refused effect, an unknown command or a hidden write

3

Not found

4

Authentication/permission

5

Native API error

7

Rate limited

10

Missing/invalid private configuration

7. MCP or CLI and token cost

MCP clients can load all schemas, defer discovery, or load selected schemas; the mode changes input overhead. CLI use still needs command/schema discovery and model-readable results. --agent and --select can reduce formatting/output for an appropriate task, but do not prove smaller total cost.

Measured on 2026-10-05 against 2.0.1, with Claude Code 2.1.286 on Claude Opus 5.5 (one short prompt with and without the server connected, the difference read from the API's own usage figures) and Codex 0.159.3 on gpt-6.1-sol:

Cost

2.0.1

3.0.0

Claude Code, every tool loaded, every message

7,900

7,551

Claude Code's default, tool search, every message

742

742

SKILL.md, read once

2,358

2,440

Codex over the CLI, one task, median of five

125,564

126,370

Codex over MCP, the same task, median of five

44,983

45,393

The task was "find the command that sends testimonial invites, and the flags it requires". Every tool loaded costs less because the testimonial integrations and media are written once. Over the CLI, two 3.0.0 runs asked which sends testimonial invites, got the command's help in the answer and took three commands; the other three asked which testimonial invites, which fits three commands alike, and took five, as every 2.0.1 run did. The medians are both five-command runs, 806 tokens apart for 3.0.0's longer general help, and over all five runs 3.0.0 averaged 109,209 against 134,461. Over MCP, Codex now prints create_testimonial with its argument comments, 1,692 characters more. SKILL.md costs 82 more because it says how approval works over MCP and how which finds a command, and what exit codes 1 and 2 cover.

Tool-list bytes or characters divided by four are not API usage, and no other offering was measured.

8. Every tool and argument

list_testimonials

Read one native page with exact current search, tag, approval, language and date/rating filters.

Argument

Type

Required

Meaning and constraints

sort

string

Optional

Native sort field; direction uses order. {"enum": ["date", "rating"]}

order

string

Optional

{"enum": ["asc", "desc"]}

approved

boolean

Optional

Exact native/schema value

rating

integer

Optional

{"minimum": 1, "maximum": 5}

type

string

Optional

{"enum": ["text", "video"]}

integration

string

Optional

{"enum": ["twitter", "product_hunt", "google", "facebook", "reddit", "capterra", "g2", "linkedin", "app_store", "trustpilot", "shopify", "play_store", "yelp", "slack", "discord", "apple_podcasts", "telegram", "whatsapp", "instagram", "youtube", "tiktok", "appsumo", "amazon", "zillow", "udemy", "chrome_web_store", "airbnb", "skillshare", "realtor", "sourceforge", "whop", "wordpress", "fiverr", "homestars", "web_page"]}

tags

array

Optional

{"maxItems": 100}

query

string

Optional

Native full-text search, including customer name/email; output may contain personal data.

lang

string

Optional

Native ISO 639 language selector.

limit

integer

Optional

Native page size. total counts only the current page. {"minimum": 1, "maximum": 1000}

page

integer

Optional

{"minimum": 1}

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

senja-cli list-testimonials --help
senja-cli schema list-testimonials
{
  "type": "object",
  "properties": {
    "sort": {
      "type": "string",
      "enum": [
        "date",
        "rating"
      ],
      "description": "Native sort field; direction uses order."
    },
    "order": {
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "approved": {
      "type": "boolean"
    },
    "rating": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5
    },
    "type": {
      "type": "string",
      "enum": [
        "text",
        "video"
      ]
    },
    "integration": {
      "type": "string",
      "enum": [
        "twitter",
        "product_hunt",
        "google",
        "facebook",
        "reddit",
        "capterra",
        "g2",
        "linkedin",
        "app_store",
        "trustpilot",
        "shopify",
        "play_store",
        "yelp",
        "slack",
        "discord",
        "apple_podcasts",
        "telegram",
        "whatsapp",
        "instagram",
        "youtube",
        "tiktok",
        "appsumo",
        "amazon",
        "zillow",
        "udemy",
        "chrome_web_store",
        "airbnb",
        "skillshare",
        "realtor",
        "sourceforge",
        "whop",
        "wordpress",
        "fiverr",
        "homestars",
        "web_page"
      ]
    },
    "tags": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "string",
        "minLength": 1,
        "description": "Nonempty tag name."
      }
    },
    "query": {
      "type": "string",
      "minLength": 1,
      "description": "Native full-text search, including customer name/email; output may contain personal data.",
      "maxLength": 10000
    },
    "lang": {
      "type": "string",
      "minLength": 1,
      "description": "Native ISO 639 language selector.",
      "maxLength": 10
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000,
      "description": "Native page size. total counts only the current page."
    },
    "page": {
      "type": "integer",
      "minimum": 1
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    }
  },
  "required": [],
  "additionalProperties": false
}

Native request: GET /testimonials. No native JSON body.

{
  "name": "list_testimonials",
  "method": "GET",
  "path": "/testimonials",
  "title": "List testimonials",
  "description": "Read one native page with exact current search, tag, approval, language and date/rating filters.",
  "group": "testimonials",
  "risk": "read",
  "params": [
    {
      "name": "sort",
      "key": "sort",
      "schema": {
        "type": "string",
        "enum": [
          "date",
          "rating"
        ],
        "description": "Native sort field; direction uses order."
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "order",
      "key": "order",
      "schema": {
        "type": "string",
        "enum": [
          "asc",
          "desc"
        ]
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "approved",
      "key": "approved",
      "schema": {
        "type": "boolean"
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "rating",
      "key": "rating",
      "schema": {
        "type": "integer",
        "minimum": 1,
        "maximum": 5
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "type",
      "key": "type",
      "schema": {
        "type": "string",
        "enum": [
          "text",
          "video"
        ]
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "integration",
      "key": "integration",
      "schema": {
        "type": "string",
        "enum": [
          "twitter",
          "product_hunt",
          "google",
          "facebook",
          "reddit",
          "capterra",
          "g2",
          "linkedin",
          "app_store",
          "trustpilot",
          "shopify",
          "play_store",
          "yelp",
          "slack",
          "discord",
          "apple_podcasts",
          "telegram",
          "whatsapp",
          "instagram",
          "youtube",
          "tiktok",
          "appsumo",
          "amazon",
          "zillow",
          "udemy",
          "chrome_web_store",
          "airbnb",
          "skillshare",
          "realtor",
          "sourceforge",
          "whop",
          "wordpress",
          "fiverr",
          "homestars",
          "web_page"
        ]
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "tags",
      "key": "tags",
      "schema": {
        "type": "array",
        "maxItems": 100,
        "items": {
          "type": "string",
          "minLength": 1,
          "description": "Nonempty tag name."
        }
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "query",
      "key": "query",
      "schema": {
        "type": "string",
        "minLength": 1,
        "description": "Native full-text search, including customer name/email; output may contain personal data.",
        "maxLength": 10000
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "lang",
      "key": "lang",
      "schema": {
        "type": "string",
        "minLength": 1,
        "description": "Native ISO 639 language selector.",
        "maxLength": 10
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "limit",
      "key": "limit",
      "schema": {
        "type": "integer",
        "minimum": 1,
        "maximum": 1000,
        "description": "Native page size. total counts only the current page."
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    },
    {
      "name": "page",
      "key": "page",
      "schema": {
        "type": "integer",
        "minimum": 1
      },
      "in": "query",
      "required": false,
      "style": "form",
      "explode": true
    }
  ],
  "bodySchema": null,
  "bodyRequired": false,
  "privateOutput": false
}

get_testimonial

Read one exact testimonial, including native video metadata and public/dashboard links.

Argument

Type

Required

Meaning and constraints

testimonial_id

string

Required

Exact testimonial ID. No slash, traversal or arbitrary URL.

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

senja-cli get-testimonial --help
senja-cli schema get-testimonial
{
  "type": "object",
  "properties": {
    "testimonial_id": {
      "type": "string",
      "minLength": 1,
      "description": "Exact testimonial ID. No slash, traversal or arbitrary URL."
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    }
  },
  "required": [
    "testimonial_id"
  ],
  "additionalProperties": false
}

Native request: GET /testimonials/{testimonial_id}. No native JSON body.

{
  "name": "get_testimonial",
  "method": "GET",
  "path": "/testimonials/{testimonial_id}",
  "title": "Read one testimonial",
  "description": "Read one exact testimonial, including native video metadata and public/dashboard links.",
  "group": "testimonials",
  "risk": "read",
  "params": [
    {
      "name": "testimonial_id",
      "key": "testimonial_id",
      "schema": {
        "type": "string",
        "minLength": 1,
        "description": "Exact testimonial ID. No slash, traversal or arbitrary URL."
      },
      "in": "path",
      "required": true,
      "style": "form",
      "explode": true
    }
  ],
  "bodySchema": null,
  "bodyRequired": false,
  "privateOutput": false
}

create_testimonial

Create one text/video testimonial from an authorized existing customer statement. Explicit approval is required.

Argument

Type

Required

Meaning and constraints

title

string

Optional

Exact native/schema value

text

string

Optional

Exact native/schema value

customer_name

string

Optional

Exact native/schema value

customer_company

string

Optional

Exact native/schema value

customer_tagline

string

Optional

Exact native/schema value

customer_username

string

Optional

Exact native/schema value

form_id

string

Optional

Exact native/schema value

url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

thumbnail_url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

customer_avatar

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

customer_company_logo

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

customer_url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

video_url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

type

string

Optional

{"enum": ["text", "video"]}

customer_email

string

Optional

{"format": "email"}

rating

integer

Optional

{"minimum": 1, "maximum": 5}

date

string

Optional

{"format": "date-time"}

approved

boolean

Optional

Explicitly true publishes the testimonial; false keeps it pending.

integration

string

Optional

{"enum": ["twitter", "product_hunt", "google", "facebook", "reddit", "capterra", "g2", "linkedin", "app_store", "trustpilot", "shopify", "play_store", "yelp", "slack", "discord", "apple_podcasts", "telegram", "whatsapp", "instagram", "youtube", "tiktok", "appsumo", "amazon", "zillow", "udemy", "chrome_web_store", "airbnb", "skillshare", "realtor", "sourceforge", "whop", "wordpress", "fiverr", "homestars", "web_page"]}

tags

array

Optional

{"maxItems": 100}

media

array

Optional

{"maxItems": 100}

media[].alt

string

Optional

Exact native/schema value

media[].url

string

Required

{"format": "uri"}

media[].type

string

Required

{"enum": ["image", "video"]}

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

confirm

boolean

Optional

Set true only when the user asked for exactly this action.

payload

object

Optional

Complete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file.

payload.title

string

Optional

Exact native/schema value

payload.text

string

Optional

Exact native/schema value

payload.customer_name

string

Required

Exact native/schema value

payload.customer_company

string

Optional

Exact native/schema value

payload.customer_tagline

string

Optional

Exact native/schema value

payload.customer_username

string

Optional

Exact native/schema value

payload.form_id

string

Optional

Exact native/schema value

payload.url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

payload.thumbnail_url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

payload.customer_avatar

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

payload.customer_company_logo

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

payload.customer_url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

payload.video_url

string

Optional

HTTPS URL without embedded credentials; provider retrieves media where supported. {"format": "uri"}

payload.type

string

Required

{"enum": ["text", "video"]}

payload.customer_email

string

Optional

{"format": "email"}

payload.rating

integer

Optional

{"minimum": 1, "maximum": 5}

payload.date

string

Optional

{"format": "date-time"}

payload.approved

boolean

Optional

Explicitly true publishes the testimonial; false keeps it pending.

payload.integration

string

Optional

{"enum": ["twitter", "product_hunt", "google", "facebook", "reddit", "capterra", "g2", "linkedin", "app_store", "trustpilot", "shopify", "play_store", "yelp", "slack", "discord", "apple_podcasts", "telegram", "whatsapp", "instagram", "youtube", "tiktok", "appsumo", "amazon", "zillow", "udemy", "chrome_web_store", "airbnb", "skillshare", "realtor", "sourceforge", "whop", "wordpress", "fiverr", "homestars", "web_page"]}

payload.tags

array

Optional

{"maxItems": 100}

payload.media

array

Optional

{"maxItems": 100}

payload.media[].alt

string

Optional

Exact native/schema value

payload.media[].url

string

Required

{"format": "uri"}

payload.media[].type

string

Required

{"enum": ["image", "video"]}

payload_file

string

Optional

Absolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags.

senja-cli create-testimonial --help
senja-cli schema create-testimonial
{
  "type": "object",
  "properties": {
    "title": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "text": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "customer_name": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "customer_company": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "customer_tagline": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "customer_username": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "form_id": {
      "type": "string",
      "minLength": 1,
      "description": ""
    },
    "url": {
      "type": "string",
      "minLength": 1,
      "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
      "format": "uri"
    },
    "thumbnail_url": {
      "type": "string",
      "minLength": 1,
      "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
      "format": "uri"
    },
    "customer_avatar": {
      "type": "string",
      "minLength": 1,
      "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
      "format": "uri"
    },
    "customer_company_logo": {
      "type": "string",
      "minLength": 1,
      "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
      "format": "uri"
    },
    "customer_url": {
      "type": "string",
      "minLength": 1,
      "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
      "format": "uri"
    },
    "video_url": {
      "type": "string",
      "minLength": 1,
      "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
      "format": "uri"
    },
    "type": {
      "type": "string",
      "enum": [
        "text",
        "video"
      ]
    },
    "customer_email": {
      "type": "string",
      "minLength": 1,
      "description": "",
      "format": "email"
    },
    "rating": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5
    },
    "date": {
      "type": "string",
      "minLength": 1,
      "description": "",
      "format": "date-time"
    },
    "approved": {
      "type": "boolean",
      "description": "Explicitly true publishes the testimonial; false keeps it pending."
    },
    "integration": {
      "type": "string",
      "enum": [
        "twitter",
        "product_hunt",
        "google",
        "facebook",
        "reddit",
        "capterra",
        "g2",
        "linkedin",
        "app_store",
        "trustpilot",
        "shopify",
        "play_store",
        "yelp",
        "slack",
        "discord",
        "apple_podcasts",
        "telegram",
        "whatsapp",
        "instagram",
        "youtube",
        "tiktok",
        "appsumo",
        "amazon",
        "zillow",
        "udemy",
        "chrome_web_store",
        "airbnb",
        "skillshare",
        "realtor",
        "sourceforge",
        "whop",
        "wordpress",
        "fiverr",
        "homestars",
        "web_page"
      ]
    },
    "tags": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "string",
        "minLength": 1,
        "description": "Nonempty tag name."
      }
    },
    "media": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "object",
        "properties": {
          "alt": {
            "type": "string",
            "minLength": 1,
            "description": ""
          },
          "url": {
            "type": "string",
            "minLength": 1,
            "description": "",
            "format": "uri"
          },
          "type": {
            "type": "string",
            "enum": [
              "image",
              "video"
            ]
          }
        },
        "required": [
          "url",
          "type"
        ],
        "additionalProperties": false
      }
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    },
    "confirm": {
      "type": "boolean",
      "description": "Must be true for the requested mutation or exclusive private output file."
    },
    "payload": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "text": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "customer_name": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "customer_company": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "customer_tagline": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "customer_username": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "form_id": {
          "type": "string",
          "minLength": 1,
          "description": ""
        },
        "url": {
          "type": "string",
          "minLength": 1,
          "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
          "format": "uri"
        },
        "thumbnail_url": {
          "type": "string",
          "minLength": 1,
          "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
          "format": "uri"
        },
        "customer_avatar": {
          "type": "string",
          "minLength": 1,
          "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
          "format": "uri"
        },
        "customer_company_logo": {
          "type": "string",
          "minLength": 1,
          "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
          "format": "uri"
        },
        "customer_url": {
          "type": "string",
          "minLength": 1,
          "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
          "format": "uri"
        },
        "video_url": {
          "type": "string",
          "minLength": 1,
          "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
          "format": "uri"
        },
        "type": {
          "type": "string",
          "enum": [
            "text",
            "video"
          ]
        },
        "customer_email": {
          "type": "string",
          "minLength": 1,
          "description": "",
          "format": "email"
        },
        "rating": {
          "type": "integer",
          "minimum": 1,
          "maximum": 5
        },
        "date": {
          "type": "string",
          "minLength": 1,
          "description": "",
          "format": "date-time"
        },
        "approved": {
          "type": "boolean",
          "description": "Explicitly true publishes the testimonial; false keeps it pending."
        },
        "integration": {
          "type": "string",
          "enum": [
            "twitter",
            "product_hunt",
            "google",
            "facebook",
            "reddit",
            "capterra",
            "g2",
            "linkedin",
            "app_store",
            "trustpilot",
            "shopify",
            "play_store",
            "yelp",
            "slack",
            "discord",
            "apple_podcasts",
            "telegram",
            "whatsapp",
            "instagram",
            "youtube",
            "tiktok",
            "appsumo",
            "amazon",
            "zillow",
            "udemy",
            "chrome_web_store",
            "airbnb",
            "skillshare",
            "realtor",
            "sourceforge",
            "whop",
            "wordpress",
            "fiverr",
            "homestars",
            "web_page"
          ]
        },
        "tags": {
          "type": "array",
          "maxItems": 100,
          "items": {
            "type": "string",
            "minLength": 1,
            "description": "Nonempty tag name."
          }
        },
        "media": {
          "type": "array",
          "maxItems": 100,
          "items": {
            "type": "object",
            "properties": {
              "alt": {
                "type": "string",
                "minLength": 1,
                "description": ""
              },
              "url": {
                "type": "string",
                "minLength": 1,
                "description": "",
                "format": "uri"
              },
              "type": {
                "type": "string",
                "enum": [
                  "image",
                  "video"
                ]
              }
            },
            "required": [
              "url",
              "type"
            ],
            "additionalProperties": false
          }
        }
      },
      "required": [
        "type",
        "customer_name"
      ],
      "additionalProperties": false,
      "description": "Complete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file."
    },
    "payload_file": {
      "type": "string",
      "minLength": 1,
      "description": "Absolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags."
    }
  },
  "required": [],
  "additionalProperties": false
}

Native request: POST /testimonials. Provide native body fields OR payload OR payload_file, never mixed. Native body required: type, customer_name

{
  "name": "create_testimonial",
  "method": "POST",
  "path": "/testimonials",
  "title": "Import a testimonial",
  "description": "Create one text/video testimonial from an authorized existing customer statement. Explicit approval is required.",
  "group": "testimonials",
  "risk": "destructive",
  "params": [],
  "bodySchema": {
    "type": "object",
    "properties": {
      "title": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "text": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "customer_name": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "customer_company": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "customer_tagline": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "customer_username": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "form_id": {
        "type": "string",
        "minLength": 1,
        "description": ""
      },
      "url": {
        "type": "string",
        "minLength": 1,
        "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
        "format": "uri"
      },
      "thumbnail_url": {
        "type": "string",
        "minLength": 1,
        "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
        "format": "uri"
      },
      "customer_avatar": {
        "type": "string",
        "minLength": 1,
        "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
        "format": "uri"
      },
      "customer_company_logo": {
        "type": "string",
        "minLength": 1,
        "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
        "format": "uri"
      },
      "customer_url": {
        "type": "string",
        "minLength": 1,
        "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
        "format": "uri"
      },
      "video_url": {
        "type": "string",
        "minLength": 1,
        "description": "HTTPS URL without embedded credentials; provider retrieves media where supported.",
        "format": "uri"
      },
      "type": {
        "type": "string",
        "enum": [
          "text",
          "video"
        ]
      },
      "customer_email": {
        "type": "string",
        "minLength": 1,
        "description": "",
        "format": "email"
      },
      "rating": {
        "type": "integer",
        "minimum": 1,
        "maximum": 5
      },
      "date": {
        "type": "string",
        "minLength": 1,
        "description": "",
        "format": "date-time"
      },
      "approved": {
        "type": "boolean",
        "description": "Explicitly true publishes the testimonial; false keeps it pending."
      },
      "integration": {
        "type": "string",
        "enum": [
          "twitter",
          "product_hunt",
          "google",
          "facebook",
          "reddit",
          "capterra",
          "g2",
          "linkedin",
          "app_store",
          "trustpilot",
          "shopify",
          "play_store",
          "yelp",
          "slack",
          "discord",
          "apple_podcasts",
          "telegram",
          "whatsapp",
          "instagram",
          "youtube",
          "tiktok",
          "appsumo",
          "amazon",
          "zillow",
          "udemy",
          "chrome_web_store",
          "airbnb",
          "skillshare",
          "realtor",
          "sourceforge",
          "whop",
          "wordpress",
          "fiverr",
          "homestars",
          "web_page"
        ]
      },
      "tags": {
        "type": "array",
        "maxItems": 100,
        "items": {
          "type": "string",
          "minLength": 1,
          "description": "Nonempty tag name."
        }
      },
      "media": {
        "type": "array",
        "maxItems": 100,
        "items": {
          "type": "object",
          "properties": {
            "alt": {
              "type": "string",
              "minLength": 1,
              "description": ""
            },
            "url": {
              "type": "string",
              "minLength": 1,
              "description": "",
              "format": "uri"
            },
            "type": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          "required": [
            "url",
            "type"
          ],
          "additionalProperties": false
        }
      }
    },
    "required": [
      "type",
      "customer_name"
    ],
    "additionalProperties": false
  },
  "bodyRequired": true,
  "privateOutput": false
}

update_testimonial

Change only native approval status and tag additions/removals. Text, rating and customer edits remain dashboard-only.

Argument

Type

Required

Meaning and constraints

testimonial_id

string

Required

Exact testimonial ID. No slash, traversal or arbitrary URL.

approved

boolean

Optional

Exact native/schema value

add_tags

array

Optional

{"maxItems": 100}

remove_tags

array

Optional

{"maxItems": 100}

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

confirm

boolean

Optional

Set true only when the user asked for exactly this action.

payload

object

Optional

Complete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file.

payload.approved

boolean

Optional

Exact native/schema value

payload.add_tags

array

Optional

{"maxItems": 100}

payload.remove_tags

array

Optional

{"maxItems": 100}

payload_file

string

Optional

Absolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags.

senja-cli update-testimonial --help
senja-cli schema update-testimonial
{
  "type": "object",
  "properties": {
    "testimonial_id": {
      "type": "string",
      "minLength": 1,
      "description": "Exact testimonial ID. No slash, traversal or arbitrary URL."
    },
    "approved": {
      "type": "boolean"
    },
    "add_tags": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "string",
        "minLength": 1,
        "description": "Nonempty tag name."
      }
    },
    "remove_tags": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "string",
        "minLength": 1,
        "description": "Nonempty tag name."
      }
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    },
    "confirm": {
      "type": "boolean",
      "description": "Must be true for the requested mutation or exclusive private output file."
    },
    "payload": {
      "type": "object",
      "properties": {
        "approved": {
          "type": "boolean"
        },
        "add_tags": {
          "type": "array",
          "maxItems": 100,
          "items": {
            "type": "string",
            "minLength": 1,
            "description": "Nonempty tag name."
          }
        },
        "remove_tags": {
          "type": "array",
          "maxItems": 100,
          "items": {
            "type": "string",
            "minLength": 1,
            "description": "Nonempty tag name."
          }
        }
      },
      "required": [],
      "additionalProperties": false,
      "description": "Complete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file."
    },
    "payload_file": {
      "type": "string",
      "minLength": 1,
      "description": "Absolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags."
    }
  },
  "required": [
    "testimonial_id"
  ],
  "additionalProperties": false
}

Native request: PATCH /testimonials/{testimonial_id}. Provide native body fields OR payload OR payload_file, never mixed. Native body requires at least one approval or nonempty tag change.

{
  "name": "update_testimonial",
  "method": "PATCH",
  "path": "/testimonials/{testimonial_id}",
  "title": "Update approval or tags",
  "description": "Change only native approval status and tag additions/removals. Text, rating and customer edits remain dashboard-only.",
  "group": "testimonials",
  "risk": "destructive",
  "params": [
    {
      "name": "testimonial_id",
      "key": "testimonial_id",
      "schema": {
        "type": "string",
        "minLength": 1,
        "description": "Exact testimonial ID. No slash, traversal or arbitrary URL."
      },
      "in": "path",
      "required": true,
      "style": "form",
      "explode": true
    }
  ],
  "bodySchema": {
    "type": "object",
    "properties": {
      "approved": {
        "type": "boolean"
      },
      "add_tags": {
        "type": "array",
        "maxItems": 100,
        "items": {
          "type": "string",
          "minLength": 1,
          "description": "Nonempty tag name."
        }
      },
      "remove_tags": {
        "type": "array",
        "maxItems": 100,
        "items": {
          "type": "string",
          "minLength": 1,
          "description": "Nonempty tag name."
        }
      }
    },
    "required": [],
    "additionalProperties": false
  },
  "bodyRequired": true,
  "privateOutput": false
}

delete_testimonial

Permanently delete exactly the requested testimonial. Irreversible; confirmation required.

Argument

Type

Required

Meaning and constraints

testimonial_id

string

Required

Exact testimonial ID. No slash, traversal or arbitrary URL.

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

confirm

boolean

Optional

Set true only when the user asked for exactly this action.

senja-cli delete-testimonial --help
senja-cli schema delete-testimonial
{
  "type": "object",
  "properties": {
    "testimonial_id": {
      "type": "string",
      "minLength": 1,
      "description": "Exact testimonial ID. No slash, traversal or arbitrary URL."
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    },
    "confirm": {
      "type": "boolean",
      "description": "Must be true for the requested mutation or exclusive private output file."
    }
  },
  "required": [
    "testimonial_id"
  ],
  "additionalProperties": false
}

Native request: DELETE /testimonials/{testimonial_id}. No native JSON body.

{
  "name": "delete_testimonial",
  "method": "DELETE",
  "path": "/testimonials/{testimonial_id}",
  "title": "Delete one testimonial",
  "description": "Permanently delete exactly the requested testimonial. Irreversible; confirmation required.",
  "group": "testimonials",
  "risk": "destructive",
  "params": [
    {
      "name": "testimonial_id",
      "key": "testimonial_id",
      "schema": {
        "type": "string",
        "minLength": 1,
        "description": "Exact testimonial ID. No slash, traversal or arbitrary URL."
      },
      "in": "path",
      "required": true,
      "style": "form",
      "explode": true
    }
  ],
  "bodySchema": null,
  "bodyRequired": false,
  "privateOutput": false
}

Read native form, widget, Wall of Love, quick-link, case-study and sizzle-reel groups with their IDs and URLs.

Argument

Type

Required

Meaning and constraints

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

senja-cli list-links --help
senja-cli schema list-links
{
  "type": "object",
  "properties": {
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    }
  },
  "required": [],
  "additionalProperties": false
}

Native request: GET /links. No native JSON body.

{
  "name": "list_links",
  "method": "GET",
  "path": "/links",
  "title": "Read project links",
  "description": "Read native form, widget, Wall of Love, quick-link, case-study and sizzle-reel groups with their IDs and URLs.",
  "group": "project_links",
  "risk": "read",
  "params": [],
  "bodySchema": null,
  "bodyRequired": false,
  "privateOutput": false
}

send_invites

Send email invites using a selected form and its existing follow-up sequence. Local cap100 recipients, not a documented provider quota.

Argument

Type

Required

Meaning and constraints

form_id

string

Optional

Exact forms[].id from list_links.

recipients

array

Optional

{"minItems": 1, "maxItems": 100}

recipients[].email

string

Required

{"format": "email"}

recipients[].name

string

Optional

Exact native/schema value

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

confirm

boolean

Optional

Set true only when the user asked for exactly this action.

payload

object

Optional

Complete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file.

payload.form_id

string

Required

Exact forms[].id from list_links.

payload.recipients

array

Required

{"minItems": 1, "maxItems": 100}

payload.recipients[].email

string

Required

{"format": "email"}

payload.recipients[].name

string

Optional

Exact native/schema value

payload_file

string

Optional

Absolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags.

senja-cli send-invites --help
senja-cli schema send-invites
{
  "type": "object",
  "properties": {
    "form_id": {
      "type": "string",
      "minLength": 1,
      "description": "Exact forms[].id from list_links."
    },
    "recipients": {
      "type": "array",
      "minItems": 1,
      "maxItems": 100,
      "items": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "minLength": 1,
            "description": "",
            "format": "email"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": ""
          }
        },
        "required": [
          "email"
        ],
        "additionalProperties": false
      }
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    },
    "confirm": {
      "type": "boolean",
      "description": "Must be true for the requested mutation or exclusive private output file."
    },
    "payload": {
      "type": "object",
      "properties": {
        "form_id": {
          "type": "string",
          "minLength": 1,
          "description": "Exact forms[].id from list_links."
        },
        "recipients": {
          "type": "array",
          "minItems": 1,
          "maxItems": 100,
          "items": {
            "type": "object",
            "properties": {
              "email": {
                "type": "string",
                "minLength": 1,
                "description": "",
                "format": "email"
              },
              "name": {
                "type": "string",
                "minLength": 1,
                "description": ""
              }
            },
            "required": [
              "email"
            ],
            "additionalProperties": false
          }
        }
      },
      "required": [
        "form_id",
        "recipients"
      ],
      "additionalProperties": false,
      "description": "Complete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file."
    },
    "payload_file": {
      "type": "string",
      "minLength": 1,
      "description": "Absolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags."
    }
  },
  "required": [],
  "additionalProperties": false
}

Native request: POST /invites. Provide native body fields OR payload OR payload_file, never mixed. Native body required: form_id, recipients

{
  "name": "send_invites",
  "method": "POST",
  "path": "/invites",
  "title": "Send form invites",
  "description": "Send email invites using a selected form and its existing follow-up sequence. Local cap100 recipients, not a documented provider quota.",
  "group": "invites",
  "risk": "destructive",
  "params": [],
  "bodySchema": {
    "type": "object",
    "properties": {
      "form_id": {
        "type": "string",
        "minLength": 1,
        "description": "Exact forms[].id from list_links."
      },
      "recipients": {
        "type": "array",
        "minItems": 1,
        "maxItems": 100,
        "items": {
          "type": "object",
          "properties": {
            "email": {
              "type": "string",
              "minLength": 1,
              "description": "",
              "format": "email"
            },
            "name": {
              "type": "string",
              "minLength": 1,
              "description": ""
            }
          },
          "required": [
            "email"
          ],
          "additionalProperties": false
        }
      }
    },
    "required": [
      "form_id",
      "recipients"
    ],
    "additionalProperties": false
  },
  "bodyRequired": true,
  "privateOutput": false
}

list_accounts

Local profile labels/default/auth method only. No keys, token paths, provider identity or network request.

Argument

Type

Required

Meaning and constraints

senja-cli list-accounts --help
senja-cli schema list-accounts
{
  "type": "object",
  "properties": {},
  "required": [],
  "additionalProperties": false
}

get_operation_schema

Local reviewed method/path/query/body schema and provenance for one native tool. No credentials or provider request.

Argument

Type

Required

Meaning and constraints

operation

string

Required

Exact native tool name, e.g. update_testimonial or send_invites. {"enum": ["list_testimonials", "get_testimonial", "create_testimonial", "update_testimonial", "delete_testimonial", "list_links", "send_invites"]}

senja-cli get-operation-schema --help
senja-cli schema get-operation-schema
{
  "type": "object",
  "properties": {
    "operation": {
      "type": "string",
      "enum": [
        "list_testimonials",
        "get_testimonial",
        "create_testimonial",
        "update_testimonial",
        "delete_testimonial",
        "list_links",
        "send_invites"
      ],
      "description": "Exact native tool name, e.g. update_testimonial or send_invites."
    }
  },
  "required": [
    "operation"
  ],
  "additionalProperties": false
}

preview_testimonial_batch

Local validation and SHA-256 of exact ordered testimonial/import/invite work, selected profile label and reviewed schema. No provider reads, key load, identity check, price or rollback guarantee.

Argument

Type

Required

Meaning and constraints

tasks

array

Required

One to twenty exact ordered supported testimonial/import/invite operations. Invite requests may include up to 100 recipients each; review the exact complete recipient list and existing form follow-up sequence. {"minItems": 1, "maxItems": 20}

tasks[].tool

string

Required

{"enum": ["create_testimonial", "update_testimonial", "delete_testimonial", "send_invites"]}

tasks[].arguments

object

Required

Actual native tool arguments without account, confirm, payload_file or output_file.

account

string

Optional

Exact selected private account profile; binds label, not key ownership.

senja-cli preview-testimonial-batch --help
senja-cli schema preview-testimonial-batch
{
  "type": "object",
  "properties": {
    "tasks": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "description": "One to twenty exact ordered supported testimonial/import/invite operations. Invite requests may include up to 100 recipients each; review the exact complete recipient list and existing form follow-up sequence.",
      "items": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string",
            "enum": [
              "create_testimonial",
              "update_testimonial",
              "delete_testimonial",
              "send_invites"
            ]
          },
          "arguments": {
            "type": "object",
            "description": "Actual native tool arguments without account, confirm, payload_file or output_file."
          }
        },
        "required": [
          "tool",
          "arguments"
        ],
        "additionalProperties": false
      }
    },
    "account": {
      "type": "string",
      "description": "Exact selected private account profile; binds label, not key ownership."
    }
  },
  "required": [
    "tasks"
  ],
  "additionalProperties": false
}

submit_testimonial_batch

Confirmed one-to-twenty ordered testimonial/import/invite tasks. Prevalidate all and verify exact hash before first request. Stop on first failure with known results/failed index/unattempted indices; no retries, rollback or implicit continuation.

Argument

Type

Required

Meaning and constraints

tasks

array

Required

One to twenty exact ordered supported testimonial/import/invite operations. Invite requests may include up to 100 recipients each; review the exact complete recipient list and existing form follow-up sequence. {"minItems": 1, "maxItems": 20}

tasks[].tool

string

Required

{"enum": ["create_testimonial", "update_testimonial", "delete_testimonial", "send_invites"]}

tasks[].arguments

object

Required

Actual native tool arguments without account, confirm, payload_file or output_file.

account

string

Optional

Exact selected private account profile; binds label, not key ownership.

confirm

boolean

Optional

Set true only when the user asked for exactly this action.

review_sha256

string

Required

Exact preview_testimonial_batch hash for identical requests, profile label, schema and order. {"pattern": "^[a-f0-9]{64}$"}

senja-cli submit-testimonial-batch --help
senja-cli schema submit-testimonial-batch
{
  "type": "object",
  "properties": {
    "tasks": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "description": "One to twenty exact ordered supported testimonial/import/invite operations. Invite requests may include up to 100 recipients each; review the exact complete recipient list and existing form follow-up sequence.",
      "items": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string",
            "enum": [
              "create_testimonial",
              "update_testimonial",
              "delete_testimonial",
              "send_invites"
            ]
          },
          "arguments": {
            "type": "object",
            "description": "Actual native tool arguments without account, confirm, payload_file or output_file."
          }
        },
        "required": [
          "tool",
          "arguments"
        ],
        "additionalProperties": false
      }
    },
    "account": {
      "type": "string",
      "description": "Exact selected private account profile; binds label, not key ownership."
    },
    "confirm": {
      "type": "boolean",
      "description": "Explicit approval for this exact requested ordered batch."
    },
    "review_sha256": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "Exact preview_testimonial_batch hash for identical requests, profile label, schema and order."
    }
  },
  "required": [
    "tasks",
    "review_sha256"
  ],
  "additionalProperties": false
}

export_testimonials

Confirmed paginated GET export to an exclusive new 0600 JSON file. Stop on short native page or local caps. Never downloads media, follows URLs, overwrites files, retries or implies an atomic complete backup.

Argument

Type

Required

Meaning and constraints

sort

string

Optional

Native sort field; direction uses order. {"enum": ["date", "rating"]}

order

string

Optional

{"enum": ["asc", "desc"]}

approved

boolean

Optional

Exact native/schema value

rating

integer

Optional

{"minimum": 1, "maximum": 5}

type

string

Optional

{"enum": ["text", "video"]}

integration

string

Optional

{"enum": ["twitter", "product_hunt", "google", "facebook", "reddit", "capterra", "g2", "linkedin", "app_store", "trustpilot", "shopify", "play_store", "yelp", "slack", "discord", "apple_podcasts", "telegram", "whatsapp", "instagram", "youtube", "tiktok", "appsumo", "amazon", "zillow", "udemy", "chrome_web_store", "airbnb", "skillshare", "realtor", "sourceforge", "whop", "wordpress", "fiverr", "homestars", "web_page"]}

tags

array

Optional

{"maxItems": 100}

query

string

Optional

Native full-text search, including customer name/email; output may contain personal data.

lang

string

Optional

Native ISO 639 language selector.

limit

integer

Optional

Native page size. total counts only the current page. {"minimum": 1, "maximum": 1000}

page

integer

Optional

{"minimum": 1}

account

string

Optional

Exact configured private account profile label; not a tenant or provider account ID.

confirm

boolean

Optional

Set true only when the user asked for exactly this action.

start_offset

integer

Optional

Resume inside the first requested page using an export receipt offset and identical filters/page size. Provider state may have changed. {"minimum": 0, "maximum": 999}

max_pages

integer

Optional

Local request budget, default 10. {"minimum": 1, "maximum": 100}

max_items

integer

Optional

Local item budget, default 1000. May stop within a page; receipt records an offset. {"minimum": 1, "maximum": 10000}

output_file

string

Required

Absolute new file in an existing private directory. Restrict Windows ACLs separately.

senja-cli export-testimonials --help
senja-cli schema export-testimonials
{
  "type": "object",
  "properties": {
    "sort": {
      "type": "string",
      "enum": [
        "date",
        "rating"
      ],
      "description": "Native sort field; direction uses order."
    },
    "order": {
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "approved": {
      "type": "boolean"
    },
    "rating": {
      "type": "integer",
      "minimum": 1,
      "maximum": 5
    },
    "type": {
      "type": "string",
      "enum": [
        "text",
        "video"
      ]
    },
    "integration": {
      "type": "string",
      "enum": [
        "twitter",
        "product_hunt",
        "google",
        "facebook",
        "reddit",
        "capterra",
        "g2",
        "linkedin",
        "app_store",
        "trustpilot",
        "shopify",
        "play_store",
        "yelp",
        "slack",
        "discord",
        "apple_podcasts",
        "telegram",
        "whatsapp",
        "instagram",
        "youtube",
        "tiktok",
        "appsumo",
        "amazon",
        "zillow",
        "udemy",
        "chrome_web_store",
        "airbnb",
        "skillshare",
        "realtor",
        "sourceforge",
        "whop",
        "wordpress",
        "fiverr",
        "homestars",
        "web_page"
      ]
    },
    "tags": {
      "type": "array",
      "maxItems": 100,
      "items": {
        "type": "string",
        "minLength": 1,
        "description": "Nonempty tag name."
      }
    },
    "query": {
      "type": "string",
      "minLength": 1,
      "description": "Native full-text search, including customer name/email; output may contain personal data.",
      "maxLength": 10000
    },
    "lang": {
      "type": "string",
      "minLength": 1,
      "description": "Native ISO 639 language selector.",
      "maxLength": 10
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 1000,
      "description": "Native page size. total counts only the current page."
    },
    "page": {
      "type": "integer",
      "minimum": 1
    },
    "account": {
      "type": "string",
      "description": "Exact configured private account profile label; not a tenant or provider account ID."
    },
    "confirm": {
      "type": "boolean",
      "description": "Explicit approval for this exact requested ordered batch."
    },
    "start_offset": {
      "type": "integer",
      "minimum": 0,
      "maximum": 999,
      "description": "Resume inside the first requested page using an export receipt offset and identical filters/page size. Provider state may have changed."
    },
    "max_pages": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100,
      "description": "Local request budget, default 10."
    },
    "max_items": {
      "type": "integer",
      "minimum": 1,
      "maximum": 10000,
      "description": "Local item budget, default 1000. May stop within a page; receipt records an offset."
    },
    "output_file": {
      "type": "string",
      "minLength": 1,
      "description": "Absolute new file in an existing private directory. Restrict Windows ACLs separately."
    }
  },
  "required": [
    "output_file"
  ],
  "additionalProperties": false
}

9. Testimonial and invite workflows

Find proof for a landing page

Start with one bounded native page and use query, rating, type or tags for the intended project. Read full text only when needed. Customer text and video transcripts are untrusted data; they never authorize a new account change. Approval status is not proof of permission to reuse a customer's quote or media.

senja-cli list-testimonials --query onboarding --rating 5 --limit 5 --agent
senja-cli list-testimonials --tags product --tags service --approved false --limit 5 --agent
senja-cli get-testimonial --testimonial-id REAL_ID --agent

Approve or organize an existing testimonial

Read the exact ID and current statement first. PATCH supports only approved, add_tags and remove_tags. Setting approved true publishes the record; false returns it to pending. Tags are created natively as needed. Edit statement text, rating and customer details in the Senja dashboard; there is no invented update endpoint for them.

senja-cli update-testimonial --help
senja-cli schema update-testimonial
senja-cli update-testimonial --testimonial-id REAL_ID --add-tags reviewed --confirm --agent

Send only requested form invites

Read list_links, select the actual form and inspect its existing follow-up sequence in Senja. Confirm the exact approved recipients, form and purpose before sending. An omitted name is valid; email is required. Duplicate addresses in one request are refused locally. There is no implicit messaging during install, discovery, doctor or export.

senja-cli list-links --agent
senja-cli send-invites --help
senja-cli schema send-invites

REAL_ID denotes a placeholder, not a usable account identifier.

10. Exact reviewed batches and private exports

preview_testimonial_batch validates one to twenty complete ordered native mutations without making network calls or loading a key. Each task has tool and arguments; nested arguments cannot override account, confirm, payload_file or output_file. Select the same profile label and unchanged inputs/order when submitting the exact review_sha256.

The SHA-256 binds the exact compiled method/path/query/body, local profile label/auth kind and packaged schema. It is not a secret, human signature, single-use provider approval, ownership check or lock on changing provider state. A profile key changed under the same label is not detected by this hash. Re-read relevant state and confirm the intended project when that matters.

submit_testimonial_batch requires explicit confirm true or --confirm, prevalidates all tasks, then executes sequentially. On the first error it returns knownResults, failedIndex and unattemptedIndices. No retry, rollback or automatic continuation occurs. A failed request can already have taken effect. Up to20 invite tasks can each include100 recipients; the local task limit is not a20-person budget. Review the full recipient list and follow-up consequences.

senja-cli preview-testimonial-batch --help
senja-cli schema submit-testimonial-batch

export_testimonials reserves one absolute new private file exclusively, performs only the bounded requested list pages, and returns path/bytes/SHA-256 and receipt metadata. Defaults: 10 pages, 1,000 items, native page size 100. Local caps: 100 pages, 10,000 items and 5 MiB final JSON. A short native page signals exhaustion within the selected filters at that moment. Native total counts the current page, not the entire project.

When a local cap stops the walk, continuation reports page, offset and limit. Resume with that page, identical filters/page size and start_offset. Page changes can cause shifted records; no atomic snapshot, complete backup or deduplication guarantee is made. A second resume writes a different new file; it never appends to or overwrites the earlier export. Review/de-duplicate native IDs when combining evolving pages.

Failure removes only the file this export newly created. Media URLs and transcript metadata remain JSON data; this package never follows or downloads them. Read-only mode refuses file output too. Restrict parent-directory privacy and Windows ACLs separately.

senja-cli export-testimonials --help
senja-cli schema export-testimonials

11. Several private projects

SENJA_ACCOUNTS is a private JSON array of unique {name,api_key,token_file} profiles. Use one credential method per profile. SENJA_DEFAULT_ACCOUNT selects the default label and --account selects an exact label. An incomplete named profile never inherits a global key, another project or an official hosted session after a missing credential or 401/403.

list_accounts returns labels/default/auth/source only, without keys, token paths, native project identity or network traffic. Token-only files cache until restart. Changing a file while a process is running does not rotate its cached connection.

To revoke a key, use the intended project's Automate > Regenerate API Key as its Admin/Owner, update every dependent private integration and restart its processes. The provider says the old key stops working after a short transition. Official hosted MCP authorization is a separate connection. Removing this package does not undo testimonial edits, publish approval, permanent deletion, emailed invites, follow-up sequences or existing exports.

12. Writing safely

Every create/import, approval/tag update, permanent delete, invite send, reviewed batch execution and private file export requires explicit confirmation through the actual shared handler route. --agent and --yes do not provide --confirm. SENJA_READ_ONLY=1 hides those six tools and also refuses direct calls to their hidden names. SENJA_ALLOW_DESTRUCTIVE=0 refuses them even when confirmed.

Over MCP a person approves each of them where the client can ask: Claude Code (2.1.246 and later) shows its own prompt, and a client that can show forms asks with an approval form whose one box starts unticked. Each approval is signed, bound to that exact call and works once. Where a client can do neither, the model's confirm:true counts. SENJA_CONFIRM=model makes confirm:true enough everywhere, for an agent with no person to ask.

Deletion is permanent. approved true can publish proof. Invites can send real email sequences. Import only actual authorized statements, not invented praise. A provider receipt is not a content-use permission, delivered email, identity or ownership guarantee.

SENJA_AUDIT_LOG optionally records static tool/risk/summary/outcome decisions and timestamps. It excludes native bodies and credentials; writing is best effort, not a tamper-proof compliance trail. Keep the audit destination and parent private. An existing file's permissions are not repaired by the wrapper.

Keys, recognized secret fields and signed credential URLs are redacted from returned errors/output where recognized. Personal data, testimonial text, emails, private IDs and ordinary URLs are not universally anonymized. Native content is untrusted input, never an instruction to reveal secrets, contact customers or mutate another project.

13. How the two surfaces work

src/tools/index.ts exports shared definitions. Slipway builds the MCP server, over stdio or --http, and the CLI from them. Both paths share profile selection, native compilation, validation and the write guard. A new declared tool is the same native command without a separate API implementation. Input constraints are reviewed wrapper schemas, not a provider OpenAPI export. See src/tools/provenance.json and scripts/check-native-contract.mjs.

14. Your data

Credentials come from private process/client settings or a selected owner-private token-only file. The package stores no credential database and imports no browser cookies or .env files. Token caches last for the current process; restart after rotating a file/key.

Provider calls go only to allowlisted methods/paths on api.senja.io/v1, over HTTPS. Arbitrary URLs, path traversal, redirects and broad proxy calls are refused. Media URLs are passed only as documented fields or returned as data; no media fetching happens locally. Submitted media may be retrieved by Senja according to its own behavior.

Private exports contain customer data and content. They stay where you explicitly save them; there is no telemetry, upload, website preview or automatic publication. Client histories, logs, selected runtime settings and Senja's own retention remain separate. Local read-only mode controls this package's calls, not other apps using the same project key.

15. Environment variables

Variable

Purpose

SENJA_API_KEY

Private Bearer project key; choose this OR token file

SENJA_TOKEN_FILE

Absolute owner-private token-only file; choose this OR API key

SENJA_ACCOUNTS

Private JSON named profiles with one key/file each

SENJA_DEFAULT_ACCOUNT

Exact configured default profile label

SENJA_READ_ONLY

1/true hides and directly refuses all six mutations/file operations

SENJA_ALLOW_DESTRUCTIVE

0/false refuses confirmed operations

SENJA_AUDIT_LOG

Optional private best-effort static guard decision log

SENJA_REQUEST_TIMEOUT_MS

Default 30000; local accepted range 100–300000 ms

SENJA_MIN_REQUEST_INTERVAL_MS

Default 250; local accepted range 0–10000 ms, not distributed native quota enforcement

SENJA_CONFIRM

human by default; model lets confirm:true alone approve over MCP, for an agent with no person to ask

SENJA_SURFACE

full by default; search lists three tools that find, describe and run the rest

SENJA_TOOL_TIMEOUT_MS

Give up on any tool after this long

SENJA_HTTP_PORT, SENJA_HTTP_HOST, SENJA_HTTP_TOKEN

For --http: port 8787 and host 127.0.0.1 by default; any other host needs the bearer token

SENJA_HTTP_ALLOWED_ORIGINS

Comma-separated browser origins allowed to call --http; a page from any other site is refused

SENJA_DEBUG

1 prints debug lines on stderr

16. Updates and removal

Use the update/removal steps in INSTALL.md. Restart processes after key changes. File outputs and native effects remain until deliberately handled. Reinstall a new desktop bundle manually; npm@latest does not hot-replace a running server.

17. Troubleshooting

Symptom

Check

No binary or Node error

Node 22+, npm/PATH in the actual GUI/remote runtime; npm.cmd if Windows policy blocks npm.ps1

Exit10 or unknown profile

Exact profile name and one private key/file; no fallback exists

Unreadable token file

Absolute owner-private regular non-symlink file under 64 KiB; restart after replacing it

401/403

Selected project/key/permissions and revocation; official hosted auth is separate

Old sort/per_page/language rejected

Use sort date/rating, order asc/desc, limit and lang

PATCH edit refused

Only approved/add_tags/remove_tags; use dashboard for text/customer/rating edits

Cannot send or delete

Explicit --confirm and local policy; inspect native permissions and real purpose

Invites skipped

Inspect native sent/skipped receipt and existing form sequence; do not blindly repeat

Export stopped at cap

Inspect page/offset/limit and resume to a different new private file

Existing output file

Choose a new file; never remove/overwrite unrelated data

Review hash mismatch

Preview the exact unchanged order/inputs/profile/schema again

429 or uncertain write result

No automatic retry; inspect native state and available rate-limit guidance

Browser-only client

Use official hosted MCP; local stdio needs a supported server runtime

Desktop GUI blocked

Organization/client support and Node 22 runtime; archive discovery does not prove GUI acceptance

18. API coverage and comparisons

Official hosted MCP

Senja already has official account MCP at https://mcp.senja.io. It searches testimonials, finds proof for a use case, creates testimonials, tags records, retrieves asset links/embeds and sends form invites. Current setup docs include Free, Starter and Pro. Hosted account authorization and client approvals are separate from this local API-key package.

Use the official connector when that native experience fits your task. A dedicated official task CLI was not identified in the provider material reviewed on October 3, 2026; this is not a universal or permanent absence claim. This package does not claim to add invites that the official MCP already supports, expose every Senja UI feature, create Studio graphics or replace native embed editing.

Reviewed community server

andrewconnell/senja-mcp at commit 417445647eac47f94c2d12d9196a68d2e1d22559 exposes three testimonial MCP tools. Its inspected API client already uses Bearer auth, separate sort/order, lang/limit and repeated tag query values. Its package does not declare a standalone task CLI; its create handler has no mandatory confirmation argument. This says nothing about external clients' approval controls.

The reviewed interface describes a data array and paid-plan prerequisite; current provider documentation describes testimonials and current-page total, and covers Free/Starter/Pro. Our request/response fixtures follow current provider fields. Community customer_website is not the current documented customer_url field.

Useful owned workflows and limits

The verified addition is a shared task CLI/local MCP with isolated named project profiles, mandatory mutation/file approval, direct read-only refusal, locally reviewed ordered writes and bounded private paginated export with explicit page/offset continuation. These controls are exercised in fixtures and real protocol/CLI processes. They do not establish overall superiority, authenticated account outcomes or token savings.

Capability

This package

Existing alternatives

Task interface

12 shared MCP tools and task CLI commands

Official hosted MCP and reviewed community stdio MCP

Native public API

Seven reviewed documented endpoints

Official hosted tools may cover different native features

Project credentials

Unique local profiles, no global credential fallback

Official connector authorization stays native

Ordered changes

Exact local hash, prevalidation, stop on failure

Not a provider-state lock or replacement for client approval

Private export

Bounded pages/items/bytes and exclusive JSON file

Not an atomic complete backup, consent registry or media downloader

Task tokens

Measured against 2.0.1 in README section 7

No comparison with another offering

19. Versions and migration

Component

Reviewed version

Package/desktop

3.0.0

Slipway

0.1.20

Native public API

v1; seven endpoints checked 2026-10-03

Community source

417445647eac47f94c2d12d9196a68d2e1d22559

Node

>=22

Historical private MCP

1.0.0; three tools

Matched Codex task/token usage

Measured against 2.0.1 in README section 7

Legacy caller

Current contract

Required change

per_page

limit

Use native page-size field

language

lang

Use native language selector

date_asc/date_desc sort

sort date/rating plus order

Send field and direction separately

tags string

tags array

Repeat --tags or use a JSON array

create name/email/headline/company

customer_name/customer_email/customer_tagline/customer_company

Use native fields and required type

avatar_url/company_logo_url

customer_avatar/customer_company_logo

Use native HTTPS fields

url used as customer website

url is source; customer_url is customer website

Choose the actual intended field

Three tools with startup key requirement

12 tools with credential-free discovery and guarded effects

Discover tools before private setup; update scripts for approval

No CLI binary

senja-cli and senja-mcp

Use @thenavidm scope and @latest

CHANGELOG.md records the dated major update. Private legacy history stays private; source publishing starts from a clean verified snapshot, preserving AGPL-3.0.

20. FAQ

Yes. Its hosted connector already searches testimonials, creates proof, retrieves asset links and sends form invites. This package adds a shared terminal/local task interface and the documented local workflows. Use the official option when it fits your needs; the comparison does not claim ours replaces its full native feature set.

The CLI runs the same tools through the same handlers, input validation and write guard, built by Slipway from each tool's one definition. Scripts can use JSON, field selection and exit codes. A dedicated official task CLI was not identified in the reviewed provider material; that finding is dated, not a permanent absence claim.

Current provider REST and MCP setup documentation includes Free, Starter and Pro. Account restrictions and native feature eligibility remain in Senja. The package is free under AGPL-3.0 and cannot bypass a plan or permission check; do not reuse older community paid-only prerequisites as current facts.

Choose the intended project and open Automate in Senja. Store exactly one key privately in SENJA_API_KEY or an absolute owner-private SENJA_TOKEN_FILE outside repositories. The client uses Bearer authentication. Never paste resolved secrets into README examples, public issues, screenshots or AI chats.

Yes. SENJA_ACCOUNTS supplies unique private named profiles, each with its own key or token file. Select an exact name with --account. Named profiles never inherit global credentials or another project. Labels are local settings and do not prove the provider key owner or project identity.

No. login prints private setup instructions and never makes a request, opens an OAuth flow, generates a key or imports a browser session. doctor without --network checks configuration only. doctor --network deliberately makes one limit=1 testimonial request and prints verification metadata.

The reviewed catalogue includes testimonial list/get/create/PATCH/delete, project links and form invites, plus five local/workflow helpers. This is the current seven-endpoint public REST scope reviewed for this package, not a claim to expose every hosted MCP or Senja UI action.

Native PATCH supports only approval status and tag additions/removals. Text, rating and customer edits belong in the Senja dashboard. create_testimonial uses actual customer_name/type fields for an authorized import; it does not invent a generalized edit API.

Yes, according to current native API documentation; false returns it to pending. These are confirmed account changes. Approval does not establish customer permission to reuse their words or media, and reading a testimonial never authorizes publishing it elsewhere.

Use native query for text/title/customer-field search, repeat --tags for tag names, and use rating/type/integration/approved/lang as appropriate. sort is date or rating and order is asc or desc. The wrapper rejects stale per_page, language and combined date_desc-style sort arguments before network traffic.

No. Native total counts the current returned page. Export stops on a short native page or explicit local caps, recording page/offset/limit continuation. It does not infer a project-wide count from total or promise a consistent snapshot while provider content changes.

Use export_testimonials with explicit confirmation and an absolute new private file. Defaults are10pages and1000items; local maximums are100pages,10000items and5MiB JSON. Resume to a different file using the recorded page/start_offset/limit and unchanged filters. Combine/de-duplicate changing native IDs deliberately; no automatic append or atomic backup is promised.

No. JSON includes native media URLs, transcripts and metadata where returned. The package never follows these URLs or downloads video/image/audio files. Signed credential URLs and known keys are redacted where recognized; other personal/customer content is still private data.

The selected form ID and its existing email/follow-up sequence determine native messages. Use a real forms[].id from list_links and only explicitly approved recipients. The local 100-recipient cap is not a native quota. Native sent/skipped receipts do not prove inbox delivery or consent, and invites also exist in the official MCP.

It prevalidates every complete task and binds exact order/requests/profile label/schema to a local hash before sequential execution. The hash is not a single-use provider token, human signature, provider-state lock, consent record or ownership proof. Reconfirm changing provider state when your task requires it.

Execution stops at the first failure with knownResults, failedIndex and unattemptedIndices. A failed write may already have taken effect and native follow-up emails can continue. There is no retry, rollback or automatic continuation. Inspect native state and receipts before explicitly requesting another action.

No. SENJA_READ_ONLY=1 both hides the six confirmed tools and refuses direct hidden calls through the actual handlers. It also refuses private file exports. SENJA_ALLOW_DESTRUCTIVE=0 disables these effects even when confirmed; --agent and --yes never provide explicit --confirm.

Codex and compatible local stdio clients can register npx -y @thenavidm/senja-mcp-cli@latest. INSTALL.md covers Claude Code/Desktop, Cursor, VS Code/Copilot, Windsurf, Zed, Gemini CLI, Docker and other stdio clients, plus Windows/macOS/Linux runtime notes. Browser-only clients need a remote connector such as the official hosted MCP.

The .mcpb includes production dependencies and asks for private sensitive settings or a token file, but compatible hosts still need a Node 22 runtime and organization support. Install newer bundle versions manually. npx@latest resolves the current registry version when the server is restarted; it does not replace a running process or provide guaranteed background updates.

In Claude Code the CLI costs nothing until it is used, plus about 2,440 tokens for SKILL.md once, where the server costs about 740 tokens a message with tool search and 7,600 with every tool loaded. In Codex, finding the command that sends testimonial invites and its flags took a median of 126,370 input tokens over the CLI and 45,393 over MCP. Section 7 has how each was measured. Proven fixture behavior and actual public artifact checks are listed separately from authenticated outcomes and desktop GUI acceptance.

Questions

Open a secret-free issue. Read CONTRIBUTING.md and SECURITY.md.

About the author

Navid Moazzez is a leading AI business strategist, and the host of the AI Creator Summit, watched by 100,000+ creators. He helps creators and founders master AI and build their own AI Operating System (AI OS) to automate their business and life. He creates useful free tools, MCP servers and CLIs that creators and founders can use in their own workflows.

Links

If this is useful, star the repo and come say hi on X.

Dependencies

Runtime: Slipway, which brings the MCP TypeScript SDK, plus Ajv and ajv-formats. Development: TypeScript, Vitest, Vite and MCPB. Exact locked versions appear above. Packaging tools are excluded from desktop runtime.

License

Preserves AGPL-3.0 and existing private legacy history. Read THIRD_PARTY_NOTICES.md. Senja service terms and trademarks remain separate.


© 2026 Navid Media. Made with ❤️ by Navid Moazzez.

Available Tools

12 tools
create_testimonialImport a testimonialC
Destructive

Create one text/video testimonial from an authorized existing customer statement. Explicit approval is required.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoHTTPS URL without embedded credentials; provider retrieves media where supported.
dateNo
tagsNo
textNo
typeNo
mediaNo
titleNo
ratingNo
accountNoExact configured private account profile label; not a tenant or provider account ID.
confirmNoSet true only when the user asked for exactly this action.
form_idNo
payloadNoComplete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file.
approvedNoExplicitly true publishes the testimonial; false keeps it pending.
video_urlNoHTTPS URL without embedded credentials; provider retrieves media where supported.
integrationNo
customer_urlNoHTTPS URL without embedded credentials; provider retrieves media where supported.
payload_fileNoAbsolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags.
customer_nameNo
thumbnail_urlNoHTTPS URL without embedded credentials; provider retrieves media where supported.
customer_emailNo
customer_avatarNoHTTPS URL without embedded credentials; provider retrieves media where supported.
customer_companyNo
customer_taglineNo
customer_usernameNo
customer_company_logoNoHTTPS URL without embedded credentials; provider retrieves media where supported.

TDQS

C2.9/5.0
Behavior2/5

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

Annotations already declare destructiveHint=true, idempotentHint=false, and openWorldHint=true, so the safety profile is partly covered. The description adds only 'explicit approval is required', without explaining that the operation is non-idempotent (repeated calls may duplicate), what gets destroyed/altered, or how the confirm/approved booleans interact. For a destructive, non-idempotent create with 25 parameters, this is thin.

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?

Two tight sentences with the core action front-loaded and no filler. It is efficient, though it errs toward under-specification given the tool's complexity rather than genuine conciseness.

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

Completeness2/5

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

With 25 parameters, nested objects, no output schema, and a destructive non-idempotent profile, the description should carry far more context than two sentences. Key behaviors (payload exclusivity, publish vs pending via approved, confirm semantics) live only in schema fragments and are never surfaced coherently.

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

Parameters2/5

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

Schema description coverage is only 44% across 25 parameters, including a nested payload object, media arrays, an integration enum, and the semantically loaded confirm/approved flags. The description mentions only that the testimonial is text or video, adding essentially nothing about the payload-vs-flat-parameter relationship, payload_file exclusivity, or the approval flag's effect.

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

Purpose4/5

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

States a specific verb+resource ('Create one text/video testimonial') and scopes the source ('from an authorized existing customer statement'), which distinguishes it from list/get/update/delete siblings. It does not, however, differentiate itself from the batch-submission sibling (submit_testimonial_batch), which has an overlapping purpose.

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?

'Explicit approval is required' implies a gate on invocation, but the description never says when to prefer this single-create tool over submit_testimonial_batch or preview_testimonial_batch, nor does it reference the confirm flag that the schema itself describes. Usage is only implied.

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

delete_testimonialDelete one testimonialA
Destructive

Permanently delete exactly the requested testimonial. Irreversible; confirmation required.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountNoExact configured private account profile label; not a tenant or provider account ID.
confirmNoSet true only when the user asked for exactly this action.
testimonial_idYesExact testimonial ID. No slash, traversal or arbitrary URL.

TDQS

A3.8/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true, readOnlyHint=false and idempotentHint=false, so the safety profile is covered structurally. The description adds real value beyond that: permanence, irreversibility, and a mandatory confirmation gate before the delete proceeds.

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 destructive verb and scope, with zero filler. Every clause carries information an agent needs before calling.

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 single-record delete with no output schema, the description plus annotations cover action, scope, reversibility, and the confirmation requirement. It omits edge behavior such as what happens when the ID does not exist, but that is a minor gap for this complexity.

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% and the schema already documents account, confirm, and testimonial_id semantics in detail. The description only echoes the 'exactly the requested' scoping and confirmation requirement, adding little beyond the schema. Baseline 3 is appropriate.

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

Purpose4/5

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

States a specific verb and resource ('Permanently delete... testimonial') with a scoping qualifier ('exactly the requested'). An agent can distinguish it from update_testimonial and create_testimonial without opening the schema, though no sibling is named explicitly.

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?

'Confirmation required' implies a precondition for use, and 'exactly the requested' hints at a no-bulk scope, but there is no explicit when-to-use/when-not guidance and no alternative tool is named for softer or reversible removal.

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

export_testimonialsExport bounded private testimonialsA
Destructive

Confirmed paginated GET export to an exclusive new0600 JSON file. Stop on short native page or local caps. Never downloads media, follows URLs, overwrites files, retries or implies an atomic complete backup.

ParametersJSON Schema
NameRequiredDescriptionDefault
langNoNative ISO 639 language selector.
pageNo
sortNoNative sort field; direction uses order.
tagsNo
typeNo
limitNoNative page size. total counts only the current page.
orderNo
queryNoNative full-text search, including customer name/email; output may contain personal data.
ratingNo
accountNoExact configured private account profile label; not a tenant or provider account ID.
confirmNoSet true only when the user asked for exactly this action.
approvedNo
max_itemsNoLocal item budget, default1000. May stop within a page; receipt records an offset.
max_pagesNoLocal request budget, default10.
integrationNo
output_fileYesAbsolute new file in an existing private directory. Restrict Windows ACLs separately.
start_offsetNoResume inside the first requested page using an export receipt offset and identical filters/page size. Provider state may have changed.

TDQS

A3.9/5.0
Behavior5/5

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

Annotations declare destructive=true / readOnly=false / openWorld=true, and the description goes well beyond them with concrete file behavior: exclusive new 0600 file, no overwriting, no media download, no URL following, no retries, and the explicit warning that this is not an atomic complete backup. That is exactly the extra context a mutating export 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?

Two short sentences, front-loaded with the core action and then the guardrails; nothing is padded. The telegraphic phrasing ('new0600') costs a little clarity for the sake of brevity.

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 17-parameter destructive export with no output schema, the description covers the highest-risk behaviors (no overwrite, non-atomic, no retry) that annotations alone would not convey. It leaves the return shape and the receipt/offset mechanics unstated, which is a minor gap.

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 59%, so the schema already documents lang, sort, limit, query, account, output_file, max_items, max_pages, and start_offset. The description reinforces the pagination/cap semantics ('short native page', 'local caps') that relate to page/limit/max_items/max_pages, but adds no syntax or constraints for the undocumented params (type, order, rating, approved, integration).

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

Purpose4/5

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

States a specific verb+resource with scope: a 'confirmed paginated GET export' of testimonials into a new JSON file. This distinguishes it from list_testimonials and the other read siblings. It falls short of 5 only because the dense jargon ('new0600') makes the object of the export slightly opaque on first read.

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?

Usage is implied rather than stated: 'Stop on short native page or local caps' and 'implies an atomic complete backup' tell the agent this is a bounded, non-backup export, and the confirm param's schema note gates invocation. But no alternative sibling is named and no explicit when-not condition is given.

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

get_operation_schemaInspect a current native operationA
Read-onlyIdempotent

Local reviewed method/path/query/body schema and provenance for one native tool. No credentials or provider request.

ParametersJSON Schema
NameRequiredDescriptionDefault
operationYesExact native tool name, e.g. update_testimonial or send_invites.

TDQS

A3.6/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive, and closed-world. The description adds meaningful context beyond that: it is the 'local reviewed' copy and requires no credentials or provider request, which tells the agent this is a side-effect-free metadata read rather than a live call. It stops short of describing the returned structure.

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, front-loaded sentences with no filler: the first states what is returned, the second removes a likely misassumption (that a call might touch credentials or a provider). Nothing is wasted.

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?

With no output schema, the description does sketch the return content (method/path/query/body schema plus provenance) and rules out auth or network side effects. It does not describe the shape or format of that schema object, leaving a small gap for a 1-parameter introspection tool.

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%, with an enumerated list of valid operation names and an example format, so the schema carries the parameter semantics. The description's 'for one native tool' reinforces that the single parameter selects an operation, matching the baseline for a fully documented schema.

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

Purpose4/5

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

States a specific verb (inspect/return) and resource (schema + provenance for one native operation), so an agent can tell this is a metadata lookup rather than an actual operation. It does not explicitly contrast itself with the sibling operation tools (get_testimonial, update_testimonial, etc.), but 'for one native tool' plus the tool name make the distinction inferable.

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

Usage Guidelines2/5

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

There is no explicit when-to-use, when-not-to-use, or alternative guidance. 'Local reviewed ... No credentials or provider request' hints that this is an offline introspection call, but the description never says to call it before invoking a native tool or how it relates to the sibling operations.

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

get_testimonialRead one testimonialB
Read-onlyIdempotent

Read one exact testimonial, including native video metadata and public/dashboard links.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountNoExact configured private account profile label; not a tenant or provider account ID.
testimonial_idYesExact testimonial ID. No slash, traversal or arbitrary URL.

TDQS

B3.3/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint, so the safety and access profile is covered. The description adds that the read includes native video metadata plus public/dashboard links, which is useful context about what it returns, but says nothing about permissions, error cases, or rate limits.

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 sentence that front-loads the action and resource and wastes nothing. Appropriately sized for a two-parameter read tool.

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?

With annotations covering safety and schema at 100% coverage, the core is complete. But the description hints at a rich return payload (native video metadata, public/dashboard links) with no output schema, so it stops short of telling the agent what it will receive or when this read is preferable to list_testimonials.

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 two parameters are fully documented in the schema itself, including the account label caveat and the traversal guard on testimonial_id. The description adds no param-level meaning, which matches the baseline when the schema does the heavy lifting.

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

Purpose4/5

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

States a specific verb ('Read') and resource ('one exact testimonial') with singular scope, distinguishing it from the sibling list_testimonials. However, it doesn't name or contrast any alternative for retrieving a testimonial by ID, so sibling differentiation is only implicit.

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

Usage Guidelines2/5

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

No when-to-use guidance, no prerequisites, and no mention of alternatives. The description never signals that list_testimonials is for enumeration while this is for a single lookup, leaving the agent to infer routing.

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

list_accountsList configured accountsB
Read-onlyIdempotent

Local profile labels/default/auth method only. No keys, token paths, provider identity or network request.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false and openWorldHint=false, so safety is covered. The description adds genuine context beyond that: output is limited to local profile labels/defaults/auth method and deliberately excludes keys, token paths, and provider identity, telling the agent the result is safe to surface.

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?

It is very short (two fragments) and wastes no words, but it is telegraphic and front-loads a qualifier rather than the core action. The terseness borders on under-specification rather than disciplined conciseness.

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 zero-parameter, read-only listing tool with no output schema and strong annotations, the description covers the essential safety and scope facts. However, it never plainly states what the tool does or what the returned list contains beyond field-level exclusions, leaving the purpose inferable only from the title.

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 takes zero parameters, so there is no parameter semantics for the description to carry. Baseline of 4 applies; nothing is missing on this dimension.

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

Purpose3/5

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

The description never states a verb or resource — it only describes the scope of what is returned ("Local profile labels/default/auth method only"). The title supplies the actual purpose, so the agent can infer this lists local accounts, but the description itself is vague about the action.

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

Usage Guidelines2/5

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

There is no when-to-use guidance and no named alternative among the numerous list_* siblings (list_customers, list_partners, list_domains, etc.). The agent must infer that this tool is for enumerating locally configured account profiles rather than any remote data.

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

list_testimonialsList testimonialsC
Read-onlyIdempotent

Read one native page with exact current search, tag, approval, language and date/rating filters.

ParametersJSON Schema
NameRequiredDescriptionDefault
langNoNative ISO 639 language selector.
pageNo
sortNoNative sort field; direction uses order.
tagsNo
typeNo
limitNoNative page size. total counts only the current page.
orderNo
queryNoNative full-text search, including customer name/email; output may contain personal data.
ratingNo
accountNoExact configured private account profile label; not a tenant or provider account ID.
approvedNo
integrationNo

TDQS

C2.8/5.0
Behavior3/5

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

Annotations cover read-only, idempotent, non-destructive. The description adds one meaningful behavioral note: output may contain personal data (customer name/email), which is genuinely useful. But it does not clarify pagination behavior, total counts, or result limits, leaving meaningful gaps for a 12-param listing 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?

Single sentence, no filler, front-loaded with the core action. It is compact but at the cost of detail; reasonable for its length.

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

Completeness2/5

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

For a 12-parameter tool with no output schema, the description is thin. It doesn't explain return shape (e.g., what 'total counts only the current page' implies), pagination usage, sort/order interaction, or the account/integration filters. It leaves the agent without enough to call it confidently.

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 42%, so roughly half the parameters are undocumented in the schema. The description lists filter categories (search, tag, approval, language, date/rating) but doesn't map them to specific parameters or explain semantics like 'exact configured private account profile label.' Baseline 3 is the ceiling given the description doesn't compensate for the coverage gap.

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

Purpose3/5

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

The description says it reads a 'native page' of testimonials and enumerates filter dimensions, but 'one native page' is unusual phrasing that doesn't clearly state whether pagination is supported (the schema has a 'page' param, so it must be). It never explicitly distinguishes from siblings like get_testimonial or export_testimonials.

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

Usage Guidelines2/5

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

No guidance on when to use this versus get_testimonial (single fetch) or export_testimonials (bulk export). The reader must infer that this is the list/browse operation, but nothing is stated.

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

preview_testimonial_batchReview exact ordered testimonial tasksB
Read-onlyIdempotent

Local validation and SHA-256 of exact ordered testimonial/import/invite work, selected profile label and reviewed schema. No provider reads, key load, identity check, price or rollback guarantee.

ParametersJSON Schema
NameRequiredDescriptionDefault
tasksYesOne to twenty exact ordered supported testimonial/import/invite operations. Invite requests may include up to100 recipients each; review the exact complete recipient list and existing form follow-up sequence.
accountNoExact selected private account profile; binds label, not key ownership.

TDQS

B3.4/5.0
Behavior4/5

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

Annotations already establish readOnly, idempotent, closed-world, non-destructive, so the bar is lower; the description still adds real value by enumerating what is NOT performed (no provider reads, no key load, no identity check, no price, no rollback guarantee). That negative-space disclosure meaningfully bounds what a preview guarantees.

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?

Two tight sentences with no filler, leading with the core operation before the disclaimers. Slightly over-compressed – the SHA-256 and 'reviewed schema' references are opaque without more context.

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?

There is no output schema, so the description arguably should say what a preview yields (the hash, validation result) and how it feeds the submit step, and it does not. It covers the operation's boundaries well but leaves the agent guessing about the return payload and the preview-to-submit workflow.

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 both parameters, including the enum of sub-tools and the recipient-list caveat on tasks. The description adds only loose echoes ('exact ordered ... work', 'selected profile label') without new syntax or format detail, matching the baseline 3.

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

Purpose4/5

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

States a specific action-and-resource pair: local validation plus SHA-256 hashing of an ordered testimonial/import/invite batch. An agent can grasp it's a dry-run validator, but the phrasing is dense jargon and it never names the sibling it pairs with (submit_testimonial_batch), so differentiation is inferred rather than stated.

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

Usage Guidelines2/5

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

No when-to-use guidance at all, and the obvious alternative submit_testimonial_batch is never mentioned. The 'No provider reads...' clauses hint at preview semantics but stop short of telling the agent when to reach for this versus submitting the batch for real.

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

send_invitesSend form invitesB
Destructive

Send email invites using a selected form and its existing follow-up sequence. Local cap100 recipients, not a documented provider quota.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountNoExact configured private account profile label; not a tenant or provider account ID.
confirmNoSet true only when the user asked for exactly this action.
form_idNoExact forms[].id from list_links.
payloadNoComplete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file.
recipientsNo
payload_fileNoAbsolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags.

TDQS

B3.2/5.0
Behavior3/5

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

Annotations already declare destructiveHint=true, openWorldHint=true, idempotentHint=false and readOnlyHint=false, so the safety profile is covered. The description adds the local 100-recipient cap and clarifies it is not a provider quota, which is useful context, but it says nothing about the confirm gate, permission requirements, or what happens to already-sent invites.

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?

Two short, front-loaded sentences with no filler. The second sentence ('Local cap100 recipients, not a documented provider quota') is slightly cryptic in its phrasing, which keeps it from a 5.

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 destructive, non-idempotent, open-world send tool with nested payload objects and no output schema, the description covers the recipient limit but omits the confirm/safety gating, account selection semantics, and any result/error behavior. Adequate but with clear gaps.

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 83%, so the schema already documents account, confirm, form_id, payload and payload_file. The description only vaguely ties form_id to the follow-up sequence and restates the 100-recipient cap that maxItems already encodes, adding little beyond structured data.

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

Purpose4/5

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

States a specific verb and resource ('Send email invites') plus the mechanism (selected form and its existing follow-up sequence), which is enough to distinguish it from the testimonial CRUD siblings. It lacks any explicit contrast with the closest sibling (create_testimonial / submit_testimonial_batch), so it stops short of a 5.

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

Usage Guidelines2/5

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

No when-to-use or when-not-to-use guidance is given. Nothing tells the agent that a 'confirm' flag and an exact 'account' profile are prerequisites, or when to prefer this over a batch/submit sibling. The only usage-like statement is the recipient cap.

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

submit_testimonial_batchExecute reviewed testimonial tasksA
Destructive

Confirmed one-to-twenty ordered testimonial/import/invite tasks. Prevalidate all and verify exact hash before first request. Stop on first failure with known results/failed index/unattempted indices; no retries, rollback or implicit continuation.

ParametersJSON Schema
NameRequiredDescriptionDefault
tasksYesOne to twenty exact ordered supported testimonial/import/invite operations. Invite requests may include up to100 recipients each; review the exact complete recipient list and existing form follow-up sequence.
accountNoExact selected private account profile; binds label, not key ownership.
confirmNoSet true only when the user asked for exactly this action.
review_sha256YesExact preview_testimonial_batch hash for identical requests, profile label, schema and order.

TDQS

A4.1/5.0
Behavior5/5

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

Goes well beyond the annotations (destructive, non-idempotent, openWorld) by disclosing the failure contract: stop on first failure, return known results, failed index and unattempted indices, with no rollback and no retries. This is exactly the operational context an agent needs before committing a destructive batch.

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?

Three dense clauses with no filler; the scope constraint is front-loaded and the failure semantics follow. Slightly telegraphic phrasing, but 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?

For a destructive no-output-schema tool, the description covers preconditions, ordering, limits and the failure return shape (results/failed index/unattempted indices). It does not describe the success return, a minor gap given no output schema exists to cover 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 description coverage is 100%, so tasks/account/confirm/review_sha256 are already documented. The description restates the 1-20 bound and hash verification but adds no semantics beyond the schema, so the baseline of 3 applies.

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

Purpose4/5

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

Names the resource (testimonial/import/invite tasks) and the scope (one-to-twenty, ordered, confirmed), which distinguishes it from the singular create_testimonial/update_testimonial siblings and from preview_testimonial_batch. The execution verb itself is only implied by 'Confirmed' and the title, so it is clear but not fully self-contained.

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?

States the precondition clearly ('Prevalidate all and verify exact hash before first request') and the negative behaviors ('no retries, rollback or implicit continuation'), which tells the agent this is a one-shot commit after preview. It stops short of explicitly naming preview_testimonial_batch as the required prior step, though review_sha256 strongly implies it.

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

update_testimonialUpdate approval or tagsA
Destructive

Change only native approval status and tag additions/removals. Text, rating and customer edits remain dashboard-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountNoExact configured private account profile label; not a tenant or provider account ID.
confirmNoSet true only when the user asked for exactly this action.
payloadNoComplete native JSON body; do not mix with body flags or payload_file. Arrays use repeated JSON object flags or a whole native array in a private file.
add_tagsNo
approvedNo
remove_tagsNo
payload_fileNoAbsolute regular non-symlink JSON body file, at most 1 MiB. Cannot mix with payload/body flags.
testimonial_idYesExact testimonial ID. No slash, traversal or arbitrary URL.

TDQS

A3.5/5.0
Behavior3/5

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

Annotations already declare destructiveHint=true, idempotentHint=false, and openWorldHint=true, so safety profile is covered externally. The description adds a real behavioral constraint (mutations limited to native approval/tags), but says nothing about permissions, reversibility, or partial-failure behavior despite being destructive.

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, zero filler, with the scope restriction front-loaded before the exclusion. Well sized for a definition of this complexity.

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 an 8-parameter destructive mutation with nested payload objects and no output schema, the description covers only the field scope. It leaves the payload/body-flag/payload_file alternatives and the confirm safeguard entirely to the schema, which is adequate but thin.

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

Parameters2/5

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

Schema coverage is 63%, so the description should compensate for the rest, particularly the account/confirm/payload-vs-payload_file mechanics. Instead it only references the approval and tag fields that the schema already names, adding no semantics for the remaining parameters.

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

Purpose4/5

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

States a specific verb+resource (change approval status and tags on a testimonial) and narrows the scope to 'only native' fields. This is clear enough to distinguish it from create_testimonial/delete_testimonial, though it never names a sibling explicitly.

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 second sentence draws an explicit exclusion boundary: text, rating, and customer edits are dashboard-only, which tells the agent when this tool is NOT the right choice. It stops short of naming which sibling to call for other operations, so the routing guidance is one-sided.

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. 6 tool updatesv3.0.0
    • Changedcreate_testimonial16 fields changed
      • addedInput schema / $defs
        Added value: +{
        +  "integration": {
        +    "enum": [
        +      "twitter",
        +      "product_hunt",
        +      "google",
        +      "facebook",
        +      "reddit",
        +      "capterra",
        +      "g2",
        +      "linkedin",
        +      "app_store",
        +      "trustpilot",
        +      "shopify",
        +      "play_store",
        +      "yelp",
        +      "slack",
        +      "discord",
        +      "apple_podcasts",
        +      "telegram",
        +      "whatsapp",
        +      "instagram",
        +      "youtube",
        +      "tiktok",
        +      "appsumo",
        +      "amazon",
        +      "zillow",
        +      "udemy",
        +      "chrome_web_store",
        +      "airbnb",
        +      "skillshare",
        +      "realtor",
        +      "sourceforge",
        +      "whop",
        +      "wordpress",
        +      "fiverr",
        +      "homestars",
        +      "web_page"
        +    ],
        +    "type": "string"
        +  },
        +  "media": {
        +    "items": {
        +      "additionalProperties": false,
        +      "properties": {
        +        "alt": {
        +          "description": "",
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "type": {
        +          "enum": [
        +            "image",
        +            "video"
        +          ],
        +          "type": "string"
        +        },
        +        "url": {
        +          "description": "",
        +          "format": "uri",
        +          "minLength": 1,
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "url",
        +        "type"
        +      ],
        +      "type": "object"
        +    },
        +    "maxItems": 100,
        +    "type": "array"
        +  }
        +}
      • changedInput schema / properties / confirm / description
        Previous value: -"Must be true for the requested mutation or exclusive private output file."New value: +"Set true only when the user asked for exactly this action."
      • addedInput schema / properties / integration / $ref
        Added value: +"#/$defs/integration"
      • removedInput schema / properties / integration / enum
        Removed value: -[
        -  "twitter",
        -  "product_hunt",
        -  "google",
        -  "facebook",
        -  "reddit",
        -  "capterra",
        -  "g2",
        -  "linkedin",
        -  "app_store",
        -  "trustpilot",
        -  "shopify",
        -  "play_store",
        -  "yelp",
        -  "slack",
        -  "discord",
        -  "apple_podcasts",
        -  "telegram",
        -  "whatsapp",
        -  "instagram",
        -  "youtube",
        -  "tiktok",
        -  "appsumo",
        -  "amazon",
        -  "zillow",
        -  "udemy",
        -  "chrome_web_store",
        -  "airbnb",
        -  "skillshare",
        -  "realtor",
        -  "sourceforge",
        -  "whop",
        -  "wordpress",
        -  "fiverr",
        -  "homestars",
        -  "web_page"
        -]
      • removedInput schema / properties / integration / type
        Removed value: -"string"
      • addedInput schema / properties / media / $ref
        Added value: +"#/$defs/media"
      • removedInput schema / properties / media / items
        Removed value: -{
        -  "additionalProperties": false,
        -  "properties": {
        -    "alt": {
        -      "description": "",
        -      "minLength": 1,
        -      "type": "string"
        -    },
        -    "type": {
        -      "enum": [
        -        "image",
        -        "video"
        -      ],
        -      "type": "string"
        -    },
        -    "url": {
        -      "description": "",
        -      "format": "uri",
        -      "minLength": 1,
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "url",
        -    "type"
        -  ],
        -  "type": "object"
        -}
      • removedInput schema / properties / media / maxItems
        Removed value: -100
      • removedInput schema / properties / media / type
        Removed value: -"array"
      • addedInput schema / properties / payload / properties / integration / $ref
        Added value: +"#/$defs/integration"
      • removedInput schema / properties / payload / properties / integration / enum
        Removed value: -[
        -  "twitter",
        -  "product_hunt",
        -  "google",
        -  "facebook",
        -  "reddit",
        -  "capterra",
        -  "g2",
        -  "linkedin",
        -  "app_store",
        -  "trustpilot",
        -  "shopify",
        -  "play_store",
        -  "yelp",
        -  "slack",
        -  "discord",
        -  "apple_podcasts",
        -  "telegram",
        -  "whatsapp",
        -  "instagram",
        -  "youtube",
        -  "tiktok",
        -  "appsumo",
        -  "amazon",
        -  "zillow",
        -  "udemy",
        -  "chrome_web_store",
        -  "airbnb",
        -  "skillshare",
        -  "realtor",
        -  "sourceforge",
        -  "whop",
        -  "wordpress",
        -  "fiverr",
        -  "homestars",
        -  "web_page"
        -]
      • removedInput schema / properties / payload / properties / integration / type
        Removed value: -"string"
      • addedInput schema / properties / payload / properties / media / $ref
        Added value: +"#/$defs/media"
      • removedInput schema / properties / payload / properties / media / items
        Removed value: -{
        -  "additionalProperties": false,
        -  "properties": {
        -    "alt": {
        -      "description": "",
        -      "minLength": 1,
        -      "type": "string"
        -    },
        -    "type": {
        -      "enum": [
        -        "image",
        -        "video"
        -      ],
        -      "type": "string"
        -    },
        -    "url": {
        -      "description": "",
        -      "format": "uri",
        -      "minLength": 1,
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "url",
        -    "type"
        -  ],
        -  "type": "object"
        -}
      • removedInput schema / properties / payload / properties / media / maxItems
        Removed value: -100
      • removedInput schema / properties / payload / properties / media / type
        Removed value: -"array"
    • Changeddelete_testimonial1 field changed
      • changedInput schema / properties / confirm / description
        Previous value: -"Must be true for the requested mutation or exclusive private output file."New value: +"Set true only when the user asked for exactly this action."
    • Changedexport_testimonials1 field changed
      • changedInput schema / properties / confirm / description
        Previous value: -"Explicit approval for this exact requested ordered batch."New value: +"Set true only when the user asked for exactly this action."
    • Changedsend_invites12 fields changed
      • addedInput schema / $defs
        Added value: +{
        +  "recipients": {
        +    "items": {
        +      "additionalProperties": false,
        +      "properties": {
        +        "email": {
        +          "description": "",
        +          "format": "email",
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        "name": {
        +          "description": "",
        +          "minLength": 1,
        +          "type": "string"
        +        }
        +      },
        +      "required": [
        +        "email"
        +      ],
        +      "type": "object"
        +    },
        +    "maxItems": 100,
        +    "minItems": 1,
        +    "type": "array"
        +  }
        +}
      • changedInput schema / properties / confirm / description
        Previous value: -"Must be true for the requested mutation or exclusive private output file."New value: +"Set true only when the user asked for exactly this action."
      • addedInput schema / properties / payload / properties / recipients / $ref
        Added value: +"#/$defs/recipients"
      • removedInput schema / properties / payload / properties / recipients / items
        Removed value: -{
        -  "additionalProperties": false,
        -  "properties": {
        -    "email": {
        -      "description": "",
        -      "format": "email",
        -      "minLength": 1,
        -      "type": "string"
        -    },
        -    "name": {
        -      "description": "",
        -      "minLength": 1,
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "email"
        -  ],
        -  "type": "object"
        -}
      • removedInput schema / properties / payload / properties / recipients / maxItems
        Removed value: -100
      • removedInput schema / properties / payload / properties / recipients / minItems
        Removed value: -1
      • removedInput schema / properties / payload / properties / recipients / type
        Removed value: -"array"
      • addedInput schema / properties / recipients / $ref
        Added value: +"#/$defs/recipients"
      • removedInput schema / properties / recipients / items
        Removed value: -{
        -  "additionalProperties": false,
        -  "properties": {
        -    "email": {
        -      "description": "",
        -      "format": "email",
        -      "minLength": 1,
        -      "type": "string"
        -    },
        -    "name": {
        -      "description": "",
        -      "minLength": 1,
        -      "type": "string"
        -    }
        -  },
        -  "required": [
        -    "email"
        -  ],
        -  "type": "object"
        -}
      • removedInput schema / properties / recipients / maxItems
        Removed value: -100
      • removedInput schema / properties / recipients / minItems
        Removed value: -1
      • removedInput schema / properties / recipients / type
        Removed value: -"array"
    • Changedsubmit_testimonial_batch1 field changed
      • changedInput schema / properties / confirm / description
        Previous value: -"Explicit approval for this exact requested ordered batch."New value: +"Set true only when the user asked for exactly this action."
    • Changedupdate_testimonial1 field changed
      • changedInput schema / properties / confirm / description
        Previous value: -"Must be true for the requested mutation or exclusive private output file."New value: +"Set true only when the user asked for exactly this action."
  2. 12 tool updatesv2.0.0
    • First observedcreate_testimonial
    • First observeddelete_testimonial
    • First observedexport_testimonials
    • First observedget_operation_schema
    • First observedget_testimonial
    • First observedlist_accounts
    • First observedlist_links
    • First observedlist_testimonials
    • First observedpreview_testimonial_batch
    • First observedsend_invites
    • First observedsubmit_testimonial_batch
    • First observedupdate_testimonial

TDQS

A3.6/5.0

Scored across 12 tools

Disambiguation4/5

Most tools target clearly distinct resources or actions: single testimonial CRUD, listing, export, batch preview/submit, links, accounts, and schema introspection. Some overlap exists because submit_testimonial_batch can include testimonial/import/invite work that overlaps with create_testimonial and send_invites, but descriptions distinguish single versus batch workflows.

Naming Consistency5/5

All tools use snake_case and follow a predictable verb_noun pattern, including compound batch tools like submit_testimonial_batch and preview_testimonial_batch. Naming is consistent and readable throughout.

Tool Count5/5

The 12 tools are well scoped for a testimonial management surface covering CRUD, invites, export, batching, links, accounts, and schema inspection. No tool appears redundant, and the count fits comfortably within a reasonable MCP server size.

Completeness4/5

Core testimonial lifecycle operations are present: create, list, get, limited update, delete, export, invites, and batch processing. Notable gaps remain: update_testimonial only changes approval status and tags, and there is no create/update/delete for forms, widgets, or links, though the core testimonial workflows are covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables AI agents to fully automate Appwrite backend operations with 143 tools covering databases, users, storage, functions, messaging, and more. Supports advanced features like GeoJSON attributes, file uploads, function deployment, and bulk operations.
    3 npm
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes the full Chatwoot API as 129 tools for AI assistants, enabling account, contact, conversation, message, inbox, team, report, help center, automation, and custom attribute management, plus exclusive Kanban and scheduled message features.
    6 npm
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Enables AI agents and clients to discover accounts and channels, read scheduling and analytics data, and manage posts, content items, and templates through 41 GraphQL-backed tools with private credential profiles and confirmation-gated mutations.
    41
    22 npm
    AGPL 3.0