Keelen

Autonomous dev team steered from chat: plain-English requests in, tested merged PRs out.

Should I use this

Quality & Safety

A
Description quality
100%
Schema completeness
72%
Naming quality
93%
Poisoning risk
100%
Permission match
100%
Protocol compliance
100%

Findings (1)

  • LOWTool 'resolve_platform_policy_conflict' name length outside 3-30 rangein resolve_platform_policy_conflict

Based on automated analysis of tool definitions and protocol compliance.

Context Cost

~12,615Tokens (tool definitions)
~1.1 KBTypical response size
Significant attention impact (9.86% of 128k context)

This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.

Install

One-Click Install

Add this to your `claude_desktop_config.json` file:

{
  "mcpServers": {
    "keelen": {
      "url": "https://keelen.ai/mcp"
    }
  }
}

Remote endpoints

https://keelen.ai/mcpstreamable-http

What it can do

Tool inventory

Tools (41)

🟢 Read-only🟡 Write🔴 Delete⚪ Unknown
🟢list_projects

List the projects in this workspace, with what each one is building.

Input Schema

{
  "type": "object",
  "properties": {},
  "title": "list_projectsArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "items": {
        "additionalProperties": true,
        "type": "object"
      },
      "title": "Result",
      "type": "array"
    }
  },
  "required": [
    "result"
  ],
  "title": "list_projectsOutput"
}
🔴create_project(name, build_description, project_kind, private, preview_command, ...)

Start building something new: creates a GitHub repo and begins work on it. Use this ONLY when the user wants a NEW repo scaffolded. If they already have a repo, use import_project(repo_full_name) instead — this tool would create a second, empty one beside theirs (list_github_repos() browses what the workspace can see). Scaffolds a new GitHub repo, a bootstrap-mode project, and submits `build_description` as the project's first Roadmap Request. `name` is a concise GitHub short repo slug (no owner); `project_kind` is REQUIRED and one of library | node_library | python_library | service | cli | web_app | godot_game | roblox_game; `preview_command` is required iff `project_kind == 'web_app'`. `engine` is OPTIONAL — one of claude_code | codex | glm | kimi | grok (defaults to claude_code); codex, glm, kimi, and grok require the workspace to have a matching connected credential. `org` is OPTIONAL — a GitHub organization login to create the repo inside (e.g. your company org); omit it to land the repo on a member's personal account. `private` defaults to True. `ci_runs_on` is OPTIONAL — the CI runner labels for the scaffolded workflow, e.g. ["self-hosted", "linux", "x64", "my-fleet"]. Omit it to inherit the workspace default (ubuntu-latest if unset). Labels no registered org runner carries are rejected, because GitHub would queue such a job forever rather than fail it. `framework` is OPTIONAL and `web_app`-only — one of vite | next (defaults to vite). It picks the scaffolded frontend rails: `vite` a vanilla-TypeScript SPA, `next` a Next.js app-router app. Passing it with any other `project_kind` is an error. The repo is created on the GitHub account of a workspace member with repo-create OAuth access (this path has no specific caller user), so the returned `repo` owner is whichever member's token resolved (or the chosen `org`). If no member has repo-create access — or the resolving member can't create in `org` — the call returns an actionable error. Returns {project_id, repo, thread_id, next_action, poll_after_seconds, next_step}; follow next_step (poll get_request_status with the returned thread_id). On the rare arm where the first Request failed to submit, next_action is "call_tool" with next_tool="submit_request". If scaffolding fails after the repository has been created, best-effort compensation deletes that just-created repository so a retry can reuse the name — this tool can therefore remove external state it created moments earlier.

Input Schema

{
  "type": "object",
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "build_description": {
      "title": "Build Description",
      "type": "string"
    },
    "project_kind": {
      "title": "Project Kind",
      "type": "string"
    },
    "private": {
      "default": true,
      "title": "Private",
      "type": "boolean"
    },
    "preview_command": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Preview Command"
    },
    "org": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Org"
    },
    "engine": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Engine"
    },
    "ci_runs_on": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Ci Runs On"
    },
    "framework": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Framework"
    }
  },
  "required": [
    "name",
    "build_description",
    "project_kind"
  ],
  "title": "create_projectArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "create_projectOutput"
}
🟡submit_request(project_id, text, workflow_profile)

Ask for a change in plain English: a feature, a bug fix, or a new direction. Submit ONE feature or intent per call; split a multi-feature ask into separate requests. `text` must be under 16000 characters. Returns {thread_id, status, next_action, poll_after_seconds, next_step}; follow next_step (re-check get_request_status after poll_after_seconds).

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "text": {
      "title": "Text",
      "type": "string"
    },
    "workflow_profile": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Workflow Profile"
    }
  },
  "required": [
    "project_id",
    "text"
  ],
  "title": "submit_requestArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "submit_requestOutput"
}
🟢get_request_status(project_id, thread_id)

Check what happened to a request, and read any questions it asked back. Intake is async (~5min cadence) - poll periodically. Read `next_action`: "wait" (still processing), "answer_questions" (call answer_request with one answer per question), "done" (see generated_roadmap_item_ids), "cancelled" (terminal, no items), "failed" (see intake_failure_reason).

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "thread_id": {
      "title": "Thread Id",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "thread_id"
  ],
  "title": "get_request_statusArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_request_statusOutput"
}
🔴set_product_vision(project_id, vision_md)

Say what this product is for, so every run knows what it is building toward. `vision_md` is free-form markdown and must be non-empty. Tenant-scoped: a project not in the caller's workspace 404s. Returns {project_id, product_vision_md, updated_at, next_step}.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "vision_md": {
      "title": "Vision Md",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "vision_md"
  ],
  "title": "set_product_visionArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "set_product_visionOutput"
}
🔴set_product_goal(project_id, goal_md)

Set the outcome to aim at right now, so work is prioritised toward one thing. `goal_md` is free-form markdown and must be non-empty. Tenant-scoped: a project not in the caller's workspace 404s. Returns {project_id, product_goal_md, updated_at, next_step}.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "goal_md": {
      "title": "Goal Md",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "goal_md"
  ],
  "title": "set_product_goalArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "set_product_goalOutput"
}
🟢list_roadmap(project_id, status)

See what is planned for a project and in what order. Queued rows also say whether the expand picker would elect them (`electable`) and, when not, why it skips them (`skip_reason`: crash_capped | noop_capped | crash_cooldown | noop_cooldown | awaiting_intake_thread | held_on_open_pr | other). A skipped item ahead of yours does not delay it.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "status": {
      "default": "queued",
      "title": "Status",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "list_roadmapArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "items": {
        "additionalProperties": true,
        "type": "object"
      },
      "title": "Result",
      "type": "array"
    }
  },
  "required": [
    "result"
  ],
  "title": "list_roadmapOutput"
}
🟡reorder_roadmap(project_id, ordered_ids)

Change what gets built first. `ordered_ids` is the desired front-to-back order of queued roadmap-item ids (get them from list_roadmap). The first id becomes the highest priority — the cadence expands the lowest-priority_int queued item next. Horizon pins still dominate: a pinned-later item stays at the back and a pinned-now item at the front, regardless of position in `ordered_ids`. Ids that are unknown or no longer queued are skipped; duplicates are rejected. Returns {updated, queue, next_step}.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "ordered_ids": {
      "items": {
        "type": "string"
      },
      "title": "Ordered Ids",
      "type": "array"
    }
  },
  "required": [
    "project_id",
    "ordered_ids"
  ],
  "title": "reorder_roadmapArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "reorder_roadmapOutput"
}
🔴cancel_roadmap_item(project_id, item_id)

Cancel / close a single roadmap item (get its id from list_roadmap). Use this to close a DELIVERED or duplicate item that keeps re-parking: when the work already shipped, every expand produces no dev-ready tasks and files a recurring `roadmap_item_parked` escalation you have to keep acking. Cancelling drops the item out of the expand queue AND resolves any open expand-lane escalation for it. Business-level replay guard: an already expanded/cancelled item changes no state, and the dashboard can restore a cancelled item. Refuses (409) while the item is actively being expanded (retry once that iter ends). Returns {id, status, changed, next_step}.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "item_id": {
      "title": "Item Id",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "item_id"
  ],
  "title": "cancel_roadmap_itemArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "cancel_roadmap_itemOutput"
}
🔴clear_horizon_pin(project_id, item_id)

Clear a queued roadmap item's horizon pin (now/next/later → none). A horizon pin dominates the queue sort, so `reorder_roadmap` cannot move a pinned item out of its band — a stale `now` pin on a delivered/duplicate item clogs the front of the queue. This unpins it and reprices the queue so the item follows plain priority order again (and reorder_roadmap can then move it). Only queued items carry a settable pin (in-flight / shipped items derive theirs), so this refuses (422) on a non-queued item — use cancel_roadmap_item to close a delivered item. Returns {id, previous_pin, horizon_pin, next_step}.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "item_id": {
      "title": "Item Id",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "item_id"
  ],
  "title": "clear_horizon_pinArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "clear_horizon_pinOutput"
}
🔴rollback_roblox_place(project_id, version_number)

Roll a Roblox project's place back to a previously-published version. For a `roblox_game` project, re-publishes the RETAINED build artifact for `version_number` (get published versions from the web Roblox Publishing card) — it never rebuilds from source, so rollback is fast + deterministic. This mints a NEW Roblox version pointing at the old build. Unknown version → 404; a version with no retained artifact → 422; a place open in Studio / rate-limited → 409 (retry); an invalid or unscoped Open Cloud key → 409 / 403. Returns {version_number, env, published_at, status, next_step}.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "version_number": {
      "title": "Version Number",
      "type": "integer"
    }
  },
  "required": [
    "project_id",
    "version_number"
  ],
  "title": "rollback_roblox_placeArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "rollback_roblox_placeOutput"
}
🟢project_status(project_id)

Check how a project is doing: what is in flight, what shipped, what is stuck. Returns lifecycle status, open task count, queued roadmap item count, runs today, verification pass rate, open blockers, pause state, and freshness. Zero `open_tasks` does not mean an empty roadmap: `queued_roadmap_items` counts planned work awaiting expansion, including work held while the scheduler is disabled. Use list_roadmap to inspect those items. `glm_peak_paused` is NOT a fault and sets no pause columns: it is the ephemeral GLM peak-hours skip. True means clean PRs hold and runs stop until `glm_peak_resumes_at`. It is an intentional cost gate, so report it as "waiting for off-peak", never as a failure. For `web_app` projects the optional `ui_review` block can mint a GitHub installation token and read the default branch's `ui-review.json`, so this status call is not guaranteed to be a purely local read.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "project_statusArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "project_statusOutput"
}
🔴close_task(project_id, task_id, reason, superseded_by_task_id, superseded_by_pr_number)

Close a task that should not be built — a duplicate, or work already shipped. Use this when a task on the board is obsolete: the change already landed in another PR, a sibling task covers it, or the user changed direction. The task is marked `cancelled` and keeps ALL of its history (acceptance criteria, QA steps, iterations) — nothing is deleted. `reason` is REQUIRED and is recorded on the audit trail; say why in one line. Pass `superseded_by_pr_number` (or `superseded_by_task_id`) when the work was genuinely delivered somewhere else — that records verified provenance instead of a bare abandon. Closing does NOT claim the content is on the default branch, so any task that declared a dependency on this one keeps waiting; deliver or re-plan those separately. Refuses with 409 while the task is being worked on by a running iteration (stop the machine first, or wait for it to finish). A task in another workspace 404s. Business-level replay guard: closing an already-closed task changes no task state. The call is not idempotent end to end — an authenticated request also persists credential-use state. The response echoes `open_tasks`: how many tasks are still open on the project, counted after the close commits. Check it — if it did not drop, the ticket was already closed and this call changed nothing. It is the same count `project_status` returns, and neither counts a closed task as open. Prefer this over leaving a dead task on the board: unfinished tasks count against the project's planning capacity, so stale duplicates quietly stop new roadmap items from being expanded.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "task_id": {
      "title": "Task Id",
      "type": "string"
    },
    "reason": {
      "title": "Reason",
      "type": "string"
    },
    "superseded_by_task_id": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Superseded By Task Id"
    },
    "superseded_by_pr_number": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Superseded By Pr Number"
    }
  },
  "required": [
    "project_id",
    "task_id",
    "reason"
  ],
  "title": "close_taskArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "close_taskOutput"
}
🟡get_product_goal(project_id)

Read a project's current product goal (the outcome work is aimed at). READ THIS BEFORE YOU CALL `set_product_goal`: the setter REPLACES the whole document rather than appending to it, so writing without reading first silently discards whatever the user already recorded. To add a line, read the current text, edit it, and set the full result back. Returns {project_id, product_goal_md, updated_at}. `product_goal_md` is None when no goal has been set. Tenant-scoped: a project not in the caller's workspace 404s.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "get_product_goalArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_product_goalOutput"
}
🟡get_product_vision(project_id)

Read a project's current product vision (what the product is for). READ THIS BEFORE YOU CALL `set_product_vision`: the setter REPLACES the whole document rather than appending to it, so writing without reading first silently discards whatever the user already recorded. To add a line, read the current text, edit it, and set the full result back. Returns {project_id, product_vision_md, updated_at}. `product_vision_md` is None when no vision has been set. Tenant-scoped: a project not in the caller's workspace 404s.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "get_product_visionArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_product_visionOutput"
}
🟢list_escalations(project_id)

See what the work is stuck on and waiting for a human decision about. `project_status` only COUNTS open escalations; this returns each one with its kind, reason, detail_md, recommended_action, and (when task-scoped) the blocked task's title + PR url. A "forever-paused" project with no open `project_pause` row is surfaced as a synthetic `orphan:<project_id>` row. Each row carries a server-derived `task_retry_available` flag and an exact `next_tool`: eligible recovery blocks route to `retry_blocked_task`, roadmap parks to `rearm_roadmap_item`, platform conflicts to their structured resolver, stale work to `replan_task`, and only safe auxiliary cards to `resolve_escalation`.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "list_escalationsArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "list_escalationsOutput"
}
🔴resolve_escalation(escalation_id, decision_md)

Acknowledge a handled ask only when no blocked task becomes invisible. `decision_md` is a required short note (why/how it was resolved), appended to the escalation's detail_md as an audit trail. Resolving a project_pause / orphan_pause RESUMES the project (clears the pause). A blocked task's final task_block/operator_action cannot be acknowledged: use its typed retry, platform-policy resolution, replan, close, or supersede operation instead. The acknowledgement itself is replay-guarded (a repeat changes nothing), but the call is not idempotent end to end: resuming a paused project lets queued refinement run, and that refinement can DELETE tasks. Accepts a real escalation UUID or a synthetic `orphan:<project_id>`.

Input Schema

{
  "type": "object",
  "properties": {
    "escalation_id": {
      "title": "Escalation Id",
      "type": "string"
    },
    "decision_md": {
      "title": "Decision Md",
      "type": "string"
    }
  },
  "required": [
    "escalation_id",
    "decision_md"
  ],
  "title": "resolve_escalationArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "resolve_escalationOutput"
}
⚪retry_blocked_task(escalation_id, decision_md)

Grant one fresh attempt after fixing a recovery-budget task block. Pass the task_block id returned by ``list_escalations``. Eligibility, tenant, task, failure class, task status, and competing blockers are all derived server-side. This is distinct from ``resolve_escalation``, which remains acknowledgement-only for task blocks. Business-level replay guard: an already-retried block is not retried again; the call is not idempotent end to end, because an authenticated request also persists credential-use state.

Input Schema

{
  "type": "object",
  "properties": {
    "escalation_id": {
      "title": "Escalation Id",
      "type": "string"
    },
    "decision_md": {
      "title": "Decision Md",
      "type": "string"
    }
  },
  "required": [
    "escalation_id",
    "decision_md"
  ],
  "title": "retry_blocked_taskArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "retry_blocked_taskOutput"
}
🟢rearm_roadmap_item(escalation_id)

Run the dashboard's replay-guarded same-item dependency re-arm. Pass an ``expand_produced_nothing`` or ``roadmap_item_parked`` escalation id. Keelen verifies delivered structural prerequisites, grants one bounded expand retry, preserves the roadmap item id and ``depends_on_item_ids``, and resolves the matching cards in one transaction. A bounded live GitHub check may be used when the local merge state is stale, so a call can reach out even when the re-arm itself changes nothing.

Input Schema

{
  "type": "object",
  "properties": {
    "escalation_id": {
      "title": "Escalation Id",
      "type": "string"
    }
  },
  "required": [
    "escalation_id"
  ],
  "title": "rearm_roadmap_itemArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "rearm_roadmap_itemOutput"
}
⚪resolve_platform_policy_conflict(escalation_id, decision, decision_md, project_cap)

Resolve a platform-policy dead-end with a structured plan decision. ``decision`` is one of ``reuse_existing_evidence``, ``split_task``, ``raise_project_cap``, or ``remove_scenario``. ``raise_project_cap`` also requires ``project_cap`` (6..32). The decision becomes a new task-plan section; the same task lineage is requeued before the escalation resolves.

Input Schema

{
  "type": "object",
  "properties": {
    "escalation_id": {
      "title": "Escalation Id",
      "type": "string"
    },
    "decision": {
      "title": "Decision",
      "type": "string"
    },
    "decision_md": {
      "title": "Decision Md",
      "type": "string"
    },
    "project_cap": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Project Cap"
    }
  },
  "required": [
    "escalation_id",
    "decision",
    "decision_md"
  ],
  "title": "resolve_platform_policy_conflictArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "resolve_platform_policy_conflictOutput"
}
🟡set_ui_review_scenario_cap(project_id, scenario_cap)

Set this web_app project's ui-review scenario budget, or reset it. ``scenario_cap`` is an integer from 6 through 32, or ``null`` to reset to the platform default of 12. The screenshot cap derives from it and moves with it, so the two can never starve each other. Raising is always allowed, including from a completely full manifest. LOWERING is refused when the default branch already declares more scenarios or screenshots than the smaller budget allows, and is also refused when that manifest cannot be read — both caps are enforced when keelen pushes and not in your CI, so an over-cap manifest fails every push while CI stays green. Read ``project_status.ui_review`` first to see the live occupancy. Repeated calls with the same value change nothing.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "scenario_cap": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Scenario Cap"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "set_ui_review_scenario_capArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "set_ui_review_scenario_capOutput"
}
🔴replan_task(project_id, task_id, title, body_md, acceptance_criteria, ...)

Replace an unretryable blocked task without losing its lineage. Creates a fresh, explicitly planned task under the same roadmap item and attempt lineage, transfers prerequisites and downstream dependents, and cancels the stale task with supersession provenance. The old PR, task, failures, criteria, and evidence remain in the audit history; nothing is marked delivered.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "task_id": {
      "title": "Task Id",
      "type": "string"
    },
    "title": {
      "title": "Title",
      "type": "string"
    },
    "body_md": {
      "title": "Body Md",
      "type": "string"
    },
    "acceptance_criteria": {
      "items": {
        "type": "string"
      },
      "title": "Acceptance Criteria",
      "type": "array"
    },
    "decision_md": {
      "title": "Decision Md",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "task_id",
    "title",
    "body_md",
    "acceptance_criteria",
    "decision_md"
  ],
  "title": "replan_taskArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "replan_taskOutput"
}
🔴control_scheduler(project_id, action)

Start, pause, or resume a project's autonomous work. `action` is one of: "enable" | "disable" | "resume" | "process_now". - enable/disable flip scheduler_enabled (the loop dispatches only enabled, status='active' projects). - resume clears a pause (peak/backoff/manual) so the project dispatches again. - process_now durably prioritizes and immediately attempts the next intake batch, independent of the dev scheduler switch. A gate returns a typed reason and recovery step instead of a spawn promise. - enable/resume can execute pending refinement that DELETES tasks, and process_now can start an intake batch, so treat this tool as destructive even when the echoed scheduler state looks unchanged. Returns the resulting scheduler state.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "action": {
      "title": "Action",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "action"
  ],
  "title": "control_schedulerArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "control_schedulerOutput"
}
🔴archive_project(project_id)

Archive a project (reversible shelve) — frees a project slot in-tool. Stops any in-flight machine, then flips the project to `archived`: it drops out of the per-tier project cap (freeing a slot for a new project) and the loop stops dispatching it, but the project + its history are kept and can be restored from the dashboard project page. Stopping a machine DESTROYS the running process state — restoring the project later cannot bring that execution back, so this is destructive despite being reversible. A replay on an already-archived project changes no row, but is not idempotent end to end. Prefer this over `delete_project` unless you specifically want the project gone. Owner-scoped (an MCP key is owner-only); a project not in the workspace 404s.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "archive_projectArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "archive_projectOutput"
}
🔴delete_project(project_id)

Soft-delete a project (the harder option) — frees a slot and hides it. Stops any in-flight machine, then flips the project to `deleted`: it disappears from `list_projects`, drops out of the project cap, and the loop stops dispatching it. Deleting ALSO permanently purges the project's stored review artifacts. The row is retained for audit and re-importing the repo restores that deleted row in place (unlike `archive_project` there is no in-tool restore), but the purged artifacts are gone for good and the stopped workers' process state cannot be recreated. A replay on a deleted project changes no row; the call is not idempotent end to end. Owner-scoped; a project not in the workspace 404s.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "delete_projectArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "delete_projectOutput"
}
🟡run_security_review(project_id)

Find security problems in a repository: a deep, whole-codebase review. Spawns a one-shot audit that scans the repo across a kind-aware taxonomy (secrets + git history, vulnerable/abandoned deps, injection, SSRF, path traversal, deserialization, crypto, info-leak, plus web authz/session/CORS, library API-misuse, game client-trust, or infra/CI as applicable) and posts findings to the project's Security review for human triage. You review the findings, then send the ones worth fixing into the loop as Requests; no fix is applied automatically. Billable; one audit in-flight per project. (Triggering is disabled while the feature is hardened for production: a project not on the operator allowlist — empty by default — returns a message instead of spawning; earlier results stay visible.) Requires a paid plan; a free or trial workspace gets a message telling the user to upgrade.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "run_security_reviewArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "run_security_reviewOutput"
}
🟡run_legal_exposure_review(project_id)

Find where a codebase creates legal exposure (privacy, consent, data handling). Spawns a one-shot review machine that reads the checkout offline and maps what the code DOES onto commonly cited legal obligations, filtered by the project's saved compliance profile (jurisdictions plus eleven product facts). Findings land on the project's Legal page for a human to triage, and you read them with get_legal_exposure_findings. No change is ever applied automatically. THIS IS NOT LEGAL ADVICE AND IT IS NOT A LEGAL CLEARANCE. The result carries a `disclaimer_md` field: repeat it to the user before you summarise anything. The review is not exhaustive, so an empty result is never proof that anything is in order. Requires a saved compliance profile (409-shaped refusal without one), a plan that carries the feature, and the project on the operator allowlist. Billable; one review in flight per project. Every refusal comes back as `ok: false` with an actionable `next_step`, and starts nothing. Rate-limited per workspace.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "run_legal_exposure_reviewArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "run_legal_exposure_reviewOutput"
}
🟢get_legal_exposure_findings(project_id, limit, include_all)

Read a project's legal exposure findings, worst exposure first. Returns each finding as an OBSERVATION plus the obligation commonly cited over that pattern, its citation, the date the citation was last checked, and whether counsel has reviewed the registry entry (`counsel_reviewed`, false today for every entry). `exposure_order` is an ORDER, never a score: there is no grade, no percentage, and no overall state in this output. Read `not_determinable` before you summarise: those are checks that could not reach a verdict, and leaving them out would turn a partial review into a clean answer. The `disclaimer_md` field must reach the user. `limit` defaults to 25 (max 100). `include_all` adds resolved, dismissed, and out-of-scope rows to the default actionable set. Read-only, no compute, no rate limit; it works even while the trigger is closed for the project.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "limit": {
      "default": 25,
      "title": "Limit",
      "type": "integer"
    },
    "include_all": {
      "default": false,
      "title": "Include All",
      "type": "boolean"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "get_legal_exposure_findingsArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_legal_exposure_findingsOutput"
}
⚪run_control_gap_review(project_id)

Start a source-bound security control gap review for one project. The review records bounded engineering observations under the user's selected Cyber Essentials or CMMC Level 1 or Level 2 context. It does not determine framework standing, and it does not make changes. The `disclaimer_md` field must be repeated to the user before any observation is summarised. The project needs a saved framework profile, an eligible plan, and a place on the operator allowlist. Billable; one review is allowed in flight per project. Refusals return `ok: false` with a next step and do not start work. Rate-limited per workspace.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "run_control_gap_reviewArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "run_control_gap_reviewOutput"
}
🟢get_control_gap_findings(project_id, limit, include_all)

Read a project's security-control observations and review boundaries. The result groups records by evidence class without a total or an overall framework outcome. Read `not_determinable` and `outside_review_scope` before describing any observation. An empty group does not establish that a control is in place, and `disclaimer_md` must reach the user. `limit` defaults to 25 (max 100). `include_all` includes resolved, dismissed, and out-of-scope records. This read-only tool has no compute quota and remains available after the trigger closes for a project.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "limit": {
      "default": 25,
      "title": "Limit",
      "type": "integer"
    },
    "include_all": {
      "default": false,
      "title": "Include All",
      "type": "boolean"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "get_control_gap_findingsArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_control_gap_findingsOutput"
}
🔴answer_request(project_id, thread_id, answers)

Answer a thread's clarifying questions (status must be awaiting_answers). `answers` is a list of {"idx": <int from get_request_status>, "answer_md": <str, 1..2000 chars>}. Answer EVERY question exactly once. Flips the thread back to intake_pending; poll get_request_status again.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "thread_id": {
      "title": "Thread Id",
      "type": "string"
    },
    "answers": {
      "items": {
        "additionalProperties": true,
        "type": "object"
      },
      "title": "Answers",
      "type": "array"
    }
  },
  "required": [
    "project_id",
    "thread_id",
    "answers"
  ],
  "title": "answer_requestArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "answer_requestOutput"
}
🔴refine_request(project_id, thread_id, feedback_md)

Say what is wrong with what a request produced, and have it reworked. Status must be "done". `feedback_md` is 1..2000 chars. Moves the thread to refine_pending; poll get_request_status to see the revised items. Refinement re-plans the thread's roadmap items and can EDIT OR HARD-DELETE the task rows it generated earlier (the machine patch-tasks endpoint deletes them outright), so treat this as destructive: up to five refinements per thread each carry that power.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    },
    "thread_id": {
      "title": "Thread Id",
      "type": "string"
    },
    "feedback_md": {
      "title": "Feedback Md",
      "type": "string"
    }
  },
  "required": [
    "project_id",
    "thread_id",
    "feedback_md"
  ],
  "title": "refine_requestArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "refine_requestOutput"
}
🔴signup(email)

Create a Keelen account (or start agent login) — emails a 6-digit code. UNAUTHENTICATED — the only tool besides verify_email that works before a bearer key is configured. `email` is where the code is sent. Flow: signup(email) -> the user reads the 6-digit code from their inbox -> verify_email(email, code) returns a reveal-once API key -> save it as this server's `Authorization: Bearer <api_key>` header in your MCP client config -> reconnect -> get_onboarding_status() to continue setup. The code expires in 15 minutes; call signup again to resend. Response is uniform whether or not the email already has an account (enumeration-safe), so signup doubles as agent LOGIN. Rate-limited per IP and per email. ASK THE USER for `email` in chat and WAIT for their answer before calling this. Do NOT infer it from your client profile, the logged-in account, git config, or any other ambient source; if you already hold a candidate, echo it back and get an explicit yes first. Because this call doubles as LOGIN, a guessed address signs the user in to whatever workspace owns it, and the rest of setup then mints an API key on, and creates a project in, an account they did not choose.

Input Schema

{
  "type": "object",
  "properties": {
    "email": {
      "title": "Email",
      "type": "string"
    }
  },
  "required": [
    "email"
  ],
  "title": "signupArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "signupOutput"
}
🔴verify_email(email, code)

Redeem the emailed 6-digit code for a reveal-once workspace API key. UNAUTHENTICATED. `email` + `code` must match a code issued by signup(email) within the last 15 minutes (5 attempts max). The returned `api_key` is shown exactly ONCE — store it ONLY in the MCP client config ("Authorization: Bearer <api_key>"), NEVER in a repo or a file you might commit. Then reconnect this server with the header set and call get_onboarding_status(). An invalid/expired/consumed code returns a uniform error — call signup(email) for a fresh one.

Input Schema

{
  "type": "object",
  "properties": {
    "email": {
      "title": "Email",
      "type": "string"
    },
    "code": {
      "title": "Code",
      "type": "string"
    }
  },
  "required": [
    "email",
    "code"
  ],
  "title": "verify_emailArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "verify_emailOutput"
}
🟡get_onboarding_status

Check what is set up so far and what to do next to get building. Reports engine_connected / github_connected / project_count / payment_status and a `next_action` string you should follow VERBATIM, polling this tool between steps. `next_action` is "call_tool" (call the tool named in `next_tool`, following `next_step`) until onboarding is complete, then "done". (`next_action_detail` echoes the pre-2026-07-29 dict shape and is DEPRECATED — it is removed 2026-10-29; read next_action/next_tool instead.) - engine step: send the user the dashboard /login link. Engine subscriptions (Claude / Codex / GLM) are connected in the DASHBOARD for security — NEVER ask for or paste engine credentials in this chat. - github step: call connect_github() for an install link. - project step: create_project(...) for a new repo, or import_project(repo_full_name) for an existing one. - launch step: poll get_provisioning_status(project_id) until ready. Re-checking is YOUR job — the server does not push. Also returns a `usage` block (pool / daily / machine-hours counters + tier caps) for capacity-aware automation clients.

Input Schema

{
  "type": "object",
  "properties": {},
  "title": "get_onboarding_statusArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_onboarding_statusOutput"
}
🟡open_dashboard

Get a one-click, pre-authenticated dashboard sign-in link for the owner. The engine-connect step — and any dashboard task (billing card update, a project page) — needs a signed-in browser. Because this server has already authenticated the workspace owner, this mints a single-use magic-link login token and returns a `/login?token=…` deep link: opening it signs the user straight into the dashboard (no email round-trip, no password) and lands them where onboarding left off. Send the user the returned `login_url`; it works once and expires in 15 minutes — call again for a fresh one.

Input Schema

{
  "type": "object",
  "properties": {},
  "title": "open_dashboardArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "open_dashboardOutput"
}
🟡connect_github

Get the link that connects a GitHub account, so work can reach real repos. Returns an `install_url` — send it to the user to open in a browser. They pick the GitHub account/org, approve the install, and land on a "connected" page; then poll get_onboarding_status() until github_connected is true. The link expires in 10 minutes — call connect_github() again for a fresh one.

Input Schema

{
  "type": "object",
  "properties": {},
  "title": "connect_githubArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "connect_githubOutput"
}
🟢list_github_repos

List repos the workspace's GitHub connection can see (for import_project). Each entry has full_name, default_branch, private, language, pushed_at. Pass a `full_name` to import_project(repo_full_name) to connect it. Reading the list can MINT a short-lived GitHub installation token, so this is not a pure read. Returns 409 if GitHub isn't connected yet — call connect_github() first.

Input Schema

{
  "type": "object",
  "properties": {},
  "title": "list_github_reposArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "items": {
        "additionalProperties": true,
        "type": "object"
      },
      "title": "Result",
      "type": "array"
    }
  },
  "required": [
    "result"
  ],
  "title": "list_github_reposOutput"
}
🔴import_project(repo_full_name, engine, build_description, project_kind, stack, ...)

Connect an EXISTING GitHub repo as a Keelen project. This is the counterpart of create_project: use THIS tool when the user already has a repo, and create_project only to scaffold a brand-new one. `repo_full_name` is "owner/repo" — it MUST be visible to the workspace's GitHub connection (list_github_repos() to browse; a non-visible repo 404s). `engine` is OPTIONAL — one of claude_code | codex | glm | kimi | grok (defaults to claude_code); codex/glm/kimi/grok require a matching connected credential. `build_description` is OPTIONAL but STRONGLY recommended — a plain-language "what should Keelen build first?" submitted as the project's first Request so the loop has work; an imported project with no Request sits idle until you call submit_request(project_id, ...). `project_kind` is OPTIONAL — one of library | node_library | python_library | service | cli | web_app | godot_game | roblox_game | unknown. Omit it and the kind is auto-detected. PASS IT when the repo is a MONOREPO (apps in subdirectories), a stack with no standard root manifest (Java, Ruby, PHP, .NET, Elixir), or when you want a classification detection cannot infer — in those cases detection yields "unknown", which BLOCKS the dev lane until someone overrides it. A value you pass is authoritative and is never overwritten by later auto-detection. `stack` is the OPTIONAL language axis (python | node | rust | go | cpp) for a language-agnostic kind. `preview_command` is REQUIRED when project_kind is "web_app" (the command that serves the app locally, e.g. "npm run dev") and optional otherwise, where it overrides the detected one. Re-importing the same live repo is replay-guarded (returns the existing project with already_exists=True), and re-importing a SOFT-DELETED repo RESTORES that deleted row in place — it does not create a fresh project. Restored queued work can include refinement that deletes tasks, and the call is not idempotent end to end. On a plan with no scheduled-project allowance the project is still created but with the loop OFF — next_step then steers to get_billing(). Otherwise follow next_step and poll get_provisioning_status(project_id).

Input Schema

{
  "type": "object",
  "properties": {
    "repo_full_name": {
      "title": "Repo Full Name",
      "type": "string"
    },
    "engine": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Engine"
    },
    "build_description": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Build Description"
    },
    "project_kind": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Project Kind"
    },
    "stack": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Stack"
    },
    "preview_command": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Preview Command"
    }
  },
  "required": [
    "repo_full_name"
  ],
  "title": "import_projectArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "import_projectOutput"
}
🟡get_provisioning_status(project_id)

Check whether a new project has finished setting up and is ready to build. Returns overall (provisioning | ready | errored), a 7-stage checklist, and a user-facing error_kind when a stage failed. next_action is "wait" with poll_after_seconds (~10s) while provisioning or errored, and "done" when overall is 'ready' — then steer the loop with submit_request(project_id, text). Reaching 'ready' also PERSISTS the owner's onboarding-completion state, so this is not a pure read. Tenant-scoped: a project not in the caller's workspace 404s.

Input Schema

{
  "type": "object",
  "properties": {
    "project_id": {
      "title": "Project Id",
      "type": "string"
    }
  },
  "required": [
    "project_id"
  ],
  "title": "get_provisioning_statusArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_provisioning_statusOutput"
}
🟡get_billing(plan)

Billing status + a Stripe checkout link when a NEW subscription is needed. `plan` is one of starter | pro | agency (default starter). When the workspace has NO live subscription and needs one (open-signup unpaid, churned, or converting from a free/trial tier), returns a `checkout_url` with next_action "browser" — send it to the user to open in a browser (the one setup step that can't happen in chat). Compute unlocks automatically once payment completes (a Stripe webhook flips the workspace to active); you do not need to block on it. A past_due workspace gets NO checkout — the fix is a card update in the dashboard billing page (a new checkout would create a second subscription); follow next_step. Subscribed or suspended-with-subscription states return checkout_url=None with an explanatory next_step.

Input Schema

{
  "type": "object",
  "properties": {
    "plan": {
      "default": "starter",
      "title": "Plan",
      "type": "string"
    }
  },
  "title": "get_billingArguments"
}

Output Schema

{
  "type": "object",
  "properties": {
    "result": {
      "additionalProperties": true,
      "title": "Result",
      "type": "object"
    }
  },
  "required": [
    "result"
  ],
  "title": "get_billingOutput"
}

Community

Rate this Server

Evidence

Recent observations

verifiedversion not recorded41 tools
verifiedversion not recorded41 tools