Skip to main content
Glama

ringg-mcp

A local MCP server exposing the Ringg AI voice-agent platform to Claude Code over stdio. Read assistants, knowledge bases and call history; make targeted edits to an assistant's prompt, custom variables and knowledge base attachments; transcribe audio with Ringg's Parrot speech-to-text.

Nothing in this server dials a phone. There are no tools for individual calls, campaigns, or call termination — by design.


Status: proof of concept, built and verified against a single test workspace. Not affiliated with or endorsed by Ringg AI.

Setup

Requires Node 20+.

git clone <this-repo>
cd RinggMCP
npm install
npm run build

Put your key in a gitignored .env at the project root:

printf 'RINGG_API_KEY=your-key-here\n' > .env

The repo ships a project-scoped .mcp.json, so opening this directory in Claude Code offers the server automatically. No secret goes in that file - the server reads .env. To register it globally instead:

claude mcp add ringg -- node /absolute/path/to/RinggMCP/dist/index.js

Environment

Variable

Required

Default

Purpose

RINGG_API_KEY

yes

Workspace API key, sent as X-API-KEY. Read from the environment or .env. Server exits 1 with a clear stderr message if unset.

RINGG_ENV_FILE

no

./.env

Path to an alternative env file. Real environment variables always win over the file.

RINGG_BASE_URL

no

https://prod-api.ringg.ai/ca/api/v0

API base. Must be https (localhost excepted).

RINGG_VERIFY_ON_START

no

0

1 probes GET /workspace at startup to validate the key. Off by default so startup stays network-free.

RINGG_LOG_LEVEL

no

info

error / warn / info / debug. All logging goes to stderr.

RINGG_TIMEOUT_MS

no

30000

Per-request timeout.

RINGG_STT_BASE_URL

no

https://prod-api.ringg.ai/stt/v1

Speech-to-text base. Same key; must be https.

RINGG_STT_TIMEOUT_MS

no

120000

Timeout for one transcription, upload included.

Get a key from the Ringg dashboard under Settings → API Key. It is shown only once at generation, and regenerating immediately revokes the previous key.

The key is read from the environment at startup, held only in the client's request headers, and never returned by a tool or written to a log. redact() scrubs it from every error message and log line.


Related MCP server: claudecode-mcp

Tools

Read

Tool

Endpoint

Notes

list_agents

GET /agent/all

Paginated. limit/offset always sent explicitly.

get_agent

GET /agent/{agent_id}

Prompt sections, custom variables, KB attachments (always an array), voice, languages, tools (by phase), classification labels, analysis config, A/B versions, plus a reference audit. include_prompt=false drops the section bodies.

list_knowledge_bases

GET /external/kb/all

Bare array upstream; no pagination.

get_knowledge_base

GET /external/kb/{kb_id}

Status plus the files / URLs / FAQs inventory.

list_calls

GET /calling/history

Summaries only — transcripts are stripped (see below).

get_call

GET /calling/call-details

view: summary | transcript | analysis | full.

list_calls deserves a note: the upstream history endpoint returns each call's full transcript inline on every row. This server strips it and reports has_transcript instead. Use get_call for actual transcript content.

get_call makes one upstream request; view selects send_analysis and projects the response:

view

send_analysis

Returns

summary

false

Metadata only

transcript

false

Metadata + conversation turns

analysis

true

Metadata + platform & client analysis

full

true

Everything

Speech-to-text

Tool

Endpoint

Notes

transcribe_audio

POST /stt/v1/transcriptions

Ringg's Parrot STT. A local file_path, or an https audio_url such as a recording_url from get_call.

Parrot is a separate Ringg service with its own base URL, authenticated by the same workspace key. It is built for Hindi, English and code-mixed speech: English words come back in Latin script, Hindi in Devanagari (आपका loan approve हो गया है). It returns one plain string, with no speaker labels and no timestamps — for a Ringg call, get_call view=transcript already has the turn-by-turn transcript, so this tool is for re-transcribing a recording independently or for audio from elsewhere. Transcribing changes nothing in the workspace, but it is billed per second of audio.

The service's limits were established against the live endpoint, and the tool enforces them before uploading anything:

  • 10,000,000 bytes per file (413 above that) — about 5 minutes of 16 kHz WAV, or 40 minutes of mono 32 kbps MP3. Larger files are refused with a compression hint.

  • WAV, MP3, FLAC and M4A only (415 otherwise) — not the OGG and OPUS the product page also lists. The service judges the format by file name or content type, never the bytes: a WAV named recording is refused, while M4A bytes named .wav go through. So the tool sniffs the container from the file's magic bytes and labels the upload to match. Anything not recognisably audio is refused rather than uploaded, so a mistyped path cannot send ~/.env to a remote service.

Also observed:

  • language is echoed back but neither validated (xx is accepted) nor, in testing, influential: Hindi audio sent as en came back identical. Omitted, it defaults to hi, and so does the tool.

  • enable_cap_punc toggles punctuation only; the text stays lower case either way.

  • Stereo is mixed to mono before recognition, so overlapping speech on the two channels of a call recording blurs together.

  • 8 kHz and µ-law WAV, the usual telephony formats, transcribe fine. A 5-second clip took 60–90 ms to process.

audio_url must be https, after redirects too. It is fetched with a plain request — the workspace key is attached only by RinggClient and never goes to another host — and capped at 10 MB while streaming, whatever Content-Length claims. The result echoes the URL without its query string, since on a signed link that is the credential. The upload is always named audio.<format>, so local file names stay local.

Real-time transcription (the SDK's WebSocket stream()) is not exposed: a tool call has no live audio to stream, and the REST endpoint covers recordings.

Write

All writes go through PATCH /agent/v1 with an operation discriminator.

Agent configuration

Tool

Operation

Semantics

update_agent_prompt

edit_prompt

Section-wise. Read-merge-write by default. Validated before every write — see Prompt reference grammar.

update_intro_message

edit_intro_message

Replaces the greeting outright. Warns when it is flattening dashboard rich text.

update_custom_variables

edit_custom_vars

add / remove deltas. Read-merge-write.

update_agent_display_name

edit_agent_display_name

Cosmetic rename. Agent-level.

Knowledge bases

Tool

Operation

Semantics

attach_knowledge_base

attach_kb

Additive; reports attachments before and after.

detach_knowledge_base

remove_kb

Detaches the one kb_id; reports attachments before and after.

Post-call analysis

Tool

Operation

Semantics

update_custom_analysis_prompt

edit_custom_analysis_prompt

Extraction prompt + typed keys. Read-merge-write; clear to remove.

update_client_analysis

edit_client_analysis

context / goal_key / keys / revenue. Merged upstream at the top level only.

update_classification_labels

edit_classification_labels

set / remove deltas, max 10. Read-merge-write. Agent-level.

update_analytics_context

edit_analytics_context

Tool-call-log switches. Deep-merged upstream.

A/B versions

Tool

Operation

Semantics

add_ab_version

add_new_ab_version

Clones a version; returns the new version_id. Starts with no traffic.

update_traffic_split

edit_traffic

{ version_id: share }, must sum to 1.0. Affects live calls.

toggle_ab_testing

toggle_ab_testing

On/off. Disabling needs one version and no calls in flight.

The endpoint's full contract — every operation, field and error code — is kept in docs/edit-agent-api.md. That file is the source of truth for this server's write path.

Writes are pinned to the version they were read from. Agent config lives on an agent version, and PATCH /agent/v1 accepts version_id. Each write tool sends the id of the version it read the current state from, so the merge and the write cannot land on different A/B variants. Every write tool takes an optional version_id to override that, and the KB tools take is_draft for editing a multi-node agent's draft.

update_custom_variables also takes config_type (outbound | inbound), which only means anything on an outbound_inbound agent — those keep two copies of the runtime config. It is ignored elsewhere, and the tool says so rather than silently dropping it.

The merge-based tools exist because the upstream API replaces the entire field on every write. update_custom_variables reads the current variable list, applies your add/remove as a set operation, and writes the whole list back, so variables you did not mention survive. update_agent_prompt does the same for prompt sections, matching by title; update_classification_labels and update_custom_analysis_prompt do the same for their maps. Each takes mode: "replace" to deliberately discard what you did not supply, and reports what that discarded.

Where the platform merges — edit_client_analysis (top level only) and edit_analytics_context (deep) — the tools pass through rather than re-merging, and update_client_analysis warns when a nested keys or revenue object you send would drop entries the stored one had.

Validation happens before the write, not after a 400. Traffic shares that do not sum to 1.0, a version id that does not belong to the agent, an analysis default with no matching key or the wrong type for its key, an 11th classification label, a blank label description — each is refused locally with a message naming the offending value, so no request is sent.

If update_agent_prompt cannot locate the agent's existing sections, it refuses to write rather than silently dropping sections it could not see.

update_custom_variables refuses to remove callee_name or mobile_number from an outbound agent. The platform rejects such a list with 403, so the tool fails locally with a message naming the variable instead of spending a round trip to be told no.

update_intro_message warns when it is about to flatten rich text. The dashboard stores the greeting as HTML, and custom variables appear there as mention spans (<span data-type="mention" data-id="{{callee_name}}">@{{callee_name}}</span>). edit_intro_message converts whatever it receives to text, so writing through this tool loses the editor's chip rendering — the variable references themselves keep working.

Out of scope, deliberately

Individual calls, campaigns, call termination, knowledge base create/edit/delete, number provisioning, telephony config, and workspace user management. scripts/check-stdout-purity.sh enforces that none of those endpoints appear in the code.

Two write operations are within reach but deliberately absent. delete_version and push_to_prod (reference section 4.5) archive versions and retire production config; they belong to a dashboard's confirm-dialog, not to a tool an agent can call in a loop. The flow-graph operations (sections 4.6–4.8) are absent for a different reason — see item 4b below: GET /agent/{id} does not return the node graph, so a tool could neither read node ids nor verify what it wrote.


Architecture

src/
  config.ts              Env load + fail-fast validation. Owns the API key.
  logger.ts              stderr-ONLY logger. No stdout code path exists here.
  ringg/
    client.ts            HTTP client: base URLs, X-API-KEY, timeout, error mapping
    errors.ts            Typed errors + secret redaction
    normalize.ts         Defensive readers for undocumented / inconsistent shapes
    agents.ts kb.ts calls.ts
    stt.ts               Speech-to-text: file/URL loading, format sniffing, size cap
  tools/
    types.ts registry.ts   ToolDefinition + the 20-tool registry
    agents/ kb/ calls/ stt/  One file per tool
  server.ts              createServer(deps) -> McpServer. Imports NO transport.
  transports/stdio.ts    The only stdio-aware file. Includes the stdout guard.
  index.ts               bin entrypoint

Transport lives at the edge only. server.ts, tools/ and ringg/ import no transport — a static check enforces this. Adding streamable HTTP later means adding src/transports/http.ts and a second entrypoint that calls createServer(); nothing under tools/ or ringg/ changes.

stdio safety

stdout carries the JSON-RPC stream, so a single stray write corrupts the session. Three layers guard it:

  1. guardStdout() in transports/stdio.ts redirects every console.* method to stderr before the transport connects — defusing a stray console.log in our code or any dependency.

  2. logger.ts has no stdout code path at all.

  3. scripts/check-stdout-purity.sh fails the build on any stdout write outside src/transports/, and scripts/smoke.mjs asserts every stdout line parses as JSON-RPC.

The server starts with no interactive prompts and never reads stdin outside the protocol. A missing RINGG_API_KEY produces one clear stderr line and exit code 1.


Verification

npm run check:stdout          # static guards: stdout purity, transport isolation, scope
npm run typecheck             # tsc --noEmit
npm run build

RINGG_API_KEY=... npm run probe          # live read-only probe; see below
RINGG_API_KEY=... npm run smoke          # protocol + tools/list
RINGG_API_KEY=... node scripts/smoke.mjs --live   # + live read-only tool calls, 1 s of STT

Fail-fast check — expect one stderr line, exit 1, and nothing on stdout:

env -u RINGG_API_KEY node dist/index.js

Run the probe before trusting the write tools

scripts/probe.mjs is read-only and makes no writes. It answers, empirically, the three questions the documentation does not:

  1. Where prompt sections actually live inside agent_config, and what their section_title values are.

  2. Which of the three documented custom-variable shapes GET /agent/{id} really returns.

  3. Whether knowledge base attachments come back singular or plural.

RINGG_API_KEY=... node scripts/probe.mjs
# or target a specific agent:
RINGG_API_KEY=... PROBE_AGENT_ID=<uuid> node scripts/probe.mjs

Raw payloads land in ./probe-output/ (gitignored). Fold anything surprising into src/ringg/normalize.ts.

Verification status

Run against a live workspace on 2026-09-02.

Area

Status

typecheck / build

✅ clean

Static guards (stdout, transport isolation, scope)

✅ 4/4

Protocol + live read tools (smoke.mjs --live)

✅ 25/25, re-run 2026-09-10 with transcribe_audio

transcribe_audio over stdio (2026-09-10)

✅ 27/27: WAV, M4A, FLAC, 8 kHz µ-law, extensionless file, https URLs, and every pre-upload refusal; key never sent to the audio host

Fail-fast: missing / empty key, bad base URL, empty env file

✅ exit 1, clear stderr, 0 bytes stdout

401 handling and key redaction

✅ actionable message, key never leaks

update_custom_variables read-merge-write

✅ live round trip on a throwaway agent

mergePromptSections logic

✅ verified against real 30k-char prompt data

attach_knowledge_base / detach_knowledge_base

✅ live round trip, including the blind-read case

update_agent_prompt live round trip on a single_node agent

✅ verified on a live A/B agent

The custom-variables round trip added two variables, removed one, confirmed the agent's original callee_name / mobile_number and the other new variable all survived, then restored the agent. A byte-level diff against the pre-test snapshot showed the agent identical apart from updated_at and tool_ids, which Ringg regenerates on every request.

The prompt round trip ran against a live A/B agent with three versions, each holding four sections of differing content (~45 KB on the target version alone). A marker was merged into one section and then reverted. Results:

  • The write landed on exactly the version the resolver had read (v3, the traffic-bearing one) — the other two versions were untouched. This settles the read-one/write-another hazard that motivated the version-targeting logic: PATCH /agent/v1 writes to the same version getActiveVersion() resolves, including when active_agent_version_id is null.

  • The three sections not named in the call survived byte-for-byte, as did the version's custom variables and knowledge base attachment.

  • After reverting, all three versions were byte-identical to the pre-test snapshot.

Section titles vary by agent template and are not a fixed vocabulary. Observed: Introduction and Objective · Response Guidelines · Task · FAQ Guidelines on one agent, and Introduction and Objective · Response Guidelines · Conversation Script · FAQs on another. Always call get_agent first to read the titles actually in use.


Prompt reference grammar

A prompt section is HTML from the dashboard's rich-text editor. Beyond prose it carries five constructs, each with its own syntax and its own failure mode. None of this is documented by Ringg; it was established by reading the prompts and mention-chip markup of every agent in a live workspace.

Construct

Literal form

Mention chip

Binds by

Custom variable

@{{name}}

data-source="custom_variable"

name

Pre-call tool data

@((tool_name.Dotted.Path))

data-source="api"

name + path

On-call tool

@||tool_name||

data-source="platform"

name

Knowledge base

@kb_name

data-source="knowledge_base"

data-id UUID only

Control flow

{% if %} / {% elif %} / {% else %} / {% endif %}

Three semantics drive most of the mistakes:

1. @(( )) is substituted as text before Jinja evaluates. Inside a {% %} statement it must therefore be quoted. Every working example in the wild reads {% if "@((t.Path))" == "True" %}; unquoted, Jinja parses the substituted text as an expression instead of comparing it as a string.

2. Booleans arrive as the strings "True" / "False" — and "False" is truthy in Jinja, so {% if "@((t.Flag))" %} is always true. Compare with == "True".

3. A knowledge base binds through the chip's UUID, never the visible text. Plain @kb_name typed as prose reads like a reference and does nothing.

Custom variables are the exception: they are native Jinja names, tested bare — {% if channel == 'meta' %}.

get_agent reports the full vocabulary an agent makes referenceable (tools split by phase, with each pre-call tool's legal response_keys), what its prompt currently references (prompt.references), and anything that will not resolve (prompt.issues).

include_prompt=false returns that vocabulary without the prompt bodies, substituting section_titles for sections. Prompt text is 78–96% of a large agent's payload — enough that 10 of the 18 agents in one live workspace produced a get_agent result too big to return through a tool call at all. Omitting the bodies brings 9 of those 10 under:

Agent

Full

include_prompt=false

test-seller-onboarding

131,773

18,809

test-agent-caching

96,024

12,883

Housing Agent

65,954

8,803

The audit is computed from the sections either way, so nothing diagnostic is lost — only the wording. Use it whenever you want the vocabulary rather than the prose, which is the normal case before a prompt write.

The one agent still over the line afterwards is over for unrelated reasons: its custom_analysis_prompt (34KB) and form_fields (27KB) are the bulk, not its prompt.

Checks run before every prompt write

update_agent_prompt validates the sections it is about to send and writes nothing if it finds a problem, returning each finding with a concrete fix. Problems in sections the call is not writing never block it; they come back as pre_existing_issues, so a legacy defect elsewhere cannot dead-end an unrelated edit. Set acknowledge_findings=true to write anyway, once the user has seen the findings.

Code

Severity

Catches

unbalanced_interpolation

error

a {{ or }} with no match

unbalanced_statement

error

a {% or %} with no match

unclosed_block / unexpected_block_end / orphan_branch

error

{% if %} without {% endif %}, stray {% endif %}, {% else %} outside a block

unquoted_field_in_jinja

error

@(( )) used unquoted inside {% %}

string_truthiness

warning

{% if "@((t.Flag))" %} — always true

unknown_custom_variable

warning

a variable the agent does not declare, in prose or in a condition

unknown_pre_call_tool / unknown_field_path

warning

a tool the agent lacks, or a path absent from its responseSelectedKeys

unknown_on_call_tool

warning

@||name|| for a tool the agent lacks

kb_not_attached

warning

a KB referenced by UUID but not attached

kb_reference_without_binding

warning

@kb_name as plain text, which binds to nothing

The platform's own validator is partial (see 4h below): it rejects an unclosed {% %} block and an unknown tag, but accepts an unclosed {{, a bad filter, and every reference error above — so these checks are the only guard for the rest.

A successful write returns a change report: per section, and per numbered step within it, what changed, with before/after excerpts windowed around the edit rather than the start of the step.

Run over the 18 agents of a live workspace, the checks left 14 clean and flagged only real defects: a prompt referencing three knowledge-base UUIDs that exist nowhere in the workspace on an agent with none attached, a stray }} left by an earlier edit, and a variable that was never declared.


Observed API behaviour

Notes gathered while building this server: first from docs.ringg.ai (llms.txt, skill.md, openapi.json and the relevant prose pages), then checked against the live API with scripts/probe.mjs. Where the two differed, the implementation follows the observed behaviour; those items are marked ✅ OBSERVED.

These are working notes for anyone integrating against the same endpoints, not a criticism of the documentation — APIs and their docs drift, and some of this may reflect newer platform features that the reference has yet to catch up with. Verified against one workspace in September 2026; your results may differ.

The most structurally important item is #0: agents are versioned, and the config fields live on the agent's active version rather than on the agent object itself.

Superseded in part. docs/edit-agent-api.md is a platform reference for PATCH /agent/v1 that post-dates these notes and settles several of them: version_id / is_draft / config_type are documented common fields, edit_event_subscriptions is real (#1), remove_kb takes a kb_id and detaches only that one (#3), and multi-node agents are edited through a separate family of flow operations (#4b). Items updated below say so inline. Everything still marked ✅ OBSERVED concerns the read path, which that reference does not cover.

0. Agents are versioned. ✅ OBSERVED

agents
  active_agent_version_id        null whenever is_ab_live is true
  ab_versions                    { <version_id>: { slug, description, call_traffic } }
  form_fields                    template builder inputs - NOT custom variables
  version_details
    <version_id>
      agent_config
        agent_prompt.prompt_sections   [{ section_title, section_content }]
        custom_variables               ["callee_name", "mobile_number", ...]
        intro_message
      knowledge_bases                  array - plural
      event_subscriptions
      language / voice / tools

The version layer is not described in the reference. Consequences, all handled in normalize.ts:

  • Which version is live is not always declared. When is_ab_live is true, active_agent_version_id is null and the live version is the ab_versions entry holding the call traffic. getActiveVersion() resolves by declared id, then by call traffic, then by sole version — and returns nothing when a real traffic split makes the choice ambiguous, rather than guessing. get_agent reports which version it read and on what basis.

  • This is a correctness issue, not tidiness: on a live A/B agent here, version v1 had a knowledge base attached and v2 had none. Reading the wrong version reports the wrong configuration.

  • Reading the agent root alone returns empty config for a good share of agents.

1. edit_event_subscriptions appears in prose but not in the OpenAPI spec. ☑️ SETTLED — docs/edit-agent-api.md section 4.3 documents the operation and its full payload (event_type, callback_url, headers, method_type, auth_fetch_config), so the OpenAPI omission was a spec gap. Webhook management remains out of scope here; the note below still describes what get_agent surfaces read-only.

Originally: webhooks/initial-setup.md, the body of endpoint/assistant/edit-assistant.md, and skill.md (3 places) all document operation: "edit_event_subscriptions". The operation enum in openapi.json does not contain it, and event_subscriptions appears zero times in the spec. The prose and the embedded OpenAPI block on the edit-assistant page differ on this point.

Observed: subscriptions are readable, at version_details.<active>.event_subscriptions — so a webhook tool would not have to write blind. Webhook management is out of scope for this server; get_agent surfaces the current subscriptions read-only.

2. custom_variables lives elsewhere than documented. ✅ OBSERVED

Source

Field

Shape

Docs: GET /agent/all

custom_variables

object map

Docs: GET /agent/{id}

form_fields

array of {key, value}

Live: GET /agent/all

custom_variables

absent entirely

Live: GET /agent/{id}

version_details.<active>.agent_config.custom_variables

array of plain strings — already the write shape

Write: PATCH /agent/v1

custom_variables

array of plain strings

form_fields is a separate concept. It holds the template builder's own inputs — agent_name, company_name, call_details, faq, intro_message — carrying values carrying values (an agent persona name, a company name). The call variables are separate values such as callee_name and mobile_number. Because edit_custom_vars replaces the whole list, writing form_fields keys back through it would overwrite the agent's real call variables — so normalize.ts never reads form_fields for this purpose, and get_agent returns the two as distinct fields.

3. Agent → knowledge base cardinality differs from the reference. ✅ OBSERVED GET /agent/{agent_id} is documented as returning a singular nullable knowledge_base_id. That field was not present in the responses observed. The field in use is version_details.<active>.knowledge_bases, an array, so an agent can hold more than one. Attach/detach are additive, and get_agent always returns an array.

☑️ docs/edit-agent-api.md section 4.2 confirms the write side: attach_kb and remove_kb both require a kb_id, and remove_kb detaches exactly that one. The detach tool no longer hedges about whether it might detach the whole set.

4. The prompt is not in the documented read schema. ✅ OBSERVED GET /agent/{agent_id} documents only agent_config as a bare object with no properties. Live, the prompt is at version_details.<active>.agent_config.agent_prompt.prompt_sections, The section_title values are not enumerated in the reference; those observed were:

Introduction and Objective · Response Guidelines · Task · FAQ Guidelines

extractPromptSections() targets the active version first and refuses to walk version_details blindly. ✅ A live round trip on an A/B agent confirmed that PATCH /agent/v1 writes to the same version the resolver reads. ☑️ The write path no longer relies on that: version_id is a documented field on every operation, so each write tool now sends the id of the version it read, making the agreement explicit instead of observed. update_agent_prompt refuses to merge when it cannot locate the sections.

Section titles are per-template, not a fixed set — two agents in this workspace use different ones. Read them with get_agent before writing.

4b. orchestration_mode changes the payload shape. ✅ OBSERVED Agents are either single_node (single prompt) or multi_node (multi-prompt). For a multi_node agent, GET /agent/{id} returns a much thinner payload: agent_prompt is null, and the node graph holding the actual script is not returned at all. edit_prompt does not reach that graph, so update_agent_prompt detects the mode and says so explicitly rather than reporting a vague "sections not found".

☑️ These agents are not uneditable, though — docs/edit-agent-api.md sections 4.6–4.8 document a whole family of flow operations (add_flow_node, edit_node_messages, edit_node_per_type_config, edges, per-node voice and DTMF overrides) that edit the graph node by node, against a version or its draft. This server does not expose them: they need the graph read back to be usable, and GET /agent/{id} does not return it. The tool now names them in its error so the limitation is attributable rather than mysterious.

4c. For multi-prompt agents, KB writes succeed but reads do not reflect them. ✅ OBSERVED version_details.<active>.knowledge_bases is absent entirely on a multi_node agent - yet attach_kb succeeds, and attaching twice returns 400 "Knowledge Base already attached", proving the platform tracks it. So the write path works while the read path shows nothing. An empty array here means unknown, not none. get_agent exposes knowledge_bases_readable, and the attach/detach tools return verified: false with an explanation instead of presenting a misleading [] → [] diff.

4d. The analysis and A/B fields sit in four different places. ✅ OBSERVED Inspected across 18 agents while building the analysis and A/B tools:

Field

Where it actually lives

Shape

intro_message

version_details.<v>.agent_config.intro_message

HTML, not text

custom_analysis_prompt

version_details.<v>.custom_analysis_prompt

{ prompt, keys: { name: "string" } }

client_analysis

version_details.<v>.client_analysis

{ keys: { name: { type, default, description } } }

analytics_context

version_details.<v>.analytics_context and …agent_config.analytics_context

{ platform_analytics, client_analytics }

classification_labels

agent root

{ label: description }

ab_versions

agent root

{ <version_id>: { slug, description, call_traffic } }

Three things worth knowing:

  • The two analysis fields use different key shapes. custom_analysis_prompt.keys is flat ({ "amount": "number" }); client_analysis.keys is nested ({ "amount": { type, default, description } }). They read as siblings and are not.

  • analytics_context was found in two places on the same agent, and on one agent in only one of them. readVersionField() tries the version, then agent_config, then the root, and reports which answered.

  • intro_message comes back as dashboard HTML, including mention spans that render a custom variable as a chip. The reference says edit_intro_message converts input to text; live it does the opposite. Plain text in is wrapped in <p>, and each {{variable}} is upgraded into the dashboard's mention markup; HTML in is stored as given (<b> normalised to <strong>). A round trip is byte-identical, so there is no formatting to lose. What is worth catching is that a reference to a variable the agent does not declare is accepted silently and interpolates to nothing on a call, so update_intro_message checks the names against custom_variables instead.

4e. edit_traffic merges; it does not replace. ✅ OBSERVED This one costs real money if you miss it. Given versions v1 and v2 split 0.5/0.5, sending traffic_split: { v1: 1.0 } does not move v2 to zero — v2 keeps its 0.5, and the agent's total becomes 1.5. The sum-to-1.0 rule is checked against the payload, not against the resulting state, so a partial split silently produces an invalid one. Deleting a version does not reclaim its share either: v1 was left holding 1.5.

update_traffic_split therefore sends an explicit share for every version the agent has, filling omitted ones with 0, and reports the resulting total.

4f. client_analysis.goal_key must name a key declared boolean. ✅ OBSERVED Undocumented, enforced with 400 "client_analysis.goal_key must reference a boolean key in client_analysis.keys". The tool checks it locally against the keys the call establishes.

4g. client_analysis cannot be unset. ✅ OBSERVED edit_client_analysis rejects both null (422 'client_analysis' is required) and {} (422 a non-empty object is required). Once the field is populated it stays populated; the closest to unset is { context: null, goal_key: null, keys: {}, revenue: {} }. Worth knowing before setting it on an agent you care about.

4h. Jinja validation is real but partial. ✅ OBSERVED edit_prompt and edit_intro_message reject an unclosed {% ... %} block and an unknown tag, naming the section and the construct. They accept an unclosed {{ name, a bad filter ({{ name | }}) and {{{{ }}. So a template error can still reach a live call — the validation is not a safety net.

5. Pagination defaults disagree. api-overview.md says "default 20, max 100". The spec's /agent/all limit carries default: 10 while its own description on the same parameter says "default: 20, max: 100". Mitigation: always send limit/offset explicitly.

6. GET /calling/history has stale hardcoded date defaults2025-09-25T00:00:00+05:30 and 2025-09-25T23:59:59+05:30 are baked into the spec as default. ✅ Verified that omitting both returns 200 with current results, so this server omits them unless supplied.

Also verified: total comes back as the string "many", not a number, so it is passed through as number | string rather than silently dropped.

7. call_type is documented as a history filter but is not a parameter. The prose says "Add filters such as agent_id, status, call_type, or bulk_list_id"; call_type is only a response field. Not exposed as a filter.

8. transcription_url is a JSON string, not a URL or an array. ✅ OBSERVED The spec types it as an array of {bot?, user?} turns. Live, it is a JSON-encoded string containing an array of {bot|user, message_id, timestamp, ...} objects. This server parses the string and maps it to a transcript array of {speaker, text, timestamp}.

9. Two agent-update endpoints exist. PATCH /agent/v1 (documented) and PATCH /public/agent/{agent_id} (in the spec, absent from llms.txt). AGENTS.md says explicitly not to treat the latter as public. This server uses PATCH /agent/v1 only.

10. Language enums diverge. /agent/v1 allows gu-IN and ar-AE; /public/agent allows bn-IN and ka-IN instead. ka-IN is Georgian and is almost certainly a typo for kn-IN, which already appears in the same enum.

11. skill.md cites two endpoints that do not exist: POST /agent/create (the real path is POST /public/agent) and operation: "edit_agent" (not in the enum).

12. KB file limits contradict. key-concepts/knowledge-base.md says up to 4 files at 512 MB each; llms.txt and the create-KB endpoint say 2 MB per file, 5 MB total, max 10 files and 20 URLs.

Items 0, 2, 3, 4 and 8 are the ones most likely to trip up an integration written straight from the reference; #2 is the one to be most careful with, since the write replaces the whole field. Items 1, 7, 10 and 11 are cosmetic. If you are integrating against Ringg, these are worth confirming against your own workspace — and worth raising with their team, who may well have newer guidance.

Contributing / running it yourself

Everything here is workspace-agnostic — no workspace ids, agent ids or keys are committed. To run against your own Ringg workspace:

  1. npm install && npm run build

  2. Put your own key in .env (RINGG_API_KEY=...). It is gitignored; never commit it.

  3. npm run probe — read-only. Confirms the shapes described in "Observed API behaviour" still hold for your workspace, and writes raw payloads to probe-output/ (also gitignored). Those payloads contain live customer data; delete them when done.

  4. node scripts/smoke.mjs --live — exercises the read tools end to end, and transcribes one second of generated silence.

Before testing the write tools, snapshot the target agent and use a disposable one: every write replaces the whole field upstream.

The write tools have been exercised end to end against a disposable single-node agent: every one of the 13 was run, its effect confirmed by reading the agent back, and the agent restored from a snapshot afterwards. Items 4d–4h above are what that run turned up. One field could not be restored — see 4g.

License

MIT — see LICENSE.

Available Tools

10 tools
attach_knowledge_baseAttach a knowledge base to an agentA
Idempotent

Attach a knowledge base to an agent so it can answer from those documents during calls. An agent may hold more than one knowledge base, so this is additive and does not replace existing attachments. The result reports the agent's attachments before and after. Note: for multi-prompt agents the Ringg API accepts the attachment but does not report attachments back, so the result will say the outcome could not be verified. Use list_knowledge_bases to find a kb_id.

ParametersJSON Schema
NameRequiredDescriptionDefault
kb_idYesThe knowledge base UUID, from list_knowledge_bases.
agent_idYesThe agent's UUID.

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already mark this as a non-read-only, idempotent, non-destructive operation, and the description adds useful behavioral detail: the result reports attachments before and after, and for multi-prompt agents the API accepts the attachment but cannot verify it. This goes beyond what annotations alone convey and helps the agent interpret the response.

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

Conciseness5/5

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

The description is front-loaded with the core purpose, followed by important behavioral semantics and the prerequisite lookup step. Each sentence adds necessary information, and nothing is redundant or filler.

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

Completeness5/5

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

The description covers the purpose, the additive behavior, the response shape, an important API edge case, and how to find a required parameter. With no output schema present, this level of detail is sufficient for an agent to invoke the tool correctly.

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

Parameters3/5

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

The input schema already documents both agent_id and kb_id, and the schema description coverage is 100%. The description reinforces that kb_id comes from list_knowledge_bases, but it does not meaningfully add new semantic detail beyond the schema.

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

Purpose5/5

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

The description uses a specific verb and resource combination: 'Attach a knowledge base to an agent' and states the intended effect ('so it can answer from those documents during calls'). It also clarifies the additive nature ('does not replace existing attachments'), which differentiates it from the sibling detach_knowledge_base without needing to name it.

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

Usage Guidelines4/5

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

The description provides clear context: the tool is for adding an attachment, is additive, and does not replace existing attachments. It also tells the agent to use list_knowledge_bases to find a kb_id. It does not explicitly say 'use detach_knowledge_base instead when removing an attachment,' but the additive guidance makes the intended selection clear enough.

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

detach_knowledge_baseDetach a knowledge base from an agentA
DestructiveIdempotent

Detach a knowledge base from an agent. This only removes the association - the knowledge base itself and its documents are untouched. The result reports the agent's attachments before and after. Note: for multi-prompt agents the Ringg API accepts the change but does not report attachments back, so the result will say the outcome could not be verified.

ParametersJSON Schema
NameRequiredDescriptionDefault
kb_idYesThe knowledge base UUID to detach.
agent_idYesThe agent's UUID.

TDQS

A4.3/5.0
Behavior5/5

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

The description goes well beyond the annotations by explaining the non-destructive nature of the operation, stating that the result reports attachments before and after, and disclosing the multi-prompt agent caveat where the API may accept the change but cannot verify it. This is genuinely useful behavioral context not available from annotations or schema.

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

Conciseness5/5

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

The description is three sentences with no filler. The primary action is front-loaded, the key limitation is stated immediately after, and the important edge case is saved for the final note. Every sentence earns its place.

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

Completeness5/5

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

For a simple two-parameter detach operation, the description covers the action, the side effects, the result shape, and the main verification caveat. The absence of an output schema is mitigated by the description explicitly saying what the result reports.

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 two parameters are clearly documented in the schema. The description does not add new parameter-level meaning, but it does not need to since the schema already handles parameter semantics adequately.

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

Purpose5/5

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

The description uses a specific verb and resource: 'Detach a knowledge base from an agent.' It also clarifies the scope by stating it 'only removes the association' and that the knowledge base and its documents remain untouched, distinguishing it from deletion or other destructive operations.

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

Usage Guidelines3/5

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

The intended use is clear from the title and first sentence, but the description does not explicitly mention when to use this tool versus alternatives such as attach_knowledge_base or list-related tools. The usage context is implied rather than stated with explicit exclusions.

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

get_agentGet agent configurationA
Read-only

Get the full configuration of one Ringg agent: prompt sections, custom variable names, attached knowledge bases, voice, languages, and tools. Knowledge base attachments are always returned as an array. The prompt object reports where its sections were located in the payload, or states plainly that none were found.

ParametersJSON Schema
NameRequiredDescriptionDefault
agent_idYesThe agent's UUID, as returned by list_agents.

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already mark readOnlyHint=true, and the description adds genuinely useful behavioral details beyond that: knowledge base attachments are always returned as an array, and the prompt object either reports where its sections were located or explicitly states none were found. This gives the agent concrete expectations about response shape and edge-case behavior.

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

Conciseness5/5

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

Two tightly written sentences front-load the core purpose, then add two high-value behavioral clarifications. Every sentence earns its place with no filler or repetition of schema details.

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

Completeness5/5

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

For a simple single-parameter read tool with no output schema, the description fully covers what the caller receives: the configuration components, the guaranteed array shape for knowledge base attachments, and the prompt object's fallback behavior. Nothing necessary for correct invocation or interpretation is missing.

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?

There is only one parameter, agent_id, and the input schema already fully documents it as the agent's UUID returned by list_agents (100% schema description coverage). The description does not add parameter-specific detail, so the baseline score of 3 is appropriate.

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

Purpose5/5

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

The description opens with a specific verb and resource: 'Get the full configuration of one Ringg agent,' and enumerates the exact contents (prompt sections, custom variables, knowledge bases, voice, languages, tools). The singular 'one' clearly distinguishes it from list_agents, and the domain terms separate it from knowledge-base-only tools.

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

Usage Guidelines4/5

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

The description makes the usage context clear: use this when you need the full configuration for a single agent. It does not explicitly name alternatives or state when not to use it, but the singular scope and enumerated fields give a strong implicit boundary against sibling list tools.

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

get_callGet call detailsA
Read-only

Get one call by id. The 'view' parameter controls how much is returned: 'summary' (default) is metadata only; 'transcript' adds the conversation turns; 'analysis' adds Ringg's platform analysis and any custom client analysis; 'full' returns everything. Prefer the narrowest view that answers the question - transcripts and analysis payloads can be large. Recording URLs expire 24 hours after the call.

ParametersJSON Schema
NameRequiredDescriptionDefault
viewNosummary | transcript | analysis | full. Defaults to summary.summary
call_idYesThe call UUID, as returned by list_calls.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already mark the operation as read-only, and the description goes beyond them by explaining view behavior, defaults, and the 24-hour expiration of recording URLs. This adds meaningful context about response scope and data freshness.

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?

Well-structured, front-loaded purpose, and every sentence adds value. The view explanation, usage guidance, and expiration warning are all relevant and efficiently written.

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

Completeness5/5

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

For a two-parameter read-only tool, the description covers invocation, parameter semantics, output scope, and a key data-freshness concern. No output schema exists, but the view descriptions sufficiently describe what is returned.

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

Parameters4/5

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

Schema coverage is 100%, so the schema documents both parameters, but the description enriches the view enum with concrete meanings (metadata, turns, analysis, full) and advises on size trade-offs. This is more than a bare restatement of the schema.

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

Purpose5/5

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

States the exact operation ('Get one call by id') and clearly differentiates itself from list_calls by focusing on a single call. The view parameter is explained precisely, making the tool's purpose unmistakable.

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

Usage Guidelines4/5

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

Provides explicit guidance on selecting the narrowest view and warns about large transcript/analysis payloads. It does not explicitly contrast with sibling tools like list_calls, but the 'by id' phrasing makes the primary use case clear.

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

get_knowledge_baseGet knowledge base detailsA
Read-only

Get one knowledge base: its name, processing status, timestamps, and the inventory of files, URLs and FAQs it contains. Check the status before attaching it to an agent - an untrained knowledge base will not answer questions during calls.

ParametersJSON Schema
NameRequiredDescriptionDefault
kb_idYesThe knowledge base UUID, from list_knowledge_bases.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true. The description adds valuable behavioral context: it warns that an untrained knowledge base will not answer questions during calls, which explains why the user should check status. No contradiction with annotations.

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

Conciseness5/5

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

Two sentences, no filler. The core purpose is front-loaded and the usage warning is actionable. Every word earns its place.

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

Completeness5/5

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

With no output schema, the description names the key returned fields (name, status, timestamps, inventory). It also gives the practical context for when to call. For a single-parameter read-only tool, nothing essential is missing.

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 kb_id parameter is already documented with provenance ('from list_knowledge_bases'). The description does not add parameter-specific meaning beyond the schema, which earns the baseline 3.

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

Purpose5/5

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

States a specific verb and resource: 'Get one knowledge base', and enumerates the returned contents (name, processing status, timestamps, inventory of files/URLs/FAQs). This clearly distinguishes it from list_knowledge_bases and the attach/detach siblings.

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

Usage Guidelines4/5

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

Explicitly tells the agent to check the status before attaching the knowledge base to an agent, providing a concrete use case. It does not name alternatives or exclusions, but the context is clear enough for routing.

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

list_agentsList agentsA
Read-only

List the voice assistants (agents) in the Ringg workspace. Returns id, display name, type, template info, call count and custom variable names for each. Use get_agent for the full configuration of one agent, including its prompt and knowledge base attachments.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoNumber of agents to return (1-100).
offsetNoNumber of agents to skip, for paging.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true and openWorldHint=true, covering the safety profile. The description adds value by specifying the returned fields (id, display name, type, template info, call count, custom variable names) and the workspace scope, which goes beyond what annotations provide.

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

Conciseness5/5

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

Two sentences with no filler: the first states the core listing behavior and return shape, the second gives a targeted alternative. The key scoping and differentiation are front-loaded, making the description efficient and easy to parse.

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

Completeness5/5

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

For a simple, read-only list tool with an output schema absent, the description adequately covers return fields, scope, and the main alternative. Pagination parameters are fully documented in the schema, so nothing needed for correct invocation is missing.

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 both parameters include their own descriptions in the schema. The tool description adds no additional parameter semantics, so the baseline of 3 applies as the schema fully carries the meaning.

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

Purpose5/5

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

Description states a specific verb and resource: 'List the voice assistants (agents) in the Ringg workspace.' It also differentiates itself from get_agent by mentioning the other tool returns full configuration, so an agent can select the correct tool without confusion.

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

Usage Guidelines5/5

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

The description explicitly tells the agent to use get_agent for a single agent's full configuration, providing a clear when-to-use alternative. This is direct sibling routing, leaving no ambiguity about the scope of list_agents.

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

list_callsList call historyA
Read-only

List calls from the Ringg workspace with optional filters. Returns SUMMARIES ONLY - status, duration, cost, agent and timestamps. Transcripts and recording URLs are deliberately omitted here; use get_call for a specific call's transcript or analysis. Dates must be ISO 8601 with a timezone offset, e.g. 2026-08-01T00:00:00+05:30.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoNumber of calls to return (1-100).
offsetNoNumber of calls to skip, for paging.
statusNoOnly return calls with this status.
agent_idNoOnly return calls handled by this agent.
end_dateNoLatest call date, ISO 8601 with offset. Omitted entirely if not supplied.
start_dateNoEarliest call date, ISO 8601 with offset. Omitted entirely if not supplied.
bulk_list_idNoOnly return calls from this campaign / bulk list.

TDQS

A4.9/5.0
Behavior5/5

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

Beyond the readOnlyHint annotation, the description discloses key behavioral traits: it returns 'SUMMARIES ONLY' with specific fields, and states that transcripts and recording URLs are 'deliberately omitted.' It also imposes an ISO 8601 timezone-offset requirement for dates. These are meaningful behavioral details not present in annotations or schema.

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

Conciseness5/5

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

The description is four sentences, each earning its place: scope, return content, exclusion with alternative routing, and date format requirement. It is front-loaded with the primary purpose and contains no filler.

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

Completeness5/5

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

For a list tool with 7 optional parameters, no required parameters, and no output schema, the description covers what an agent needs: response content summary, field list, exclusions, date format, and a pointer to the sibling for deeper data. Pagination and defaults are already described in the schema. Nothing essential is missing.

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

Parameters4/5

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

Schema description coverage is 100%, so every parameter is already documented. The tool description adds value by framing all parameters as optional and by giving a concrete date format example, but it does not introduce new per-parameter semantics beyond what the schema already provides. This is slightly above the baseline of 3.

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

Purpose5/5

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

The description states a specific verb and resource: 'List calls from the Ringg workspace with optional filters.' It differentiates itself from the sibling get_call by explicitly noting that transcripts and recording URLs are omitted and that get_call should be used for those, making the tool's scope unambiguous.

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

Usage Guidelines5/5

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

The description gives explicit when-to-use guidance: it says this tool returns summaries only and that get_call is the alternative for a specific call's transcript or analysis. It also provides the required date format with an example, so an agent knows exactly how to supply date filters.

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

list_knowledge_basesList knowledge basesA
Read-only

List every knowledge base in the Ringg workspace, with id, name, type and creation time. Use get_knowledge_base for the document inventory of one, and attach_knowledge_base to associate one with an agent. This server cannot create, edit or delete knowledge bases.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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

The annotations already mark the tool as readOnlyHint=true, and the description reinforces this by stating the server cannot create, edit, or delete knowledge bases. It adds the workspace-level scope and expected return fields, though it does not mention pagination or error behavior; for a zero-parameter read-only list, this is a minor gap.

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

Conciseness5/5

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

The description is three sentences, each earning its place: it front-loads the core listing behavior, then provides alternative tool routing, then states the capability limitation. There is no filler or redundancy.

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

Completeness5/5

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

For a zero-parameter listing tool with no output schema, the description supplies the scope, the returned fields, alternative routing, and the read-only constraint. Nothing essential for invoking or interpreting this tool correctly is missing.

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

Parameters4/5

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

The tool has zero parameters, so there are no parameter semantics to document beyond the schema. The description does not need to add parameter details, and the baseline for a 0-parameter tool applies.

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

Purpose5/5

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

The description uses the explicit verb 'List' with a specific resource ('knowledge bases'), scopes it to the Ringg workspace, and names the returned fields (id, name, type, creation time). This clearly distinguishes it from get_knowledge_base, which targets a single knowledge base.

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

Usage Guidelines5/5

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

The description explicitly routes the agent to get_knowledge_base for a specific knowledge base's document inventory and to attach_knowledge_base for associating one with an agent. This gives clear when-to-use guidance and differentiates the workspace-level listing purpose.

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

update_agent_promptUpdate agent promptA
DestructiveIdempotent

Update an agent's prompt section by section. In the default 'merge' mode the tool reads the agent's current prompt, overwrites only the sections you name (matched by title), preserves every other section, and writes the complete section list back - the upstream API replaces the whole prompt, so the merge is what protects the sections you did not mention. Use mode='replace' to set the prompt to exactly the sections you supply, discarding the rest. Call get_agent first to see the existing section titles.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNo'merge' (default) keeps existing sections you did not name. 'replace' discards every section you did not supply.merge
agent_idYesThe agent's UUID.
sectionsYesSections to write. Each replaces the full content of the matching section.

TDQS

A4.9/5.0
Behavior5/5

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

The description goes well beyond the annotations by revealing the read-modify-write behavior, explaining that the upstream API replaces the whole prompt, and clarifying how merge protects unmentioned sections. The destructive nature of replace mode is explicitly disclosed, which is critical and additive context.

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

Conciseness5/5

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

The description is dense yet efficient, front-loading the core purpose and then explaining modes with necessary nuance. Every sentence contributes, and the critical safety guidance about the merging behavior is placed up front.

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

Completeness5/5

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

For a mutation tool with no output schema and a potentially destructive replace mode, this description is complete. It covers preconditions, mode semantics, what gets preserved versus discarded, and the API behavior that motivates the merge. An agent has enough information to call the tool correctly and safely.

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?

Since schema description coverage is 100%, the baseline is 3, but the description adds value by explaining the behavior of merge mode and how it relates to the sections parameter. It also reinforces the case-insensitive matching behavior and the need to call get_agent, which supports correct parameter usage beyond the raw schema.

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

Purpose5/5

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

The description names a specific verb and resource ('Update an agent's prompt section by section') and immediately distinguishes the two operational modes. This clearly differentiates the tool from sibling tools like get_agent and list_agents, which are read-only and not prompt modification tools.

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

Usage Guidelines5/5

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

The description explicitly explains when to use merge mode versus replace mode and instructs the agent to call get_agent first to see existing section titles. This gives concrete, actionable guidance for correct tool selection and invocation.

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

update_custom_variablesUpdate agent custom variablesA
DestructiveIdempotent

Add or remove custom variable names on an agent. The upstream API replaces the entire variable list on every write, so this tool reads the agent's current variables, applies your add/remove as a set operation, and writes the complete merged list back - variables you do not mention are preserved. Custom variables are names only (e.g. 'loan_amount'); values are supplied per call or per campaign row. Prompts reference them as @{{variable_name}}.

ParametersJSON Schema
NameRequiredDescriptionDefault
addNoVariable names to add. Already-present names are ignored.
removeNoVariable names to remove. Names that are not present are ignored.
agent_idYesThe agent's UUID.

TDQS

A4.4/5.0
Behavior5/5

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

The description discloses a non-obvious upstream behavior: the API replaces the entire variable list on every write, so the tool performs a read-modify-write merge that preserves unmentioned variables. This goes well beyond the annotations, explaining why the operation is idempotent and destructive in specific ways. It also clarifies that variables are names only and how prompts reference them.

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?

Three dense sentences, each adding distinct value: the action, the critical read-modify-write behavior, and the naming/value semantics. No filler, no repetition of schema fields, and the most important behavioral caveat is front-loaded.

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 three-parameter update tool with no output schema, the description covers the key behavioral details needed to invoke it correctly: the merge behavior, preservation of unspecified variables, and the meaning of custom variables. It does not describe return values or error conditions, but those are less critical given the annotations and schema coverage.

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

Parameters4/5

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

The schema already documents all parameters with useful descriptions, including the 'already-present names are ignored' behavior. The tool description adds meaning by explaining that variables are names only, values are supplied elsewhere, and prompts reference them via @{{variable_name}}. This enhances an agent's understanding of add and remove beyond the raw schema.

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

Purpose5/5

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

The description opens with a specific verb and resource: 'Add or remove custom variable names on an agent.' This clearly identifies the action and target, and differentiates the tool from siblings like update_agent_prompt or attach_knowledge_base. The scope (agent custom variables) is immediately unambiguous.

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

Usage Guidelines3/5

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

The description makes the operational context clear: use this tool to manage an agent's custom variable names. However, it does not explicitly say when to choose this over alternatives or when not to use it, such as when updating prompts or knowledge bases. Usage is implied rather than stated.

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. 10 tool updatesv0.1.0
    • First observedattach_knowledge_base
    • First observeddetach_knowledge_base
    • First observedget_agent
    • First observedget_call
    • First observedget_knowledge_base
    • First observedlist_agents
    • First observedlist_calls
    • First observedlist_knowledge_bases
    • First observedupdate_agent_prompt
    • First observedupdate_custom_variables

TDQS

A4.6/5.0

Scored across 10 tools

Disambiguation5/5

Each tool maps clearly to a distinct resource/action pair: list/get for agents, knowledge bases, and calls; update for prompt/variables; attach/detach for knowledge base relationships. The list vs get descriptions explicitly distinguish summaries from full detail, so there is no real ambiguity.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern: list_*, get_*, update_*, attach_*, detach_*. The two update tools are further differentiated by their target, update_agent_prompt and update_custom_variables, keeping naming predictable throughout.

Tool Count5/5

Ten tools is a well-scoped count for the domain of Ringg agent configuration, knowledge base association, and call retrieval. Each tool serves a useful purpose without redundancy or unnecessary surface area.

Completeness4/5

Core workflows are covered: listing and reading agents, knowledge bases, and calls, updating agent prompts and custom variables, and attaching/detaching knowledge bases. Minor gaps remain around lifecycle operations like creating or deleting agents, and agent voice/languages/tools are not updatable through this set.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers