Server definition
- Hash
- sha256:3242e565794b7bd59a091ab2fcadbd90a010914e4a34a03b5d9ebf5a932f718c
- What it is
- What a remote MCP server returned when asked what it offers: 42 tools
The blob, as servednamed by its sha256
{
"instructions": "keelen-saas Roadmap intake. Submit product requests; PM intake classifies each into roadmap items, clarifying questions, or steering rules.\n\nNo project yet? There are TWO ways to get one - ASK the user which applies, do not assume:\n0a. EXISTING repo -> import_project(repo_full_name[, build_description, project_kind, stack, preview_command, engine]) - connect a repo the user already has. list_github_repos() browses what the workspace's GitHub connection can see. `project_kind` is optional (auto-detected) but PASS IT for a monorepo or a stack with no standard root manifest (Java, Ruby, PHP, .NET) - detection yields 'unknown' there, which blocks the dev lane.\n0b. NEW repo -> create_project(name, build_description, project_kind[, private, preview_command, org]) - scaffold a NEW GitHub repo + bootstrap project and submit build_description as the first Request. `name` is a concise repo slug; `project_kind` is required (library | node_library | python_library | web_app | godot_game | roblox_game); web_app needs preview_command. Pass `org` (a GitHub org login) to create the repo inside that organization; omit it to use a workspace member's personal account (repo-create OAuth). Then poll get_request_status with the returned thread_id.\n\nWorkflow:\n1. submit_request(project_id, text) - ONE feature/intent per call, text < 16000 chars. Split a multi-feature ask into separate requests.\n2. Poll get_request_status. Intake runs async (~5min cadence) - wait between polls. Read next_action: wait | answer_questions | done | cancelled | failed.\n3. If answer_questions -> answer_request (one answer per question).\n4. After done -> optionally refine_request (max 5x) to adjust items.\n\nProject context (optional, anytime):\n- set_product_vision(project_id, vision_md) - set the product vision.\n- set_product_goal(project_id, goal_md) - set the product goal.\nBoth are prepended to PM/dev/QA iter prompts as project context.\n\nOperating a running project:\n- project_status(project_id) - lifecycle status, open tasks, queued_roadmap_items (planned work awaiting expansion, separate from open tasks), iters today, prs_merged_today (the DELIVERY signal: distinct PRs merged today), ui_review (web_app only: scenarios/scenario_cap/captures/capture_cap of the default-branch ui-review.json — both caps are enforced at push time, not in your CI), pass_rate (the local verification pass rate over the last 14 days of dev/QA iterations - NOT a delivery measure; read prs_merged_today for that), verify_decided_14d / verify_errored_14d (the ratio's denominator and its sandbox-timeout share: when errored dominates, pass_rate measures the verify budget, not the code), open escalation COUNT, plus pause state (scheduler_enabled, scheduler_paused_until, pause_reason), freshness (last_iter_at, last_pm_iter_at, last_successful_deploy_at — a LEGACY pointer; read deployment {merged, deployed, verified, last_deploy_attempt} for the three facts kept separately, and treat only `verified` as a verified deployment), and glm_peak_paused (+ glm_peak_resumes_at) for the EPHEMERAL GLM peak-hours skip the pause columns can't show — to tell a paused-by-policy project from a genuine stall. pm_lane {last_pm_iter_at, repo_access} is the PLANNING lane's live health: repo_access 'failing' means that lane cannot read the repo from GitHub even while runs still start and github_connected reads true (that flag is presence-only).\n- list_roadmap(project_id) - the queued roadmap items + their priority_int; reorder_roadmap(project_id, ordered_ids) reprioritises that queue (first id = expanded next; horizon pins still dominate).\n- list_escalations(project_id) - the open dead-ends to surface + resolve (project_status only counts them).\n- resolve_escalation(escalation_id, decision_md) - acknowledge one handled ask only when a blocked task keeps another visible path; a project_pause/orphan_pause resolve RESUMES the project.\n- retry_blocked_task(escalation_id, decision_md) - after fixing the cause of an eligible recovery-budget task block, grant that task one fresh attempt.\n- rearm_roadmap_item(escalation_id) - perform the dashboard's idempotent same-item dependency re-arm without changing its id or declared roadmap dependencies.\n- resolve_platform_policy_conflict(escalation_id, decision, decision_md[, project_cap]) - apply a structured product decision to the plan and atomically requeue the same task lineage.\n- replan_task(project_id, task_id, title, body_md, acceptance_criteria, decision_md) - replace unretryable stale work under the same roadmap/attempt lineage and transfer its graph edges.\n- control_scheduler(project_id, action) - action enable|disable|resume|process_now to wake a dark or paused project.\n- run_security_review(project_id) - kick off a deep whole-repo security audit; findings post to the project's Security review to triage + send to the loop.\n- run_legal_exposure_review(project_id) - kick off a legal exposure review; it maps code observations to commonly cited legal obligations under the project's saved compliance profile. Needs that profile first.\n- get_legal_exposure_findings(project_id) - read those findings, worst exposure first, with the checks that could not decide.\n BOTH legal tools return a `disclaimer_md` field. Read it to the user before you summarise a finding. This lane is an ENGINEERING review, not legal advice and not a legal clearance, it is not exhaustive, and an empty result is never proof that anything is in order. Never turn its output into a score, a grade, or an overall verdict.\n\n- run_control_gap_review(project_id) - kick off a security control gap review after the user saves a framework selection and stack context.\n- get_control_gap_findings(project_id) - read source-bound observations, review boundaries, and items the review could not determine.\n BOTH control-review tools return a `disclaimer_md` field. Read it to the user before you summarise an observation. The output is evidence from named sources, not a framework outcome. An empty group never establishes that a control is in place.\n\nMost tool responses carry next_action + next_step. next_action is ALWAYS a string, one of: wait | answer_questions | done | cancelled | failed | call_tool | browser | collect_code | save_key_and_reconnect | none. 'call_tool' means call the tool named in the sibling `next_tool` field; 'browser' means send the user the URL named in next_step. When next_action is 'wait' AND poll_after_seconds is present, re-check after that many seconds - re-checking is YOUR job, the server does not push. A 'wait' with NO poll_after_seconds has nothing to poll over MCP (e.g. run_security_review, whose findings land on the project's Security review page, or an errored get_provisioning_status, which will not change until its reported cause is fixed): report it to the user instead of looping.\n\nGetting started (no account yet):\nThis server accepts a tokenless connection for signup. If the user has no Keelen account, drive the whole setup from chat:\n1. ASK THE USER for the email address, in chat, and WAIT for their answer before calling signup(email). Do NOT take the address from your client profile, the logged-in account, git config, or any other ambient source - the user's Keelen account is often NOT the account you are running under. If you already hold a candidate address, echo it back and get an explicit yes first. This matters because signup also doubles as agent LOGIN for an existing account: a guessed address silently signs you in to whatever workspace owns it, and the next steps then mint an API key on, and create a project in, the wrong account. signup(email) emails the user a 6-digit code.\n2. Ask the user to read the code from their inbox, then verify_email(email, code) - it returns a reveal-once API key.\n3. Save that key as this server's Authorization header (\"Authorization: Bearer <api_key>\") in the MCP CLIENT CONFIG, then RECONNECT. The key is shown once - never write it to a repo/file.\n4. get_onboarding_status() - loop it and follow next_action/next_tool + next_step verbatim: engine connect happens in the DASHBOARD — call open_dashboard() for a one-click signed-in link (the user is already authenticated through this server, so no email login; NEVER paste Claude/Codex/GLM credentials in chat) -> connect_github() for the install link -> create_project (new repo) or import_project(repo) (existing; list_github_repos() to browse) -> get_provisioning_status(project_id) until ready (if it reports errored, stop polling and fix the cause first).\n5. get_billing() when the user is ready to run compute - it returns a Stripe checkout link (the one step that must open a browser).",
"tools": [
{
"description": "Answer a thread's clarifying questions (status must be awaiting_answers).\n\n `answers` is a list of {\"idx\": <int from get_request_status>, \"answer_md\":\n <str, 1..2000 chars>}. Every question needs exactly one answer. The call\n flips the thread back to intake_pending; get_request_status reports the\n subsequent state.\n ",
"inputSchema": {
"properties": {
"answers": {
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Answers",
"type": "array"
},
"project_id": {
"title": "Project Id",
"type": "string"
},
"thread_id": {
"title": "Thread Id",
"type": "string"
}
},
"required": [
"project_id",
"thread_id",
"answers"
],
"title": "answer_requestArguments",
"type": "object"
},
"name": "answer_request",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "answer_requestOutput",
"type": "object"
}
},
{
"description": "Archive a project (reversible shelve) — frees a project slot in-tool.\n\n Stops any in-flight machine, then flips the project to `archived`: it drops\n out of the per-tier project cap (freeing a slot for a new project) and the\n loop stops dispatching it, but the project + its history are kept and can be\n restored from the dashboard project page. Stopping a machine DESTROYS the\n running process state — restoring the project later cannot bring that\n execution back, so this is destructive despite being reversible. A replay on\n an already-archived project changes no row, but is not idempotent end to end.\n This is preferable to `delete_project` unless the project must be gone.\n Owner-scoped (an MCP key is owner-only); a project not in the workspace 404s.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "archive_projectArguments",
"type": "object"
},
"name": "archive_project",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "archive_projectOutput",
"type": "object"
}
},
{
"description": "Cancel / close a single roadmap item (list_roadmap returns its id).\n\n For a DELIVERED or duplicate item that keeps re-parking: once the work has\n shipped, every expand produces no dev-ready tasks and files a recurring\n `roadmap_item_parked` escalation that has to be acked. Cancelling drops the\n item out of the expand queue AND resolves any open expand-lane escalation for\n it. Business-level replay guard: an already expanded/cancelled item changes no\n state, and the dashboard can restore a cancelled item. Refuses (409) while the\n item is actively being expanded (a retry once that iteration ends succeeds).\n Returns {id, status, changed, next_step}.\n ",
"inputSchema": {
"properties": {
"item_id": {
"title": "Item Id",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id",
"item_id"
],
"title": "cancel_roadmap_itemArguments",
"type": "object"
},
"name": "cancel_roadmap_item",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "cancel_roadmap_itemOutput",
"type": "object"
}
},
{
"description": "Clear a queued roadmap item's horizon pin (now/next/later → none).\n\n A horizon pin dominates the queue sort, so `reorder_roadmap` cannot move a\n pinned item out of its band — a stale `now` pin on a delivered/duplicate item\n clogs the front of the queue. This unpins it and reprices the queue so the\n item follows plain priority order again (and reorder_roadmap can then move it).\n Only queued items carry a settable pin (in-flight / shipped items derive\n theirs), so this refuses (422) on a non-queued item — a delivered item is\n closed with cancel_roadmap_item. Returns {id, previous_pin, horizon_pin,\n next_step}.\n ",
"inputSchema": {
"properties": {
"item_id": {
"title": "Item Id",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id",
"item_id"
],
"title": "clear_horizon_pinArguments",
"type": "object"
},
"name": "clear_horizon_pin",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "clear_horizon_pinOutput",
"type": "object"
}
},
{
"description": "Close a task that should not be built — a duplicate, or work already shipped.\n\n For a task on the board that is obsolete: the change already landed in\n another PR, a sibling task covers it, or the user changed direction. The\n task is marked `cancelled` and keeps ALL of its history (acceptance\n criteria, QA steps, iterations) — nothing is deleted.\n\n `reason` is REQUIRED and is recorded on the audit trail in one line.\n `superseded_by_pr_number` (or `superseded_by_task_id`) records verified\n provenance when the work was genuinely delivered somewhere else, instead of\n a bare abandon. Closing does NOT claim the content is on the default branch,\n so any task that declared a dependency on this one keeps waiting; those are\n delivered or re-planned separately.\n\n Refuses with 409 while the task is being worked on by a running iteration\n (an in-flight iteration must finish, or its machine must be stopped, before\n the close succeeds). A task in another\n workspace 404s. Business-level replay guard: closing an already-closed task\n changes no task state. The call is not idempotent end to end — an\n authenticated request also persists credential-use state.\n\n The response echoes `open_tasks`: how many tasks are still open on the\n project, counted after the close commits. A count that did not drop means\n the ticket was already closed and this call changed nothing. It is the same\n count `project_status` returns, and neither counts a closed task as open.\n\n This is preferable to leaving a dead task on the board: unfinished tasks\n count against the project's planning capacity, so stale duplicates quietly\n stop new roadmap items from being expanded.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
},
"reason": {
"title": "Reason",
"type": "string"
},
"superseded_by_pr_number": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Superseded By Pr Number"
},
"superseded_by_task_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Superseded By Task Id"
},
"task_id": {
"title": "Task Id",
"type": "string"
}
},
"required": [
"project_id",
"task_id",
"reason"
],
"title": "close_taskArguments",
"type": "object"
},
"name": "close_task",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "close_taskOutput",
"type": "object"
}
},
{
"description": "Get the link that connects a GitHub account, so work can reach real repos.\n\n Returns an `install_url` that the user opens in a browser to pick the GitHub\n account/org, approve the install, and land on a \"connected\" page;\n get_onboarding_status() then reports github_connected true. The link expires\n in 10 minutes; a repeat call mints a fresh one.\n ",
"inputSchema": {
"properties": {},
"title": "connect_githubArguments",
"type": "object"
},
"name": "connect_github",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "connect_githubOutput",
"type": "object"
}
},
{
"description": "Start, pause, or resume a project's autonomous work.\n\n `action` is one of: \"enable\" | \"disable\" | \"resume\" | \"process_now\".\n - enable/disable flip scheduler_enabled (the loop dispatches only enabled,\n status='active' projects).\n - resume clears a pause (peak/backoff/manual) so the project dispatches again.\n - process_now durably prioritizes and immediately attempts the next intake\n batch, independent of the dev scheduler switch. A gate returns a typed\n reason and recovery step instead of a spawn promise.\n - enable/resume can execute pending refinement that DELETES tasks, and\n process_now can start an intake batch, so the tool is destructive even\n when the echoed scheduler state looks unchanged.\n\n Returns the resulting scheduler state.\n ",
"inputSchema": {
"properties": {
"action": {
"title": "Action",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id",
"action"
],
"title": "control_schedulerArguments",
"type": "object"
},
"name": "control_scheduler",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "control_schedulerOutput",
"type": "object"
}
},
{
"description": "Start building something new: creates a GitHub repo and begins work on it.\n\n For a NEW repo. Given an existing repo, import_project(repo_full_name) is\n the right call — this one would create a second, empty repo beside it\n (list_github_repos() browses what the workspace can see).\n\n Scaffolds a new GitHub repo, a bootstrap-mode project, and submits\n `build_description` as the project's first Roadmap Request. `name` is a\n concise GitHub short repo slug (no owner); `project_kind` is REQUIRED and\n one of library | node_library | python_library | service | cli | web_app |\n godot_game | roblox_game; `preview_command` is required iff\n `project_kind == 'web_app'`.\n `engine` is\n OPTIONAL — one of claude_code | codex | glm | kimi | grok (defaults to\n claude_code); codex, glm, kimi, and grok require the workspace to have a\n matching connected credential.\n `org` is OPTIONAL — a GitHub organization login to create the repo inside\n (e.g. your company org); omit it to land the repo on a member's personal\n account. `private` defaults to True.\n `ci_runs_on` is OPTIONAL — the CI runner labels for the scaffolded workflow,\n e.g. [\"self-hosted\", \"linux\", \"x64\", \"my-fleet\"]. Omit it to inherit the\n workspace default (ubuntu-latest if unset). Labels no registered org runner\n carries are rejected, because GitHub would queue such a job forever rather\n than fail it.\n `framework` is OPTIONAL and `web_app`-only — one of vite | next (defaults to\n vite). It picks the scaffolded frontend rails: `vite` a vanilla-TypeScript\n SPA, `next` a Next.js app-router app. Passing it with any other\n `project_kind` is an error.\n\n The repo is created on the GitHub account of a workspace member with\n repo-create OAuth access (this path has no specific caller user), so the\n returned `repo` owner is whichever member's token resolved (or the chosen\n `org`). If no member has repo-create access — or the resolving member can't\n create in `org` — the call returns an actionable error.\n\n Returns {project_id, repo, thread_id, next_action, poll_after_seconds,\n next_step}; next_step names the follow-up poll of get_request_status with\n the returned thread_id. On the rare arm where the first Request failed to\n submit, next_action is \"call_tool\" with next_tool=\"submit_request\".\n\n If scaffolding fails after the repository has been created, best-effort\n compensation deletes that just-created repository so a retry can reuse the\n name — this tool can therefore remove external state it created moments\n earlier.\n ",
"inputSchema": {
"properties": {
"build_description": {
"title": "Build Description",
"type": "string"
},
"ci_runs_on": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Ci Runs On"
},
"engine": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Engine"
},
"framework": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Framework"
},
"name": {
"title": "Name",
"type": "string"
},
"org": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Org"
},
"preview_command": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Preview Command"
},
"private": {
"default": true,
"title": "Private",
"type": "boolean"
},
"project_kind": {
"title": "Project Kind",
"type": "string"
}
},
"required": [
"name",
"build_description",
"project_kind"
],
"title": "create_projectArguments",
"type": "object"
},
"name": "create_project",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "create_projectOutput",
"type": "object"
}
},
{
"description": "Soft-delete a project (the harder option) — frees a slot and hides it.\n\n Stops any in-flight machine, then flips the project to `deleted`: it\n disappears from `list_projects`, drops out of the project cap, and the loop\n stops dispatching it. Deleting ALSO permanently purges the project's stored\n review artifacts. The row is retained for audit and re-importing the repo\n restores that deleted row in place (unlike `archive_project` there is no\n in-tool restore), but the purged artifacts are gone for good and the stopped\n workers' process state cannot be recreated. A replay on a deleted project\n changes no row; the call is not idempotent end to end. Owner-scoped; a\n project not in the workspace 404s.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "delete_projectArguments",
"type": "object"
},
"name": "delete_project",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "delete_projectOutput",
"type": "object"
}
},
{
"description": "Billing status + a Stripe checkout link when a NEW subscription is needed.\n\n `plan` is one of starter | pro | agency (default starter). When the\n workspace has NO live subscription and needs one (open-signup unpaid,\n churned, or converting from a free/trial tier), returns a `checkout_url`\n with next_action \"browser\" — the user opens it in a browser (the one setup\n step that can't happen in chat). Compute unlocks automatically once payment\n completes (a Stripe webhook flips the workspace to active); blocking on it\n is unnecessary. A past_due workspace gets NO checkout — the fix is a card\n update in the dashboard billing page (a new checkout would create a second\n subscription); next_step carries that fix. Subscribed or\n suspended-with-subscription states return checkout_url=None with an\n explanatory next_step.\n ",
"inputSchema": {
"properties": {
"plan": {
"default": "starter",
"title": "Plan",
"type": "string"
}
},
"title": "get_billingArguments",
"type": "object"
},
"name": "get_billing",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_billingOutput",
"type": "object"
}
},
{
"description": "Read a project's security-control observations and review boundaries.\n\n The result groups records by evidence class without a total or an overall\n framework outcome. `not_determinable` and `outside_review_scope` qualify\n every observation. An empty group does not establish that a control is in\n place, and `disclaimer_md` must reach the user.\n\n `limit` defaults to 25 (max 100). `include_all` includes resolved,\n dismissed, and out-of-scope records. This read-only tool has no compute\n quota and remains available after the trigger closes for a project.\n ",
"inputSchema": {
"properties": {
"include_all": {
"default": false,
"title": "Include All",
"type": "boolean"
},
"limit": {
"default": 25,
"title": "Limit",
"type": "integer"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "get_control_gap_findingsArguments",
"type": "object"
},
"name": "get_control_gap_findings",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_control_gap_findingsOutput",
"type": "object"
}
},
{
"description": "Read a project's legal exposure findings, worst exposure first.\n\n Returns each finding as an OBSERVATION plus the obligation commonly cited\n over that pattern, its citation, the date the citation was last checked,\n and whether counsel has reviewed the registry entry (`counsel_reviewed`,\n false today for every entry). `exposure_order` is an ORDER, never a score:\n there is no grade, no percentage, and no overall state in this output.\n\n `not_determinable` lists the checks that could not reach a verdict;\n omitting them would turn a partial review into a clean answer. The\n `disclaimer_md` field must reach the user.\n\n `limit` defaults to 25 (max 100). `include_all` adds resolved, dismissed,\n and out-of-scope rows to the default actionable set. Read-only, no compute,\n no rate limit; it works even while the trigger is closed for the project.\n ",
"inputSchema": {
"properties": {
"include_all": {
"default": false,
"title": "Include All",
"type": "boolean"
},
"limit": {
"default": 25,
"title": "Limit",
"type": "integer"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "get_legal_exposure_findingsArguments",
"type": "object"
},
"name": "get_legal_exposure_findings",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_legal_exposure_findingsOutput",
"type": "object"
}
},
{
"description": "Check what is set up so far and what to do next to get building.\n\n Reports engine_connected / github_connected / project_count / payment_status\n and a `next_action` string whose value is authoritative, with this tool\n polled between steps. `next_action` is \"call_tool\" (the tool named in\n `next_tool`, with `next_step` describing the call) until onboarding is\n complete, then \"done\". (`next_action_detail` echoes the pre-2026-07-29 dict\n shape and is DEPRECATED — it is removed 2026-10-29; next_action/next_tool\n are the current fields.)\n - engine step: the dashboard /login link goes to the user. Engine\n subscriptions (Claude / Codex / GLM) are connected in the DASHBOARD for\n security — engine credentials are never asked for or pasted in this chat.\n - github step: connect_github() returns an install link.\n - project step: create_project(...) for a new repo, or\n import_project(repo_full_name) for an existing one.\n - launch step: get_provisioning_status(project_id) reports readiness.\n Re-checking is the caller's job — the server does not push.\n Also returns a `usage` block (pool / daily / machine-hours counters + tier\n caps) for capacity-aware automation clients.\n ",
"inputSchema": {
"properties": {},
"title": "get_onboarding_statusArguments",
"type": "object"
},
"name": "get_onboarding_status",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_onboarding_statusOutput",
"type": "object"
}
},
{
"description": "Read a project's current product goal (the outcome work is aimed at).\n\n The paired setter `set_product_goal` replaces the whole document rather\n than appending to it, so a write without a prior read silently discards\n whatever the user already recorded; adding a line means reading the current\n text, editing it, and setting the full result back.\n\n Returns {project_id, product_goal_md, updated_at}. `product_goal_md` is\n None when no goal has been set. Tenant-scoped: a project not in the\n caller's workspace 404s.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "get_product_goalArguments",
"type": "object"
},
"name": "get_product_goal",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_product_goalOutput",
"type": "object"
}
},
{
"description": "Read a project's current product vision (what the product is for).\n\n The paired setter `set_product_vision` replaces the whole document rather\n than appending to it, so a write without a prior read silently discards\n whatever the user already recorded; adding a line means reading the current\n text, editing it, and setting the full result back.\n\n Returns {project_id, product_vision_md, updated_at}. `product_vision_md` is\n None when no vision has been set. Tenant-scoped: a project not in the\n caller's workspace 404s.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "get_product_visionArguments",
"type": "object"
},
"name": "get_product_vision",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_product_visionOutput",
"type": "object"
}
},
{
"description": "Check whether a new project has finished setting up and is ready to build.\n\n Returns overall (provisioning | ready | errored), a 7-stage checklist, and a\n user-facing error_kind when a stage failed. next_action is \"wait\" with\n poll_after_seconds (~10s) while provisioning, \"wait\" with NO\n poll_after_seconds when errored (fix the reported cause, then re-check —\n polling again without fixing it will not change the result), and \"done\" when\n overall is 'ready' — submit_request(project_id, text) then steers the loop.\n Reaching 'ready' also PERSISTS the owner's onboarding-completion\n state, so this is not a pure read. Tenant-scoped: a project not in the\n caller's workspace 404s.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "get_provisioning_statusArguments",
"type": "object"
},
"name": "get_provisioning_status",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_provisioning_statusOutput",
"type": "object"
}
},
{
"description": "Check what happened to a request, and read any questions it asked back.\n\n Intake is async (~5min cadence), so the status changes between calls.\n `next_action` is one of: \"wait\" (still processing), \"answer_questions\"\n (answer_request takes one answer per question), \"done\" (see\n generated_roadmap_item_ids), \"cancelled\" (terminal, no items), \"failed\"\n (see intake_failure_reason).\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
},
"thread_id": {
"title": "Thread Id",
"type": "string"
}
},
"required": [
"project_id",
"thread_id"
],
"title": "get_request_statusArguments",
"type": "object"
},
"name": "get_request_status",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "get_request_statusOutput",
"type": "object"
}
},
{
"description": "Connect an EXISTING GitHub repo as a Keelen project.\n\n This is the counterpart of create_project: it is for a repo that already\n exists, while create_project scaffolds a brand-new one.\n\n `repo_full_name` is \"owner/repo\" — it MUST be visible to the workspace's\n GitHub connection (list_github_repos() to browse; a non-visible repo 404s).\n `engine` is OPTIONAL — one of claude_code | codex | glm | kimi | grok\n (defaults to claude_code); codex/glm/kimi/grok require a matching connected\n credential.\n `build_description` is OPTIONAL but STRONGLY recommended — a plain-language\n \"what should Keelen build first?\" submitted as the project's first Request so\n the loop has work; an imported project with no Request sits idle until\n submit_request(project_id, ...) adds one.\n\n `project_kind` is OPTIONAL — one of library | node_library | python_library |\n service | cli | web_app | godot_game | roblox_game | unknown. Omit it and the\n kind is auto-detected. It is required for a MONOREPO (apps in\n subdirectories), a stack with no standard root manifest (Java, Ruby, PHP,\n .NET, Elixir), or a classification detection cannot infer — detection yields\n \"unknown\" there, which BLOCKS the dev lane until\n someone overrides it. A supplied value is authoritative and is never\n overwritten by later auto-detection. `stack` is the OPTIONAL language axis\n (python | node | rust | go | cpp) for a language-agnostic kind.\n `preview_command` is REQUIRED when project_kind is \"web_app\" (the command\n that serves the app locally, e.g. \"npm run dev\") and optional otherwise,\n where it overrides the detected one.\n\n Re-importing the same live repo is replay-guarded (returns the existing\n project with already_exists=True), and re-importing a SOFT-DELETED repo\n RESTORES that deleted row in place — it does not create a fresh project.\n Restored queued work can include refinement that deletes tasks, and the call\n is not idempotent end to end. On a plan with no scheduled-project allowance\n the project is still created but with the loop OFF — next_step then names\n get_billing(). Otherwise next_step names the poll of\n get_provisioning_status(project_id).\n ",
"inputSchema": {
"properties": {
"build_description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Build Description"
},
"engine": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Engine"
},
"preview_command": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Preview Command"
},
"project_kind": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Project Kind"
},
"repo_full_name": {
"title": "Repo Full Name",
"type": "string"
},
"stack": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Stack"
}
},
"required": [
"repo_full_name"
],
"title": "import_projectArguments",
"type": "object"
},
"name": "import_project",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "import_projectOutput",
"type": "object"
}
},
{
"description": "See what the work is stuck on and waiting for a human decision about.\n\n `project_status` only COUNTS open escalations; this returns each one with its\n kind, reason, detail_md, recommended_action, and (when task-scoped) the\n blocked task's title + PR url. A card about a roadmap item carries its\n `roadmap_item_id` (null for a task-only, project-level or synthetic row),\n which matches the `id` in `list_roadmap`. A \"forever-paused\" project with no open\n `project_pause` row is surfaced as a synthetic `orphan:<project_id>` row.\n Each row carries a server-derived `task_retry_available` flag and an exact\n `next_tool`: eligible recovery blocks route to `retry_blocked_task`, roadmap\n parks to `rearm_roadmap_item`, platform conflicts to their structured\n resolver, stale work to `replan_task`, and only safe auxiliary cards to\n `resolve_escalation`.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "list_escalationsArguments",
"type": "object"
},
"name": "list_escalations",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "list_escalationsOutput",
"type": "object"
}
},
{
"description": "List repos the workspace's GitHub connection can see (for import_project).\n\n Each entry has full_name, default_branch, private, language, pushed_at. A\n `full_name` is what import_project(repo_full_name) connects. Reading the\n list can MINT a short-lived GitHub installation token, so this is not a pure\n read. Returns 409 if GitHub isn't connected yet — connect_github() is\n required first. A provider failure is translated into a typed, actionable\n error instead of passing GitHub's response through: a missing or suspended\n App installation, a revoked OAuth token, or a non-rate-limit permission\n denial is a 409 naming connect_github(); a recognized rate limit is a 429;\n other provider failures are a 502; a timeout or transport failure is a 503.\n ",
"inputSchema": {
"properties": {},
"title": "list_github_reposArguments",
"type": "object"
},
"name": "list_github_repos",
"outputSchema": {
"properties": {
"result": {
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Result",
"type": "array"
}
},
"required": [
"result"
],
"title": "list_github_reposOutput",
"type": "object"
}
},
{
"description": "List the projects in this workspace, with what each one is building.",
"inputSchema": {
"properties": {},
"title": "list_projectsArguments",
"type": "object"
},
"name": "list_projects",
"outputSchema": {
"properties": {
"result": {
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Result",
"type": "array"
}
},
"required": [
"result"
],
"title": "list_projectsOutput",
"type": "object"
}
},
{
"description": "See what is planned for a project and in what order.\n\n Queued rows also say whether the expand picker would elect them\n (`electable`) and, when not, why it skips them (`skip_reason`:\n awaiting_intake_thread | awaiting_clarification | crash_capped |\n noop_capped | crash_cooldown | noop_cooldown | held_on_open_pr | other).\n The first reason that applies, in that order, is the one returned.\n `awaiting_clarification` means the planner asked a planning question about\n the item and waits for the answer: `list_escalations` returns that card\n with the item's `roadmap_item_id`. A skipped item ahead of yours does not\n delay it.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
},
"status": {
"default": "queued",
"title": "Status",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "list_roadmapArguments",
"type": "object"
},
"name": "list_roadmap",
"outputSchema": {
"properties": {
"result": {
"items": {
"additionalProperties": true,
"type": "object"
},
"title": "Result",
"type": "array"
}
},
"required": [
"result"
],
"title": "list_roadmapOutput",
"type": "object"
}
},
{
"description": "Get a one-click, pre-authenticated dashboard sign-in link for the owner.\n\n The engine-connect step — and any dashboard task (billing card update, a\n project page) — needs a signed-in browser. Because this server has already\n authenticated the workspace owner, this mints a single-use magic-link login\n token and returns a `/login?token=…` deep link: opening it signs the user\n straight into the dashboard (no email round-trip, no password) and lands\n them where onboarding left off. The returned `login_url` works once and\n expires in 15 minutes; a repeat call mints a fresh one.\n ",
"inputSchema": {
"properties": {},
"title": "open_dashboardArguments",
"type": "object"
},
"name": "open_dashboard",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "open_dashboardOutput",
"type": "object"
}
},
{
"description": "Read a compact progress digest for one project (delivery, activity,\n holds, next decision).\n\n Returns the closed MCP projection: delivery counts and the five newest\n delivered items, a recent loop-activity count, current holds, the single\n highest-priority open decision, and `more_decisions`. Two steering fields\n are ALWAYS present: `next_action` is `\"call_tool\"` with\n `next_tool=\"list_escalations\"` when a decision exists, otherwise\n `\"none\"` with `next_tool: null`. `since` is an optional ISO-8601\n timestamp with a timezone; it defaults to 24 hours ago and is clamped to\n the last seven days. This tool is **off by default and not generally\n available**: it is registered only when the server was started with\n `KEELEN_PROGRESS_DIGEST_ENABLED` enabled, and a live disable refuses\n calls until restart. Tenant-scoped: a project outside the caller's\n workspace 404s.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
},
"since": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Since"
}
},
"required": [
"project_id"
],
"title": "project_digestArguments",
"type": "object"
},
"name": "project_digest",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "project_digestOutput",
"type": "object"
}
},
{
"description": "Check how a project is doing: what is in flight, what shipped, what is stuck.\n\n Returns lifecycle status, open task count, queued roadmap item count, runs\n today, verification pass rate, open blockers, pause state, and freshness.\n Zero `open_tasks` does not mean an empty roadmap: `queued_roadmap_items`\n counts planned work awaiting expansion, including work held while the\n scheduler is disabled. list_roadmap returns those items.\n\n `glm_peak_paused` is NOT a fault and sets no pause columns: it is the\n ephemeral GLM peak-hours skip. True means clean PRs hold and runs stop\n until `glm_peak_resumes_at`. It is an intentional cost gate, so the accurate\n description is \"waiting for off-peak\", not a failure.\n\n For `web_app` projects the optional `ui_review` block can mint a GitHub\n installation token and read the default branch's `ui-review.json`, so this\n status call is not guaranteed to be a purely local read.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "project_statusArguments",
"type": "object"
},
"name": "project_status",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "project_statusOutput",
"type": "object"
}
},
{
"description": "Run the dashboard's replay-guarded same-item dependency re-arm.\n\n ``escalation_id`` is an ``expand_produced_nothing`` or\n ``roadmap_item_parked`` escalation id. Keelen verifies delivered structural\n prerequisites, grants one bounded\n expand retry, preserves the roadmap item id and ``depends_on_item_ids``, and\n resolves the matching cards in one transaction. A bounded live GitHub check\n may be used when the local merge state is stale, so a call can reach out\n even when the re-arm itself changes nothing.\n ",
"inputSchema": {
"properties": {
"escalation_id": {
"title": "Escalation Id",
"type": "string"
}
},
"required": [
"escalation_id"
],
"title": "rearm_roadmap_itemArguments",
"type": "object"
},
"name": "rearm_roadmap_item",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "rearm_roadmap_itemOutput",
"type": "object"
}
},
{
"description": "Say what is wrong with what a request produced, and have it reworked.\n\n Status must be \"done\". `feedback_md` is 1..2000 chars. Moves the thread to\n refine_pending; get_request_status reports the revised items. Refinement\n re-plans the thread's roadmap items and can EDIT OR HARD-DELETE the task rows\n it generated earlier (the machine patch-tasks endpoint deletes them\n outright), so it is destructive: up to five refinements per thread each\n carry that power.\n ",
"inputSchema": {
"properties": {
"feedback_md": {
"title": "Feedback Md",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
},
"thread_id": {
"title": "Thread Id",
"type": "string"
}
},
"required": [
"project_id",
"thread_id",
"feedback_md"
],
"title": "refine_requestArguments",
"type": "object"
},
"name": "refine_request",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "refine_requestOutput",
"type": "object"
}
},
{
"description": "Change what gets built first.\n\n `ordered_ids` is the desired front-to-back order of queued roadmap-item ids\n (list_roadmap returns them). The first id becomes the highest priority —\n the cadence expands the lowest-priority_int queued item next. Horizon pins\n still dominate: a pinned-later item stays at the back and a pinned-now item\n at the front, regardless of position in `ordered_ids`. Ids that are unknown\n or no longer queued are skipped; duplicates are rejected. Returns {updated,\n queue, next_step}.\n ",
"inputSchema": {
"properties": {
"ordered_ids": {
"items": {
"type": "string"
},
"title": "Ordered Ids",
"type": "array"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id",
"ordered_ids"
],
"title": "reorder_roadmapArguments",
"type": "object"
},
"name": "reorder_roadmap",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "reorder_roadmapOutput",
"type": "object"
}
},
{
"description": "Replace an unretryable blocked task without losing its lineage.\n\n Creates a fresh, explicitly planned task under the same roadmap item and\n attempt lineage, transfers prerequisites and downstream dependents, and\n cancels the stale task with supersession provenance. The old PR, task,\n failures, criteria, and evidence remain in the audit history; nothing is\n marked delivered.\n ",
"inputSchema": {
"properties": {
"acceptance_criteria": {
"items": {
"type": "string"
},
"title": "Acceptance Criteria",
"type": "array"
},
"body_md": {
"title": "Body Md",
"type": "string"
},
"decision_md": {
"title": "Decision Md",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
},
"task_id": {
"title": "Task Id",
"type": "string"
},
"title": {
"title": "Title",
"type": "string"
}
},
"required": [
"project_id",
"task_id",
"title",
"body_md",
"acceptance_criteria",
"decision_md"
],
"title": "replan_taskArguments",
"type": "object"
},
"name": "replan_task",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "replan_taskOutput",
"type": "object"
}
},
{
"description": "Acknowledge a handled ask only when no blocked task becomes invisible.\n\n `decision_md` is a required short note (why/how it was resolved), appended to\n the escalation's detail_md as an audit trail. Resolving a project_pause /\n orphan_pause RESUMES the project (clears the pause). A blocked task's final\n task_block/operator_action cannot be acknowledged: its typed retry,\n platform-policy resolution, replan, close, or supersede operation is the\n correct path instead.\n The acknowledgement itself is replay-guarded (a repeat changes nothing), but\n the call is not idempotent end to end: resuming a paused project lets queued\n refinement run, and that refinement can DELETE tasks. Accepts a real\n escalation UUID or a synthetic `orphan:<project_id>`.\n ",
"inputSchema": {
"properties": {
"decision_md": {
"title": "Decision Md",
"type": "string"
},
"escalation_id": {
"title": "Escalation Id",
"type": "string"
}
},
"required": [
"escalation_id",
"decision_md"
],
"title": "resolve_escalationArguments",
"type": "object"
},
"name": "resolve_escalation",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "resolve_escalationOutput",
"type": "object"
}
},
{
"description": "Resolve a platform-policy dead-end with a structured plan decision.\n\n ``decision`` is one of ``reuse_existing_evidence``, ``split_task``,\n ``raise_project_cap``, or ``remove_scenario``. ``raise_project_cap`` also\n requires ``project_cap`` (6..32). The decision becomes a new task-plan\n section; the same task lineage is requeued before the escalation resolves.\n\n On a project that runs the ui-review evidence lifecycle (``ui-review.json``\n is a retained evidence catalog), ``raise_project_cap`` changes the\n per-run capture budget -- how many scenarios one capture run holds -- not\n catalog capacity: the catalog has no scenario limit, so no decision is\n needed to make room in it.\n\n On a ``web_app`` task, ``reuse_existing_evidence`` also repairs a frozen\n theme-only collision in the same transaction: criteria whose evidence specs\n differ only by a light/dark variant token (for example\n ``setup-timeline-light`` and ``setup-timeline-dark``) are refrozen to one\n shared cell that lists the required ``themes``, the old and new specs are\n audited, and the plan tells the worker to cover that cell with one\n scenario. Criteria on one route, state and variant that name different\n ready selectors are likewise refrozen to one selector. Every criterion\n keeps its row and alias; unrelated specs are not changed.\n ",
"inputSchema": {
"properties": {
"decision": {
"title": "Decision",
"type": "string"
},
"decision_md": {
"title": "Decision Md",
"type": "string"
},
"escalation_id": {
"title": "Escalation Id",
"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",
"type": "object"
},
"name": "resolve_platform_policy_conflict",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "resolve_platform_policy_conflictOutput",
"type": "object"
}
},
{
"description": "Grant one fresh attempt after fixing a recovery-budget task block.\n\n ``escalation_id`` is the task_block id returned by ``list_escalations``.\n Eligibility, tenant, task, failure class, task status, and competing blockers\n are all derived server-side. This is distinct from ``resolve_escalation``,\n which remains acknowledgement-only for task blocks. Business-level replay guard:\n an already-retried block is not retried again; the call is not idempotent end\n to end, because an authenticated request also persists credential-use state.\n ",
"inputSchema": {
"properties": {
"decision_md": {
"title": "Decision Md",
"type": "string"
},
"escalation_id": {
"title": "Escalation Id",
"type": "string"
}
},
"required": [
"escalation_id",
"decision_md"
],
"title": "retry_blocked_taskArguments",
"type": "object"
},
"name": "retry_blocked_task",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "retry_blocked_taskOutput",
"type": "object"
}
},
{
"description": "Roll a Roblox project's place back to a previously-published version.\n\n For a `roblox_game` project, re-publishes the RETAINED build artifact for\n `version_number` (the web Roblox Publishing card lists published versions)\n — it never rebuilds from source, so rollback is fast + deterministic. This\n mints a NEW Roblox version pointing at the old build. Unknown version → 404;\n a version with no retained artifact → 422; a place open in Studio /\n rate-limited → 409 (a retry can succeed); an invalid or unscoped Open Cloud\n key → 409 / 403. Returns {version_number, env, published_at, status,\n next_step}.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
},
"version_number": {
"title": "Version Number",
"type": "integer"
}
},
"required": [
"project_id",
"version_number"
],
"title": "rollback_roblox_placeArguments",
"type": "object"
},
"name": "rollback_roblox_place",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "rollback_roblox_placeOutput",
"type": "object"
}
},
{
"description": "Start a source-bound security control gap review for one project.\n\n The review records bounded engineering observations under the user's\n selected Cyber Essentials or CMMC Level 1 or Level 2 context. It does not determine framework\n standing, and it does not make changes. The `disclaimer_md` field must\n reach the user before any observation is summarised.\n\n The project needs a saved framework profile, an eligible plan, and a place\n on the operator allowlist. Billable; one review is allowed in flight per\n project. Refusals return `ok: false` with a next step and do not start work.\n Rate-limited per workspace.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "run_control_gap_reviewArguments",
"type": "object"
},
"name": "run_control_gap_review",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "run_control_gap_reviewOutput",
"type": "object"
}
},
{
"description": "Find where a codebase creates legal exposure (privacy, consent, data handling).\n\n Spawns a one-shot review machine that reads the checkout offline and maps\n what the code DOES onto commonly cited legal obligations, filtered by the\n project's saved compliance profile (jurisdictions plus eleven product\n facts). Findings land on the project's Legal page for a human to triage,\n readable with get_legal_exposure_findings. No change is ever applied\n automatically.\n\n THIS IS NOT LEGAL ADVICE AND IT IS NOT A LEGAL CLEARANCE. The result\n carries a `disclaimer_md` field that must reach the user before any summary.\n The review is not exhaustive, so an empty result is never proof that\n anything is in order.\n\n Requires a saved compliance profile (409-shaped refusal without one),\n a plan that carries the feature, and the project on the operator allowlist.\n Billable; one review in flight per project. Every refusal comes back as\n `ok: false` with an actionable `next_step`, and starts nothing.\n Rate-limited per workspace.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "run_legal_exposure_reviewArguments",
"type": "object"
},
"name": "run_legal_exposure_review",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "run_legal_exposure_reviewOutput",
"type": "object"
}
},
{
"description": "Find security problems in a repository: a deep, whole-codebase review.\n\n Spawns a one-shot audit that scans the repo across a kind-aware taxonomy\n (secrets + git history, vulnerable/abandoned deps, injection, SSRF, path\n traversal, deserialization, crypto, info-leak, plus web authz/session/CORS,\n library API-misuse, game client-trust, or infra/CI as applicable) and posts\n findings to the project's Security review for human triage. The findings\n are reviewed by a human and the ones worth fixing go into the loop as\n Requests; no fix is applied automatically. Billable; one audit in-flight\n per project.\n (Triggering is disabled while the feature is hardened for production: a\n project not on the operator allowlist — empty by default — returns a message\n instead of spawning; earlier results stay visible.) Requires a paid plan;\n a free or trial workspace gets a message telling the user to upgrade.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id"
],
"title": "run_security_reviewArguments",
"type": "object"
},
"name": "run_security_review",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "run_security_reviewOutput",
"type": "object"
}
},
{
"description": "Set the outcome to aim at right now, so work is prioritised toward one thing.\n\n `goal_md` is free-form markdown and must be non-empty. Tenant-scoped: a\n project not in the caller's workspace 404s. Returns {project_id,\n product_goal_md, updated_at, next_step}.\n ",
"inputSchema": {
"properties": {
"goal_md": {
"title": "Goal Md",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id",
"goal_md"
],
"title": "set_product_goalArguments",
"type": "object"
},
"name": "set_product_goal",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "set_product_goalOutput",
"type": "object"
}
},
{
"description": "Say what this product is for, so every run knows what it is building toward.\n\n `vision_md` is free-form markdown and must be non-empty. Tenant-scoped: a\n project not in the caller's workspace 404s. Returns {project_id,\n product_vision_md, updated_at, next_step}.\n ",
"inputSchema": {
"properties": {
"project_id": {
"title": "Project Id",
"type": "string"
},
"vision_md": {
"title": "Vision Md",
"type": "string"
}
},
"required": [
"project_id",
"vision_md"
],
"title": "set_product_visionArguments",
"type": "object"
},
"name": "set_product_vision",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "set_product_visionOutput",
"type": "object"
}
},
{
"description": "Set this web_app project's ui-review scenario budget, or reset it.\n\n ``scenario_cap`` is an integer from 6 through 32, or ``null`` to reset to\n the platform default of 12. The screenshot cap derives from it and moves\n with it, so the two can never starve each other.\n\n Raising is always allowed, including from a completely full manifest.\n LOWERING is refused when the default branch already declares more scenarios\n or screenshots than the smaller budget allows, and is also refused when that\n manifest cannot be read — both caps are enforced when keelen pushes and not\n in your CI, so an over-cap manifest fails every push while CI stays green.\n\n On a project with the retained evidence catalog (the response carries\n ``evidence_lifecycle: true``) the same parameter is a CAPTURE-RUN setting:\n it limits how many scenarios and screenshots one capture run holds, a pull\n request that needs more is split into capture batches, and the catalog\n keeps every scenario whatever the value. The response then also reports\n ``catalog`` (the retained counts) separately from ``run_limits``, and a\n lower is not refused for catalog size once the catalog is migrated.\n\n ``project_status.ui_review`` reports the live occupancy. Repeated calls with\n the same value change nothing.\n ",
"inputSchema": {
"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",
"type": "object"
},
"name": "set_ui_review_scenario_cap",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "set_ui_review_scenario_capOutput",
"type": "object"
}
},
{
"description": "Create a Keelen account (or start agent login) — emails a 6-digit code.\n\n UNAUTHENTICATED — the only tool besides verify_email that works before a\n bearer key is configured. `email` is where the code is sent. Flow:\n signup(email) -> the user reads the 6-digit code from their inbox ->\n verify_email(email, code) returns a reveal-once API key, stored as this\n server's `Authorization: Bearer <api_key>` header in the MCP client config;\n reconnecting the client and calling get_onboarding_status() then continues\n setup. The code expires in 15 minutes; a repeat signup resends it. Response\n is uniform whether or not the email already has an account\n (enumeration-safe), so signup doubles as agent LOGIN. Rate-limited per IP\n and per email.\n\n The `email` must be explicitly provided and confirmed by the user in chat\n before this call. It is not inferred from a client profile, the logged-in\n account, git config, or any other ambient source; a candidate address the\n caller already holds is echoed back for an explicit yes first. Because this\n call doubles as LOGIN, a guessed address signs the user in to whatever\n workspace owns it, and the rest of setup then mints an API key on, and\n creates a project in, an account they did not choose.\n ",
"inputSchema": {
"properties": {
"email": {
"title": "Email",
"type": "string"
}
},
"required": [
"email"
],
"title": "signupArguments",
"type": "object"
},
"name": "signup",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "signupOutput",
"type": "object"
}
},
{
"description": "Ask for a change in plain English: a feature, a bug fix, or a new direction.\n\n Each call carries ONE feature or intent; a multi-feature ask belongs in\n separate requests. `text` must be under 16000 characters. Returns\n {thread_id, status, next_action, poll_after_seconds, next_step}; next_step\n names the re-check of get_request_status after poll_after_seconds.\n ",
"inputSchema": {
"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",
"type": "object"
},
"name": "submit_request",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "submit_requestOutput",
"type": "object"
}
},
{
"description": "Redeem the emailed 6-digit code for a reveal-once workspace API key.\n\n UNAUTHENTICATED. `email` + `code` must match a code issued by signup(email)\n within the last 15 minutes (5 attempts max). The returned `api_key` is shown\n exactly ONCE — store it ONLY in the MCP client config\n (\"Authorization: Bearer <api_key>\"), NEVER in a repo or a file you might\n commit. Reconnecting this server with the header set and calling\n get_onboarding_status() continues setup. An invalid/expired/consumed code\n returns a uniform error; a fresh code comes from signup(email).\n ",
"inputSchema": {
"properties": {
"code": {
"title": "Code",
"type": "string"
},
"email": {
"title": "Email",
"type": "string"
}
},
"required": [
"email",
"code"
],
"title": "verify_emailArguments",
"type": "object"
},
"name": "verify_email",
"outputSchema": {
"properties": {
"result": {
"additionalProperties": true,
"title": "Result",
"type": "object"
}
},
"required": [
"result"
],
"title": "verify_emailOutput",
"type": "object"
}
}
]
}Verify it yourself
curl -s https://api.teppi.xyz/v1/evidence/sha256:3242e565794b7bd59a091ab2fcadbd90a010914e4a34a03b5d9ebf5a932f718c | sha256sum