Skip to content

feat(kilo-mcp): publish all non-admin/debug mutations - #6315

Merged
iscekic merged 5 commits into
mainfrom
fix/kilo-mcp-publish-non-admin-mutations
Sep 18, 2026
Merged

iscekic merged 5 commits into
mainfrom
fix/kilo-mcp-publish-non-admin-mutations

Conversation

@iscekic

@iscekic iscekic commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

PR #6224 added mutation support to the Kilo MCP, but the catalog only published the 10 paths in MCP_MUTATION_ALLOWLIST (agentProfiles). Every other mutation was invisible to search and rejected by call.

Root cause

buildCatalogRows gated mutations on an exact-path allowlist:

const kept = leaf.type === 'query' || (leaf.type === 'mutation' && isAllowedMutation(leaf.path));

The rootRouter exposes 671 mutations. 517 are non-denylisted (top-level segment not admin, debug, or test); only 10 were published. This is a catalog-scope bug, not a deployment failure: the catalog-merge job for #6224 succeeded and its Upsert Vectorize index step passed (run 35289442683).

Fix

  • Remove mutations.ts (MCP_MUTATION_ALLOWLIST, isAllowedMutation) and its exact-path gate. buildCatalogRows now keeps every query and mutation that survives the existing top-level denylist, and still drops subscriptions.
  • Replace the allowlist rot guard with a mutation drift guard: an enumeration that publishes no mutation row throws instead of emitting a query-only catalog.
  • Regenerate services/kilo-mcp/catalog.json: 897 rows (380 queries, 517 mutations), every row with a summary. catalog.test.ts asserts the committed catalog regenerates byte-for-byte and that every non-denylisted router mutation is present.

Verification

  • apps/web jest src/scripts/mcp-catalog: 31 passed.
  • services/kilo-mcp vitest: 282 passed.
  • node --test scripts/kilo-mcp-catalog.test.mjs: 29 passed.
  • oxlint and oxfmt --list-different clean on the changed files.
  • tsgo --noEmit reports no error in mcp-catalog (remaining output is pre-existing, in unrelated packages).

Note

The denylist is top-level-segment based, as before. Nested paths whose segment name merely contains admin (for example codingPlans.adminMarkRevocationFailed) stay published, consistent with how queries are already filtered.

The catalog published only the 10 allowlisted agentProfiles mutations
(MCP_MUTATION_ALLOWLIST). The router exposes 671 mutations and 517 are
non-denylisted (non admin/debug/test), so every other mutation was
invisible to search and call.

- Drop MCP_MUTATION_ALLOWLIST and its exact-path gate; buildCatalogRows
  keeps every query and mutation that survives the top-level denylist
  (admin, debug, test) and still drops subscriptions.
- Replace the allowlist rot guard with a mutation drift guard: a
  query-only enumeration throws instead of silently removing write
  support from every MCP client.
- Regenerate services/kilo-mcp/catalog.json: 897 rows (380 queries, 517
  mutations), every row with a summary.

The Vectorize upsert job for the original PR (#6224) succeeded, so the
missing mutations were a catalog-scope bug, not a failed index update.
@iscekic iscekic self-assigned this Sep 18, 2026
Comment thread apps/web/src/scripts/mcp-catalog/catalog.ts
Comment thread apps/web/src/scripts/mcp-catalog/catalog.ts Outdated
@kilo-code-bot

kilo-code-bot Bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The follow-up commit ee6b15bfc resolves the prior finding: the object-merge branch now runs only when every chained schema carries no keyword beyond $schema/type/properties/required, and any chain carrying .strict()'s additionalProperties: false, .passthrough(), or intersection keywords falls back to a top-level allOf that preserves each schema's own keywords. I verified the regenerated catalog (853 rows, 32 intersection rows) and the previously flagged rows, and found no new issues in the changed code.

Files Reviewed (3 files)
  • apps/web/src/scripts/mcp-catalog/catalog.ts
  • apps/web/src/scripts/mcp-catalog/catalog.test.ts
  • services/kilo-mcp/catalog.json (generated)
Previous Review Summaries (3 snapshots, latest commit a544a6f)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit a544a6f)

Status: 1 Issues Found | Recommendation: Address before merge

Executive Summary

The chained-input schema composition added in 7da532c8 merges object inputs correctly for properties/required but silently drops other top-level keywords, so strict chained procedures publish a schema more permissive than the upstream tRPC validator.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
apps/web/src/scripts/mcp-catalog/catalog.ts 186 Merged schema drops additionalProperties: false (and other top-level keywords) from chained .input() schemas
Files Reviewed (4 files)
  • apps/web/src/scripts/mcp-catalog/catalog.ts - 1 issue
  • apps/web/src/scripts/mcp-catalog/catalog.test.ts
  • services/kilo-mcp/catalog.json (generated)
  • REVIEW.md

