# Keelen (remote · keelen.ai)

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

- Trust score: 61/100 (medium)
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-21

> **Recent critical change**: Authorization (2026-08-21). See the changelog below before you install this server.

## Components

- remote · `keelen.ai`: 61/100 (this document), [markdown](https://verifymcp.io/servers/jamie7893-keelen/keelen.md), [page](https://verifymcp.io/servers/jamie7893-keelen/keelen)

## Channel facts

- Endpoint: `https://keelen.ai/mcp`
- Transports: `streamable-http`
- Auth: `none`
- Version: `1.0.0`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-21.

- **Endpoint Security**: 63/100
  - The endpoint's TLS certificate is valid, in date, and uses a strong key.
  - Authorisation check failed: no authorisation is required to call this server, and it exposes a tool marked destructive (cancel_roadmap_item).
  - HTTPS is enforced; there's no plaintext access path.
  - The HSTS (Strict-Transport-Security) header is present.
  - DNSSEC check failed: this domain isn't protected by DNSSEC.
- **Transport & Reachability**: 100/100
  - Verified streamable-http transport via a live MCP handshake.
- **Schema Quality & AI Usability**: 64/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 7305 tokens (~221/item across 33 items; 33 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 7/100
  - Stability observed for 2 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 71/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### Claude

```bash
claude mcp add --transport http jamie7893-keelen https://keelen.ai/mcp
```

### Codex

```toml
[mcp_servers.jamie7893-keelen]
url = "https://keelen.ai/mcp"
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "jamie7893-keelen": {
      "type": "remote",
      "url": "https://keelen.ai/mcp",
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add jamie7893-keelen --url https://keelen.ai/mcp --transport streamable-http
```

### Hermes

```yaml
mcp_servers:
  jamie7893-keelen:
    url: "https://keelen.ai/mcp"
```

### Other

```json
{
  "mcpServers": {
    "jamie7893-keelen": {
      "type": "http",
      "url": "https://keelen.ai/mcp"
    }
  }
}
```

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-21 (score 61, 0)

- [critical regression] Authorization: unverified → fail
- [security] Tool “connect_github” rewrote its description, which is the text the model reads
- [security] Tool “control_scheduler” rewrote its description, which is the text the model reads
- [security] Tool “create_project” rewrote its description, which is the text the model reads
- [security] Tool “get_onboarding_status” rewrote its description, which is the text the model reads
- [security] Tool “get_provisioning_status” rewrote its description, which is the text the model reads
- [security] Tool “get_request_status” rewrote its description, which is the text the model reads
- [security] Tool “list_escalations” rewrote its description, which is the text the model reads
- [security] Tool “list_projects” rewrote its description, which is the text the model reads
- [security] Tool “list_roadmap” rewrote its description, which is the text the model reads
- [security] Tool “project_status” rewrote its description, which is the text the model reads
- [security] Tool “refine_request” rewrote its description, which is the text the model reads
- [security] Tool “reorder_roadmap” rewrote its description, which is the text the model reads
- [security] Tool “resolve_escalation” rewrote its description, which is the text the model reads
- [security] Tool “run_legal_exposure_review” rewrote its description, which is the text the model reads
- [security] Tool “run_security_review” rewrote its description, which is the text the model reads
- [security] Tool “set_product_goal” rewrote its description, which is the text the model reads
- [security] Tool “set_product_vision” rewrote its description, which is the text the model reads
- [security] Tool “submit_request” rewrote its description, which is the text the model reads
- [security] Tool “archive_project” is now declared destructive
- [security] Tool “cancel_roadmap_item” is now declared destructive
- [security] Tool “delete_project” is now declared destructive
- [security] Tool “rollback_roblox_place” is now declared destructive

### 2026-08-20 (score 61, 0)

- [security] The server rewrote its instructions, which are the text every model session reads
- [security] Tool “signup” rewrote its description, which is the text the model reads
- [functional improvement] Stability: unverified → 0.03

### 2026-08-19 (score 61)

First indexed and scored.

## MCP tools (33)

### `list_projects` (~22 tokens)

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

Output parameters:

- `result` (array)

### `create_project` (~644 tokens)

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 (defaults to claude_code);
    codex, glm, and kimi 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 —…

Input parameters:

- `build_description` (string, required)
- `ci_runs_on`
- `engine`
- `framework`
- `name` (string, required)
- `org`
- `preview_command`
- `private` (boolean)
- `project_kind` (string, required)

Output parameters:

- `result` (object)

### `submit_request` (~110 tokens)

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 parameters:

- `project_id` (string, required)
- `text` (string, required)

Output parameters:

- `result` (object)

### `get_request_status` (~116 tokens)

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 parameters:

- `project_id` (string, required)
- `thread_id` (string, required)

Output parameters:

- `result` (object)

### `set_product_vision` (~94 tokens)

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 parameters:

- `project_id` (string, required)
- `vision_md` (string, required)

Output parameters:

- `result` (object)

### `set_product_goal` (~93 tokens)

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 parameters:

- `goal_md` (string, required)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `list_roadmap` (~37 tokens)

See what is planned for a project and in what order.

Input parameters:

- `project_id` (string, required)
- `status` (string)

Output parameters:

- `result` (array)

### `reorder_roadmap` (~145 tokens)

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 parameters:

- `ordered_ids` (array, required)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `cancel_roadmap_item` (~169 tokens)

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. Idempotent for an already expanded/cancelled item; refuses
    (409) while the item is actively being expanded (retry once that iter ends).
    Returns {id, status, changed, next_step}.

Input parameters:

- `item_id` (string, required)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `clear_horizon_pin` (~184 tokens)

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 parameters:

- `item_id` (string, required)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `rollback_roblox_place` (~175 tokens)

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 parameters:

- `project_id` (string, required)
- `version_number` (integer, required)

Output parameters:

- `result` (object)

### `project_status` (~136 tokens)

Check how a project is doing: what is in flight, what shipped, what is stuck.

    Returns lifecycle status, open task count, runs today, verification pass
    rate, open blockers, pause state, and freshness.

    `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.

Input parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `list_escalations` (~127 tokens)

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.
    Pass any returned `id` to resolve_escalation.

Input parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `resolve_escalation` (~140 tokens)

Clear a blocker once you have dealt with it, so work can continue.

    `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 task_block /
    operator_action is acknowledged only (the task keeps its board status).
    Idempotent. Accepts a real escalation UUID or a synthetic `orphan:<project_id>`
    id from list_escalations.

Input parameters:

- `decision_md` (string, required)
- `escalation_id` (string, required)

Output parameters:

- `result` (object)

### `control_scheduler` (~135 tokens)

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 forces the next intake batch (no-op if the scheduler is off or
      no intake_pending request is queued).
    Returns the resulting scheduler state.

Input parameters:

- `action` (string, required)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `archive_project` (~145 tokens)

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. Idempotent. 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 parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `delete_project` (~130 tokens)

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. The row is retained for audit but there is NO in-tool
    restore (unlike `archive_project`) — re-import the repo to reconnect it as a
    fresh project. Idempotent. Owner-scoped; a project not in the workspace 404s.

Input parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `run_security_review` (~225 tokens)

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 parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `run_legal_exposure_review` (~257 tokens)

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 parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `get_legal_exposure_findings` (~243 tokens)

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 parameters:

- `include_all` (boolean)
- `limit` (integer)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `run_control_gap_review` (~160 tokens)

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 parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `get_control_gap_findings` (~160 tokens)

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 parameters:

- `include_all` (boolean)
- `limit` (integer)
- `project_id` (string, required)

Output parameters:

- `result` (object)

### `answer_request` (~103 tokens)

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 parameters:

- `answers` (array, required)
- `project_id` (string, required)
- `thread_id` (string, required)

Output parameters:

- `result` (object)

### `refine_request` (~87 tokens)

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.

Input parameters:

- `feedback_md` (string, required)
- `project_id` (string, required)
- `thread_id` (string, required)

Output parameters:

- `result` (object)

### `signup` (~309 tokens)

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 parameters:

- `email` (string, required)

Output parameters:

- `result` (object)

### `verify_email` (~163 tokens)

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 parameters:

- `code` (string, required)
- `email` (string, required)

Output parameters:

- `result` (object)

### `get_onboarding_status` (~293 tokens)

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.

Output parameters:

- `result` (object)

### `open_dashboard` (~148 tokens)

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.

Output parameters:

- `result` (object)

### `connect_github` (~104 tokens)

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.

Output parameters:

- `result` (object)

### `list_github_repos` (~82 tokens)

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. Returns 409 if
    GitHub isn't connected yet — call connect_github() first.

Output parameters:

- `result` (array)

### `import_project` (~522 tokens)

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 (defaults to
    claude_code); codex/glm/kimi 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 repo is idempotent (returns the existing project with
    already_exists=True). 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…

Input parameters:

- `build_description`
- `engine`
- `preview_command`
- `project_kind`
- `repo_full_name` (string, required)
- `stack`

Output parameters:

- `result` (object)

### `get_provisioning_status` (~141 tokens)

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' (which also marks onboarding complete) — then steer the
    loop with submit_request(project_id, text). Tenant-scoped: a project not in
    the caller's workspace 404s.

Input parameters:

- `project_id` (string, required)

Output parameters:

- `result` (object)

### `get_billing` (~202 tokens)

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 parameters:

- `plan` (string)

Output parameters:

- `result` (object)

## Diagnostics

Captured diagnostic sections: TLS, DNSSEC, Authorisation, Transports. The full working is on the page: https://verifymcp.io/servers/jamie7893-keelen/keelen#diagnostics

## Score history

- 2026-08-21: 61
- 2026-08-20: 61
- 2026-08-19: 61

## Links

- Remote endpoint: https://keelen.ai/mcp
- Repository: https://github.com/jamie7893/keelen-mcp
- Changelog RSS feed: https://verifymcp.io/servers/jamie7893-keelen/keelen.xml
- Changelog JSON feed: https://verifymcp.io/servers/jamie7893-keelen/keelen.json
- HTML version of this page: https://verifymcp.io/servers/jamie7893-keelen/keelen
