Setup recurring
Configure a recurring cost-reduction agent through a progressive Q-and-A wizard. Each call merges the latest answer into a session (keyed on session_id) and either asks the next missing question or, once all answers are in, emits policy.yaml plus the chosen scheduler manifest (Kubernetes CronJob, GitHub Actions workflow, or crontab + wrapper script) with step-by-step apply instructions.
Example
You
set up a weekly 30% savings tick
Log10x
Wizard Q1: which services should the policy target?
Pass target_services: [] for all, or a named list.
Re-invoke with session_id: "recurring-lx8a-9k2pq3".
More to ask
- "recurring policy: payments and checkout, 40%, daily 3am UTC"
- "emit a k8s CronJob that trims 25% off cart-svc weekly"
- "schedule a GitHub Actions tick every 6h against acme/log10x-config"
Prerequisites
Reporter deployed (metrics drive the per-pattern planner that the tick invokes). A reachable gitops repo or local path for policy.yaml, and credentials for the chosen scheduler (kubectl access for k8s_cron, repo write for github_actions, host shell for crontab).
Schema and samples
Input example
Representative call (synthetic, not captured from the live demo env).
Input schema
Agent-facing JSON Schema (the canonical shape the MCP server publishes via tools/list):
{
"type": "object",
"properties": {
"session_id": {
"type": "string",
"description": "Wizard session handle. Omit on the first call — a new session is minted and returned. Pass it back unchanged on every subsequent call."
},
"target_services": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "Services the policy targets. Empty array = all services. Use service names from `log10x_services`. The wizard asks if omitted."
},
"target_percent": {
"type": "integer",
"minimum": 1,
"maximum": 95,
"description": "Desired savings target, as a percentage of current log volume (1-95). Default: 30. The CLI's per-pattern planner works backward from this target."
},
"schedule": {
"anyOf": [
{
"type": "string",
"enum": [
"daily-03utc",
"every-6h",
"every-12h",
"every-24h-localtz"
]
},
{
"type": "string",
"pattern": "^[\\d*,\\-\\/\\s]+$"
}
],
"description": "Tick schedule. Presets: daily-03utc (default), every-6h, every-12h, every-24h-localtz. Or pass a raw 5-field cron expression (e.g. \"0 5 * * 1\")."
},
"scheduler": {
"type": "string",
"enum": [
"k8s_cron",
"github_actions",
"crontab"
],
"description": "Where the recurring tick runs. k8s_cron = Kubernetes CronJob (default when kubectl reachable), github_actions = GHA workflow, crontab = crontab + wrapper script."
},
"config_plane": {
"type": "string",
"minLength": 1,
"description": "Gitops repo URL (e.g. https://github.com/acme/log10x-config) or local path where the recurring CLI reads policy.yaml and writes updated cap CSVs."
},
"exceptions": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"description": "Service names the policy must never touch (audit / regulatory / executive). Optional — defaults to empty. Pass [] to explicitly clear all exceptions."
},
"min_delta_pp": {
"type": "integer",
"minimum": 0,
"maximum": 50,
"description": "Minimum change in savings percentage points required before the tick commits a new CSV. Default: 2. Set to 0 to commit on every tick."
},
"env_id": {
"type": "string",
"description": "Log10x environment ID. Auto-detected from LOG10X_ENV_ID when absent."
},
"namespace": {
"type": "string",
"description": "Kubernetes namespace for the CronJob. Default: log10x. Only used when scheduler=k8s_cron."
},
"secret_name": {
"type": "string",
"description": "Name of the Kubernetes Secret holding LOG10X_API_KEY. Default: log10x-secret. Only used when scheduler=k8s_cron."
},
"confirm": {
"type": "boolean",
"description": "Set to true to confirm the configuration and emit the artifacts. The wizard asks for confirmation interactively when omitted."
}
},
"additionalProperties": false
}
Source: src/tools/setup-recurring.ts.
Output example
Representative envelope (synthetic, not captured from the live demo env). view: "summary" returns the full StructuredOutput with typed data. Long YAML bodies trimmed for readability; the real call returns them in full.
Headline (the 1-line agent-facing answer):
Recurring policy emitted: log10x-cronjob.yaml + policy.yaml ready
{
"schema_version": "1.0",
"schema_epoch": "2026-05-25",
"tool": "log10x_setup_recurring",
"view": "summary",
"summary": {
"headline": "Recurring policy emitted: log10x-cronjob.yaml + policy.yaml ready"
},
"data": {
"mode": "emit",
"ok": true,
"session_id": "recurring-lx8a-9k2pq3",
"policy_yaml": "target_services:\n - payment\n - checkout\ntarget_percent: 30\nschedule: daily-03utc\nmin_delta_pp: 2\n...",
"scheduler_manifest": "apiVersion: batch/v1\nkind: CronJob\nmetadata:\n name: log10x-recurring\n namespace: log10x\nspec:\n schedule: \"0 3 * * *\"\n...",
"scheduler_manifest_filename": "log10x-cronjob.yaml",
"apply_instructions": "### Apply instructions: Kubernetes CronJob\n\n1. Commit `policy.yaml` to the root of your gitops repo...",
"markdown": "## Recurring cost-reduction policy: artifacts ready\n\n| Field | Value |\n|---|---|\n| Services | payment, checkout |\n...",
"human_summary": "Recurring policy emitted for 2 services targeting 30% savings via k8s_cron. Artifacts: policy.yaml + log10x-cronjob.yaml. Apply instructions are in data.apply_instructions. Run log10x_commitment_report after the first week to verify realized savings."
},
"actions": [
{
"tool": "log10x_doctor",
"args": {},
"reason": "confirm the environment is healthy before the first tick runs",
"role": "optional-followup"
}
],
"truncated": false,
"warnings": []
}
Output schema
The data block inside the StructuredOutput envelope. The shape is a discriminated union on mode: next_question while the wizard is still gathering answers, emit once all questions are answered.
type ToolData =
| {
mode: 'next_question';
ok: false;
session_id: string;
question_id:
| 'target_services'
| 'target_percent'
| 'schedule'
| 'scheduler'
| 'config_plane'
| 'exceptions'
| 'confirm';
markdown: string;
human_summary: string;
}
| {
mode: 'emit';
ok: true;
session_id: string;
policy_yaml: string;
scheduler_manifest: string;
scheduler_manifest_filename: string;
apply_instructions: string;
crontab_wrapper_script?: string;
markdown: string;
human_summary: string;
};
Envelope-level fields the agent should also read: summary.headline (1-line answer), actions[] (next-call chain hints as {tool, args, reason} — required-next while questions remain, optional-followup pointing at log10x_doctor after emit), truncated: boolean, images[] (PNG attachments where applicable), schema_epoch (engine-ID stability boundary).