Fix these issues in Kilo Cloud

Previous review (commit 994fed8)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental review of the follow-up commit: both prior warnings are resolved. isDenylistedPath now drops internal segments (admin/debug/dev/test) anywhere in the path, and the drift guard is renamed and documented to match its intended semantics (counts exposed mutation procedures, not emitted rows).

Verified against the regenerated catalog: 853 rows (491 mutations, 362 queries), zero denylisted paths present, every row has a non-empty summary with a path-bearing searchBlob, and user-facing calls such as slack.testConnection/discord.testConnection remain published. Test suites were not executed in this read-only session.

Files Reviewed (3 files)
  • apps/web/src/scripts/mcp-catalog/catalog.ts
  • apps/web/src/scripts/mcp-catalog/catalog.test.ts
  • services/kilo-mcp/catalog.json (generated)

Previous review (commit 95b2a85)

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

Publishing every non-top-level-denylisted mutation also exposes nested admin/dev procedures (e.g. organizations.admin.grantCredit, *.devRemoveDbRowOnly) to MCP clients, and the replacement drift guard can pass while emitting a query-only catalog.

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/web/src/scripts/mcp-catalog/catalog.ts 186 Top-level-only denylist admits nested admin/dev mutations into the MCP catalog
apps/web/src/scripts/mcp-catalog/catalog.ts 188 Drift guard counts mutation leaves, so query-only rows can still be emitted without throwing
Files Reviewed (6 files)
  • apps/web/src/scripts/mcp-catalog/catalog.ts - 2 issues
  • apps/web/src/scripts/mcp-catalog/catalog.test.ts
  • apps/web/src/scripts/mcp-catalog/dump.ts
  • apps/web/src/scripts/mcp-catalog/mutations.ts (deleted)
  • apps/web/src/scripts/mcp-catalog/catalog-rot-guard.test.ts (deleted)
  • services/kilo-mcp/catalog.json (generated)

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

Address the Kilo review on this PR.

- Match admin/debug/dev/test as path segments anywhere, not only at the
  top level. This drops 21 nested admin mutations, 5 dev-only mutations,
  and 18 nested admin queries that the previous predicate admitted.
  `test` stays exact-only so user-facing calls such as
  `slack.testConnection` remain published.
- Count exposed mutation procedures in the drift guard and word the error
  accordingly: a mutation without a committed summary is a legitimate
  `missing` entry that the dump generates, not a router drift.

Regenerate catalog.json: 853 rows (362 queries, 491 mutations).
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 18, 2026

@pandemicsyn pandemicsyn left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably have a preferred an opt-in/default-deny approach (like the orig) , let make sure we at least have a brief mention in REVIEWS.md or something to try and catch us leaking admin/sensitive routes.

Comment thread apps/web/src/scripts/mcp-catalog/catalog.ts
Comment thread apps/web/src/scripts/mcp-catalog/catalog.ts
The catalog published only the first `.input()` schema. A chained procedure
such as `workspaceFolders.create` adds a second `.input()` that requires
`name` and `color`. MCP clients saw `organizationId` alone, so a request
passed local validation and failed upstream.

Compose every chained input into one schema. Object schemas merge their
properties and required keys. A key that two schemas declare keeps both
constraints through `allOf`. A non-object chain falls back to a top-level
`allOf`. Tags and search blobs now cover every input field.

Regenerate catalog.json: 853 rows; 207 rows get expanded input schemas.
Review guidance from the #6315 review: ask for justification when a newly
published MCP mutation issues or rotates credentials or tokens, touches
secrets, changes organization membership or authorization, deploys, charges
or purchases, or deletes data.
Comment thread apps/web/src/scripts/mcp-catalog/catalog.ts
The flat merge kept only `properties` and `required`. A chained `.strict()`
input then lost `additionalProperties: false`, so the published schema
accepted unknown keys that upstream tRPC rejects.

Merge only plain object schemas. Any chain with a keyword such as
`additionalProperties` falls back to a top-level `allOf`, which preserves
each schema's own keywords. `organizations.cloudAgentNext.listRepositoryBranches`
and `organizations.groups.getPolicyEditorData` now publish their strict
constraint. Regenerate catalog.json: 32 rows use the faithful intersection.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants