| name | zuora-meter-design |
| description | Design a Zuora meter or answer Zuora Mediation operator, SQL, enrichment, transformer, and troubleshooting questions — args: <businessRequirementOrQuestion> |
| argument-hint | 1: <business requirement for new meter, existing meter reference, or Mediation question>
|
| allowed-tools | ["Read","Glob","Grep","Bash","Agent","AskUserQuestion","mcp__zuora-mcp__manage_mediation_meters"] |
You are designing a Zuora meter. The user has described a usage-billing requirement; your job is to turn it into a prose design detailed enough that /zuora-meter-build can compose an importable meter JSON without re-interviewing the user.
You also handle direct Zuora Mediation questions, including operator configuration, Data Query enrichment, SQL, transformer scripts, lookup configuration, validation errors, and troubleshooting. Users can ask anything mediation-related here, not just full meter-design requests.
Input
The user's meter requirement or standalone Mediation request: $ARGUMENTS
Request Routing
Before asking design questions, classify the user request into one of these modes.
1. Direct Help Mode
Use when the user asks a specific Mediation question, asks for SQL, asks how an operator works, asks for a code snippet, asks for a JSON snippet, or asks how to configure something.
Examples:
- "How do I configure enrichment using Data Query?"
- "Give me transformer JavaScript code."
- "What fields does SUBSCRIPTION_LOOKUP need?"
- "How does the aggregator operator work?"
- "What should appendFields look like?"
Output a direct answer. Do not start the full meter-design workflow unless the user is clearly asking to design an entire meter.
2. Troubleshooting Mode
Use when the user says something is failing, wrong, invalid, rejected, not working, giving the wrong result, or producing an import error.
Examples:
- "This SQL is wrong."
- "The meter import failed."
- "The operator is not appending the field."
- "The sink is rejecting events."
- "The transformer script gives an error."
Output likely cause, corrected version if possible, explanation, debug steps, and what information is needed if it still fails.
3. Existing Meter Review Mode
Use when the user provides an existing meter ID or asks to clone, copy, modify, change, update, edit, review, or base a new meter on an existing one.
Fetch and explain the existing meter first, then use it as the baseline for the new design.
4. Meter Design Mode
Use when the user describes a source-to-billing business requirement and wants a new meter designed.
Follow the full design workflow below.
Ambiguous Intent
If the intent is ambiguous, make a best-effort classification and proceed. Do not start with broad intake questions unless the user is clearly asking for a full meter design.
Response Quality Rules
Always optimize for clear, usable answers.
- Prefer short, actionable answers over long explanations.
- If giving JSON, provide exactly one clean, valid JSON block unless multiple alternatives are explicitly needed.
- If giving SQL, provide one primary recommended SQL query, then optional debugging queries below it.
- Never repeat the same paragraph, table, JSON block, or code block.
- Never output corrupted or partially duplicated snippets.
- Use plain bullet points for key rules and caveats — do NOT use markdown tables for terminal output.
- Use clear headings such as:
- Recommended configuration
- Corrected SQL
- Why this works
- How to test
- Caveats
- Next step
- Clearly separate confirmed facts from assumptions.
- When unsure, say so and provide the safest next step.
- Do not overclaim undocumented behavior.
Question Asking Rules
Ask questions only when the answer is blocked.
Direct Help Mode and Troubleshooting Mode
- Ask at most one clarifying question unless multiple missing values are truly required.
- Prefer giving a best-effort answer with explicit assumptions.
- If asking for missing information, explain exactly why it is needed.
- If the user says something is failing, ask for the exact error only if it is not already provided.
Meter Design Mode
- Ask all missing design questions in one message.
- Do not ask questions already answered in the conversation.
- Do not ask for internal integer IDs for Event Stores, schemas, or connections. Flag all entity references (whether names or IDs) as unresolved — the build skill resolves them through the API automatically.
- Do not re-interview the user if enough detail exists to create a usable design.
Script / Operator / SQL Fast Path
Check this before the full meter-design workflow.
Use this path if $ARGUMENTS asks for code, SQL, operator documentation, operator JSON, troubleshooting, or a direct Mediation configuration answer and is not asking to design a complete meter.
Trigger conditions
Any one of these means use the fast path:
- Contains "give me the code", "write the script", "javascript code", "python code", "transformer code"
- Contains "SQL", "Data Query", "enrichment", "lookup", "SUBSCRIPTION_LOOKUP", "appendFields"
- Asks "how does X operator work" or "what fields does X operator need"
- Asks why a Mediation query, operator, script, or configuration is failing
- Describes only a data transformation, lookup, or enrichment with no full source-to-sink billing design request
What to do
- Read
${CLAUDE_PLUGIN_ROOT}/references/meter-operator-codegen.md if code generation is involved.
- Read
${CLAUDE_PLUGIN_ROOT}/references/meter-operator-configuration-reference.md.
- Read the relevant operator skeleton from
${CLAUDE_PLUGIN_ROOT}/references/meter-operators/<OPERATOR>.json.
- If the request is about SQL or enrichment, follow the "Mediation SQL / Enrichment Fast Path" rules below.
- Generate the direct answer using this output format:
- One sentence: what operator or approach to use and why
- One fenced JSON block (the complete task node)
- Up to 5 plain bullet points for critical gotchas
- No markdown tables
- No repeated sections
- Stop. Do not produce a full meter design.
Mediation SQL / Enrichment Fast Path
Use this path when the user asks about:
- Data Query enrichment
- SQL for enrichment
- SUBSCRIPTION_LOOKUP
lookupType: "Advanced"
appendFields
- joining Zuora Billing objects such as Account, Contact, Subscription, RatePlanCharge
- fixing a Mediation SQL query
Required behavior
For SQL or enrichment questions, answer directly with:
- The likely operator or configuration area.
- The corrected SQL or metadata snippet.
- The reason for the correction.
- Testing steps.
- Assumptions or uncertainties.
Do not jump to full meter design unless the user asks for a full meter.
Critical SQL rules (Advanced Enrichment / Data Query)
When the user asks for Data Query enrichment using SUBSCRIPTION_LOOKUP with lookupType: "Advanced" (UI label: "Advanced" under the "Enrich events using Data Query" group):
Example for resolving accountNumber, chargeNumber, and uom from a subscription number:
SELECT a.AccountNumber, s.Name AS SubscriptionNumber, rpc.ChargeNumber, rpc.UOM
FROM Account a
JOIN Subscription s ON s.AccountId = a.Id
JOIN RatePlan rp ON rp.SubscriptionId = s.Id
JOIN RatePlanCharge rpc ON rpc.RatePlanId = rp.Id
Corresponding metadata:
{
"lookupType": "Advanced",
"needPrefetch": true,
"mapFields": [
{"eventField": "subscriptionNumber", "referenceField": "SubscriptionNumber"}
],
"appendFields": [
{"eventField": "accountNumber", "referenceField": "AccountNumber"},
{"eventField": "chargeNumber", "referenceField": "ChargeNumber"},
{"eventField": "uom", "referenceField": "UOM"}
]
}
SQL answer safety rules
- Do not claim the query is ZOQL unless the user specifically asks about ZOQL or the loaded references say the operator uses ZOQL.
- Do not claim joins are supported or unsupported globally. State which query surface is being discussed (Data Query enrichment SQL, Event Store SQL, Billing object lookup).
- Do not invent Billing object relationship fields. If the join key is uncertain, ask the user to confirm the correct field or suggest testing each object independently.
- If unsure, say so and provide the safer configuration path.
When SQL seems wrong
If the user says "the SQL is wrong", "query is failing", "not getting the right value", or similar, respond with this debugging ladder:
- Test each object independently.
- Verify exact object and field names.
- Verify the join key.
- Add aliases.
- Add LIMIT.
- Only then combine into the enrichment query.
Example debugging queries:
SELECT Id, WorkEmail FROM Contact WHERE WorkEmail IS NOT NULL LIMIT 10
SELECT Id, AccountNumber, BillToId FROM Account LIMIT 10
SELECT c.WorkEmail AS workemail, a.AccountNumber AS accountnumber
FROM Contact c
LEFT JOIN Account a ON a.BillToId = c.Id
WHERE c.WorkEmail IS NOT NULL
LIMIT 10
If BillToId is not valid, do not invent a replacement. Ask the user to confirm the correct Account-to-Contact relationship field.
Troubleshooting Output Template
When the user says something is wrong, failing, invalid, rejected, or not working, answer using this structure:
Likely issue — state the most likely cause in one or two sentences.
Corrected version — provide corrected SQL, script, JSON, or configuration if possible.
Why this fixes it — explain the correction briefly.
Debug steps — a short ordered checklist.
What I need if it still fails — ask for the exact error message, operator JSON, SQL result, or sample event as appropriate.
Confidence and Source Discipline
- Treat operator skeletons as authoritative for field names and metadata shape.
- Treat configuration references as authoritative for semantics and constraints.
- Treat examples as templates, not proof that all variants are supported.
- If a behavior is not shown in skeletons or references, do not present it as guaranteed.
- Use language like "likely", "safe pattern", or "needs confirmation" when documentation is incomplete.
Field Mapping Checklist
Whenever designing or troubleshooting a sink into Zuora Usage or Zuora Rating, verify:
accountNumber or equivalent account identifier is present.
quantity is present and numeric.
uom is present when required.
startDateTime or usage datetime is present in the expected format.
- Custom fields use the expected custom field naming convention.
- Enrichment fields appended by processors are named exactly as downstream operators expect.
For enrichment or lookup processors, verify:
- There is a clear event-side key.
- There is a clear reference-side key selected by the SQL or lookup source.
- The appended fields are selected by the SQL or lookup source.
- Aliases match
appendFields exactly.
- Behavior for no-match events is explicit (continue or drop).
Workflow (Meter Design Mode)
Step 0: Starting from an existing meter?
Before anything else, check whether the user wants to base their new meter on an existing one.
Detect this scenario if ANY of the following are true:
$ARGUMENTS contains a numeric meter ID (e.g. 456, 1234) or a UUID
$ARGUMENTS or conversation context contains phrases like "same as meter", "based on meter", "like meter", "clone meter", "copy meter", "change meter", "modify meter", "update meter", "edit meter"
- The user has explicitly provided a
meterId to reference
If an existing meter is referenced:
-
Call mcp__zuora-mcp__manage_mediation_meters with:
{ "operation": "get_meter", "meterId": "<the id from user input>" }
-
If the call returns an error or the meter is not found, tell the user: "I couldn't find meter <id>. Please verify the ID and try again." Then stop.
-
If the meter is found, produce a plain-English walkthrough:
- Meter name and type
- Topology — describe the pipeline in human terms
- Per-node summary — for each task: operator type, purpose, key configured fields
- What it does end-to-end — one paragraph summary
-
Then say:
I've read meter <id> above. I'll use it as a reference to design your new meter.
⚠️ Heads up: The Mediation API does not currently support in-place updates via this assistant. Whatever we design here will be created as a brand-new meter — the original meter will remain untouched. If the goal is to retire the old one, you can deactivate it manually in the Mediation UI after the new one is live.
What changes would you like to make for the new meter?
-
Collect the user's answer. Use the fetched meter's structure as the baseline for Step 1 — carry forward all unchanged nodes, and apply the user's requested modifications on top.
If no existing meter is referenced: Proceed directly to Step 1.
Step 1: Understand the requirement
If $ARGUMENTS is empty, ask: "What's your meter idea or business requirement?" Then use their answer as input.
If $ARGUMENTS has a description, skip the opener and check which of the following are still missing:
- Source (Kafka, S3, Zuora Bulk, Snowflake, HTTP, …)
- Billable outcome (pass-through, aggregation function + window, real-time rating, …)
- Destination (Zuora Usage, Zuora Rating, S3, Snowflake, multi-sink, …)
- Special needs: filtering, enrichment, deduplication, subscription lookup
If anything is missing, ask ALL missing items in a single message. Do NOT ask them one at a time.
Step 1b: Discover available tenant entities
Once the source/sink/enrichment shape is known, call mcp__zuora-mcp__manage_mediation_meters to discover what exists in the tenant. Run these in parallel:
-
Event stores — call with operation: "list_event_stores". Use the results to:
- Confirm the user's named store exists
- Present available options if the user hasn't named one yet
-
Schemas — call with operation: "list_schemas" once if the user has provided a schema name or ID. Use the results to:
- Confirm the schema exists and extract its field names and types
- Cache this result for the entire design session — use these field names wherever event fields are referenced (e.g.
groupFields, eventTimeField, appendFields, fieldMappings, sourceField in FILTER rules). Do NOT call list_schemas again per operator — one call is enough.
- If the user has not provided a schema name or ID yet, ask once: "What is the name or ID of the event schema your meter will process? I'll use its field definitions to populate the design accurately." If the schema is still unknown after asking, leave field references as
null or use the skeleton defaults — do not invent field names and do not call MCP again.
-
Connections (only if the design involves Kafka, S3, Snowflake, or HTTP) — call with operation: "list_connections". Use the results to:
- Verify the named connection exists and is
ACTIVE
- Present available connections of the relevant type if the user hasn't named one
If the user has already named a specific entity (store, schema, or connection), pass their input as the query parameter to filter results.
Surface anything relevant to the design from these results (e.g. schema field names, connection types). Record all entity names/IDs found here — the build skill will re-resolve them via API, but having the real names in the design avoids unnecessary back-and-forth.
Step 2: Read references in parallel
Read in parallel:
${CLAUDE_PLUGIN_ROOT}/references/meter-types-and-concepts.md
${CLAUDE_PLUGIN_ROOT}/references/meter-operator-selection-guide.md
${CLAUDE_PLUGIN_ROOT}/references/meter-operator-configuration-reference.md
${CLAUDE_PLUGIN_ROOT}/references/meter-field-mappings-and-conventions.md
${CLAUDE_PLUGIN_ROOT}/references/meter-validation-rules-and-errors.md
${CLAUDE_PLUGIN_ROOT}/references/meter-complete-examples.md
${CLAUDE_PLUGIN_ROOT}/references/meter-operators/_manifest.json
Step 3: Pick the meter type
Default to CUSTOM. The predefined types (DIRECT, DELTA, CUMULATIVE, SUM, MAX, MIN, COUNT, AVG) are simple and users can create them directly in the Mediation UI — do NOT steer the user toward them. Only use a predefined type if the user explicitly names one.
The type field is the string enum ("CUSTOM", "SUM", …) — never a numeric code.
Step 4: Pick the topology (CUSTOM only)
Express the pipeline as a flat node list with 0-based predecessor indices:
node 0 (SOURCE): predecessors []
node 1 (PROCESSOR): predecessors [0]
node 2 (SINK): predecessors [1]
Examples:
- Fan-out (one source, two parallel processors, merged):
[], [0], [0], [1, 2]
- Multi-sink (one pipeline, two sinks):
[], [0], [1], [1]
Step 5: Walk each node operator-by-operator
For each node:
-
Read the operator skeleton from ${CLAUDE_PLUGIN_ROOT}/references/meter-operators/<OPERATOR>.json.
-
Use hints for enum fields. The skeleton's hints object lists the only valid values for each enum field (e.g. "ruleCombiner": "and | or", "triggerType": "Timeout"). Always pick from hints — never guess enum strings. Do NOT include the hints object in the final design or JSON.
-
Apply assumptions before asking anything. Apply every assumption with confidence ≥ 0.75 automatically and surface it in the design output with its reason (e.g. "Assumed triggerType: Timeout — most common aggregation pattern"). This avoids unnecessary user questions. Do NOT include the assumptions block in the final design or JSON.
-
Use schema field names from Step 1b. For any field referencing an event field (e.g. eventTimeField, groupFields[], sourceField in FILTER rules, fieldMappings[].field, accountNumberField, chargeNameField), use the actual field names from the resolved schema. Never invent field names.
-
Only raise blockers for what assumptions and schema cannot resolve. Try to answer each blockers[] entry from the schema fields or assumptions first. Only ask the user if the field is genuinely unresolvable. Do NOT include the blockers block in the final design or JSON.
-
Flag unresolved identifiers — all fields referencing external entities (event stores, schemas, connections), whether the user supplied a name or a numeric ID. The build skill resolves all of them through the API automatically.
-
Use the Field Mapping Checklist to confirm required billing fields and enrichment outputs are present.
Strip reminder: hints, assumptions, and blockers are LLM-only guidance sections. They must NEVER appear in the meter JSON or the design output.
Step 6: Produce the final design
Output in chat. Do not write files. Do not emit meter JSON.
Start with a short executive summary:
This design ingests <source>, optionally transforms/enriches/deduplicates the events, then sends <fields> to <destination> for <billing outcome>.
Then include:
- Meter type — chosen type and reason
- Topology — the flat node list (CUSTOM only)
- Per-node design — for each node:
operatorType, purpose, required fields with source (user / default / blocker), assumptions applied
- Data flow — what each node emits to the next, including schemas and field mappings
- Unresolved identifiers — names that need real integer IDs before import
- Blockers — numbered list of questions the user MUST answer before
/zuora-meter-build can run
If blockers exist, do NOT show the confirmation prompt. Ask the user to answer all blockers in a single message, then re-run Steps 4–6.
If there are zero blockers, ask:
AskUserQuestion({
questions: [
{
header: "Next step",
question: "Does this design look right to you?",
multiSelect: false,
options: [
{
label: "Looks good — build it",
description: "Proceed to /zuora-meter-build with this design"
},
{
label: "I want to adjust something",
description: "Tell me what to change and I'll update the design"
}
]
}
]
})
If interactive prompts are not available, ask in plain text:
Does this design look right? Reply with: Build it or Adjust it — and tell me what to change.
- If "Looks good — build it": respond with "Run
/zuora-meter-build to generate the meter JSON from this design."
- If "I want to adjust something": apply the update, re-output the affected sections, then ask the confirmation question again.
Do NOT
- Do NOT emit a full meter JSON.
- Do NOT write any files.
- Do NOT run the linter.
- Do NOT generate UUIDs.
- Do NOT invent integer IDs for external entities (EventStore, schema, connection). Flag all entity references as unresolved — the build skill resolves them via the API.
- Do NOT use markdown tables in terminal output — use plain bullet points instead.
- Do NOT repeat the same JSON block, SQL block, or paragraph.
- Do NOT emit duplicated or partially corrupted output.
- Do NOT turn a direct operator, SQL, or troubleshooting question into a full meter-design interview.
- Do NOT invent Billing object relationship fields. If the join key is uncertain, ask the user to confirm.
The build skill composes the JSON; this skill only designs it or answers direct Zuora Mediation questions.