Skip to content

feat(kilo-mcp): remote MCP worker with hybrid catalog search, tRPC calls, and OAuth 2.1 sign-in (part 1/1) - #6030

Open
iscekic wants to merge 13 commits into
mainfrom
kwf/kilo-remote-mcp-c204-l1
Open

feat(kilo-mcp): remote MCP worker with hybrid catalog search, tRPC calls, and OAuth 2.1 sign-in (part 1/1)#6030
iscekic wants to merge 13 commits into
mainfrom
kwf/kilo-remote-mcp-c204-l1

Conversation

@iscekic

@iscekic iscekic commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Changelog for users

  • Agents had no way to discover or invoke the Kilo API over MCP. A new Kilo MCP worker now serves MCP Streamable HTTP with exactly two tools: search and call.
  • search turns a plain-language task into matching Kilo endpoints: exact path and keyword matches rank first.
  • A Vectorize semantic index also returns the right endpoint for queries with zero keyword overlap. If the index is unavailable, search still answers from keyword matching instead of failing.
  • call executes a Kilo API query as the signed-in user. It accepts only catalog paths and validates arguments against the published schema before sending anything.
  • Results larger than 16 KB come back cut at the cap with a [truncated] marker.
  • Connecting a client runs MCP OAuth 2.1: dynamic client registration, sign-in with the existing Kilo login, an org picker, PKCE-protected token exchange, and refresh-token rotation.
  • Access tokens are bound to the user, the chosen org, and this MCP. Requests without a valid token get a 401 challenge pointing clients at the sign-in metadata.
  • The catalog publishes all 359 tRPC queries except admin.*, debug.*, and test.*, each with a search-friendly summary authors can hand-edit.

Changelog for maintainers

  • The dump in apps/web/src/scripts/mcp-catalog walks the live rootRouter (queries only, denylist admin/debug/test) and keeps committed summaries byte-for-byte. Missing summaries are generated per router file via OpenRouter or Anthropic; a corrupt committed catalog fails loudly, and an incomplete catalog is never written.
  • CI runs the catalog workflow on every PR and gates the work on a change-detection step, so catalog (PR) can be a required status check without leaving unrelated PRs blocked. Same-repo PRs get the refreshed services/kilo-mcp/catalog.json committed to the branch, which removes catalog drift. Fork PRs fail with a catalog.patch artifact and a comment, including when GitHub withholds the secret. The main-push job re-dumps and upserts Vectorize; PR jobs never touch it. scripts/kilo-mcp-catalog.test.mjs asserts this wiring.
  • The worker bundles the committed catalog at build time. The Vectorize index pins @cf/baai/bge-base-en-v1.5 (768 dimensions, cosine) with vector id = procedure path; only the search query is embedded per request. Production deploys from main keep the bundled catalog and the index in lockstep.
  • Search ranks exact-path hits above path-sequence hits above token overlap, and every lexical hit above a semantic-only hit. A Vectorize failure degrades to token-only results with a logged warning.
  • call rejects unknown paths and schema-invalid input before any upstream request. It forwards over apps/web's tRPC GET transport with the verified identity's Kilo credential and the org header from the token claims. x-kilocode-organizationid mirrors the apps/web constant — keep them in sync.
  • The OAuth 2.1 suite covers well-known metadata, public-client DCR, the /authorize pairing with consent page, a single-use status relay, and a membership-validated org picker. /token enforces single-use codes, S256 PKCE, and rotating hashed refresh tokens. State lives in one Durable Object (KiloMcpOAuthStore, DO SQLite, migration tag v1, 6-hour purge alarm).
  • /mcp accepts only this worker's HS256 tokens (issuer, audience, expiry, and jti registry checked). A deployment missing MCP_TOKEN_SECRET or the Durable Object binding refuses POSTs with 503 instead of forwarding unverified bearers.
  • Resolved: commit 97df6a80c opens the authorize page's Continue with Kilo sign-in link in a new tab (target="_blank" rel="noopener noreferrer"), so the pairing-status poll survives sign-in. The authorize screenshot above predates the fix.
  • The dump's kilo run never uses a maintainer's personal credential. Both jobs exchange MCP_CATALOG_TOKEN_SECRET at POST /api/internal/mcp-catalog/token for a 1-hour token that belongs to the benchmarking service account.

Human steps before merge and deploy

Needs a repo admin.

  • Add catalog (PR) to the main-tests ruleset required status checks. The automation token lacks repo admin and gets HTTP 404 from the rulesets API. GitHub UI: Settings → Rules → main-tests → Require status checks → add catalog (PR).

Needs the deployer.

  • Run pnpm web:env set MCP_CATALOG_TOKEN_SECRET --only production --production-file <(op read "op://Kilo Web ENV Production/MCP_CATALOG_TOKEN_SECRET/password") so Vercel serves the mint route. Agents must not run this command.
  • Approve the PR (1 review required).

Already done, no action: MCP_CATALOG_TOKEN_SECRET is in 1Password (Kilo Web ENV Production) and in GitHub repo secrets; the personal MCP_CATALOG_KILO_AUTH secret is deleted from GitHub and 1Password; MCP_TOKEN_SECRET is set on both kilo-mcp and kilo-mcp-dev; the kilo-mcp-catalog Vectorize index exists.

E2E proof (stack-wide)

e10-device-auth.png

e10-authorize.png

e11-expired-pairing.png

Owner request for the stack

Kilo Remote MCP

New Cloudflare Worker (services/kilo-mcp). Not MCP Gateway. Two tools: search, call.

Shape

  • Include every tRPC query except admin.*, debug.*, and test.*.
  • No mutations in v1. No per-procedure opt-in. Denylist, not allowlist.
  • Worker speaks MCP. It does not run tRPC.
  • apps/web stays the authorization server and the tRPC executor.
  • Product auth is MCP OAuth 2.1. The first slice ships without it.

Catalog

One dump script, in apps/web (only place rootRouter loads). Two jobs:

PR job

  1. Keep all queries minus the denylist.
  2. If a row has no summary, an LLM reads the procedure handler and traces what it does. It writes a search-friendly summary: what the call does, in words an agent would type. Not implementation detail. Not a restatement of the path. If a summary already exists, keep it.
  3. Emit catalog.json: path, kind, summary, JSON Schema, derived tags, search blob.
  4. Tags and keywords are derived (path, kind, schema keys). Do not author them.
  5. If catalog.json changed, CI commits it to the PR branch. Authors can edit a committed summary; CI will keep the edit.
  6. Fork PRs cannot be pushed; CI fails and posts the catalog patch.
  7. Do not generate summaries at MCP runtime. Do not write Vectorize.

Merge job (main only)

  1. Run the same dump (fill missing summaries if any slipped through).
  2. Embed every catalog row with one pinned Workers AI model. Upsert Vectorize. Vector id = procedure path.
  3. PRs do not write Vectorize. Do not embed per call.

Do not copy trpc-registry.ts. Do not put hand-written .meta({ mcp }) on each procedure.

Auth

  • MCP OAuth 2.1: PKCE, DCR, protected-resource metadata, resource indicator.
  • User signs in with existing Kilo login. Picks an org.
  • Token is bound to user + org + this MCP. Worker verifies. Forwards that identity to /api/trpc/{path}.

Agent loop

  1. search is hybrid: token overlap on the bundled catalog plus Vectorize kNN on the query embedding. Exact path/token hits rank above semantic hits.
  2. call accepts only catalog paths. Validates input against the published schema.
  3. Worker POSTs to /api/trpc/{path} as that user/org. Truncates the result.

Production Worker deploys from main, so bundled catalog and Vectorize stay in lockstep.

First slice

Bootstrap once: dump the query catalog (minus denylist), fill missing summaries, embed, upsert Vectorize. Prove dump → hybrid search → call on the Worker. No OAuth in this slice.

Owner manual verification

Owner verification is pending; CI did not prove these paths automatically.

  • Real GitHub PR run: same-repo PR gets a bot catalog.json commit; fork PR fails with catalog.patch. Not run here.
  • Open a fork PR touching apps/web/src/** without a summary and assert the job fails with the guidance comment. Needs a real fork PR.
  • The third original check (merge to main and observe the bot commit before the Vectorize upsert) is superseded: drift is removed because the PR job commits the catalog to the PR branch, and the merge job only fills straggler summaries.

PR stack (merge bottom to top)

@iscekic
iscekic marked this pull request as draft September 9, 2026 23:16
Comment thread services/kilo-mcp/src/auth/token.ts
Comment thread services/kilo-mcp/wrangler.jsonc
@kilo-code-bot

kilo-code-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit only applies oxfmt formatting to services/kilo-mcp/src/auth.test.ts and services/kilo-mcp/src/store/oauth-store.test.ts; there are no functional changes, so no new issues were found.

Files Reviewed (11 files)
  • .github/workflows/kilo-mcp-catalog.yml
  • scripts/kilo-mcp-catalog.test.mjs
  • services/kilo-mcp/src/auth.test.ts
  • services/kilo-mcp/src/auth/verify.test.ts
  • services/kilo-mcp/src/auth/verify.ts
  • services/kilo-mcp/src/call.test.ts
  • services/kilo-mcp/src/call.ts
  • services/kilo-mcp/src/index.test.ts
  • services/kilo-mcp/src/index.ts
  • services/kilo-mcp/src/store/oauth-store.test.ts
  • services/kilo-mcp/src/store/oauth-store.ts
Previous Review Summaries (7 snapshots, latest commit 867f373)

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

Previous review (commit 867f373)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (11 files)
  • .github/workflows/kilo-mcp-catalog.yml
  • scripts/kilo-mcp-catalog.test.mjs
  • services/kilo-mcp/src/auth.test.ts
  • services/kilo-mcp/src/auth/verify.test.ts
  • services/kilo-mcp/src/auth/verify.ts
  • services/kilo-mcp/src/call.test.ts
  • services/kilo-mcp/src/call.ts
  • services/kilo-mcp/src/index.test.ts
  • services/kilo-mcp/src/index.ts
  • services/kilo-mcp/src/store/oauth-store.test.ts
  • services/kilo-mcp/src/store/oauth-store.ts

Previous review (commit 556ff14)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • .github/workflows/kilo-mcp-catalog.yml
  • apps/web/src/app/api/internal/mcp-catalog/token/route.ts
  • apps/web/src/app/api/internal/mcp-catalog/token/route.test.ts
  • apps/web/src/lib/config.server.ts
  • docs/token-issuance-policy.md
  • scripts/kilo-mcp-catalog.test.mjs

Previous review (commit efedce1)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (1 file)
  • services/kilo-mcp/wrangler.jsonc

Previous review (commit d606033)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (2 files)
  • apps/web/src/scripts/mcp-catalog/catalog.ts
  • apps/web/src/scripts/mcp-catalog/catalog.test.ts

Previous review (commit 352b9f9)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
apps/web/src/scripts/mcp-catalog/catalog.ts 566 runKiloCompletion does not match the in-repo kilo run --format json contract
Files Reviewed (12 files)
  • apps/web/src/scripts/mcp-catalog/catalog.ts - 1 issue
  • apps/web/src/scripts/mcp-catalog/catalog.test.ts
  • apps/web/src/scripts/mcp-catalog/dump.ts
  • .github/workflows/kilo-mcp-catalog.yml
  • scripts/kilo-mcp-catalog.test.mjs
  • services/kilo-mcp/wrangler.jsonc
  • services/kilo-mcp/src/auth/http.ts
  • services/kilo-mcp/src/auth/authorize.test.ts
  • services/kilo-mcp/src/oauth-pages/authorize-page.ts
  • services/kilo-mcp/src/oauth-pages/authorize-page.test.ts
  • services/kilo-mcp/src/oauth-pages/org-picker.ts
  • services/kilo-mcp/src/oauth-pages/org-picker.test.ts

Fix these issues in Kilo Cloud

Previous review (commit 5df3ac4)

Status: No Issues Found | Recommendation: Merge

Files Reviewed (12 files)
  • services/kilo-mcp/src/auth/token.ts
  • services/kilo-mcp/src/store/oauth-store.ts
  • services/kilo-mcp/wrangler.jsonc
  • services/kilo-mcp/src/auth/token.test.ts
  • services/kilo-mcp/src/store/oauth-store.test.ts
  • services/kilo-mcp/src/auth/authorize.test.ts
  • services/kilo-mcp/src/auth/dcr.test.ts
  • services/kilo-mcp/src/index.test.ts
  • services/kilo-mcp/src/oauth-pages/authorize-page.test.ts
  • services/kilo-mcp/src/oauth-pages/org-picker.test.ts
  • services/kilo-mcp/catalog.json
  • pnpm-lock.yaml

Previous review (commit 513bd2f)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

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

WARNING

File Line Issue
services/kilo-mcp/src/auth/token.ts 306 Refresh-token reuse does not revoke the stolen grant

SUGGESTION

File Line Issue
services/kilo-mcp/wrangler.jsonc 58 env.dev omits secrets.required
Files Reviewed (32 files)
  • services/kilo-mcp/src/auth/token.ts - 1 issue
  • services/kilo-mcp/wrangler.jsonc - 1 issue
  • services/kilo-mcp/src/index.ts
  • services/kilo-mcp/src/auth.ts
  • services/kilo-mcp/src/auth/verify.ts
  • services/kilo-mcp/src/auth/authorize.ts
  • services/kilo-mcp/src/auth/dcr.ts
  • services/kilo-mcp/src/auth/jwt.ts
  • services/kilo-mcp/src/auth/pkce.ts
  • services/kilo-mcp/src/auth/metadata.ts
  • services/kilo-mcp/src/auth/http.ts
  • services/kilo-mcp/src/call.ts
  • services/kilo-mcp/src/search.ts
  • services/kilo-mcp/src/search-knn.ts
  • services/kilo-mcp/src/embedding.ts
  • services/kilo-mcp/src/types.ts
  • services/kilo-mcp/src/store/oauth-store.ts
  • services/kilo-mcp/src/db/sqlite-schema.ts
  • services/kilo-mcp/src/oauth-pages/authorize-page.ts
  • services/kilo-mcp/src/oauth-pages/org-picker.ts
  • services/kilo-mcp/drizzle/0000_happy_zaladane.sql
  • services/kilo-mcp/drizzle/0001_cynical_karen_page.sql
  • services/kilo-mcp/scripts/embed-catalog.ts
  • services/kilo-mcp/package.json
  • apps/web/src/scripts/mcp-catalog/catalog.ts
  • apps/web/src/scripts/mcp-catalog/dump.ts
  • apps/web/jest.config.ts
  • .github/workflows/kilo-mcp-catalog.yml
  • scripts/kilo-mcp-catalog.test.mjs
  • .oxfmtrc.json
  • services/kilo-mcp/src/auth/token.test.ts
  • services/kilo-mcp/catalog.json

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

@iscekic
iscekic force-pushed the kwf/kilo-remote-mcp-c204-l1 branch from cda4506 to 5df3ac4 Compare September 10, 2026 00:58
@iscekic
iscekic marked this pull request as ready for review September 10, 2026 01:10
@iscekic iscekic added the human-ready The PR is ready for human review. label Sep 10, 2026
@iscekic iscekic self-assigned this Sep 10, 2026
The dump now shells out to `kilo run` for missing summaries instead of
calling OpenRouter or Anthropic directly. One pinned model generates every
summary: kilo/deepseek/deepseek-v4.1-flash at variant max.

- Feed the prompt on stdin, so a batch is never bounded by ARGV_MAX.
- Run the CLI in the OS temp dir, so it does not load this repo's config.
- Reduce the CLI JSON event stream to the assistant text parts.
- Classify a missing CLI or a signed-out CLI as non-retryable, and keep
  the hand-write path in the error text.
- Install @kilocode/cli in both CI jobs and pass the credential as
  KILO_AUTH_CONTENT from MCP_CATALOG_KILO_AUTH.
Add the Cloudflare custom domain and disable the workers.dev URL in
production. The issuer is derived from the request URL, so the OAuth
metadata advertises https://kilo-mcp.kilosessions.ai without extra config.
The dev env keeps its own workers.dev URL and index.
Use the apps/web palette on the consent page, the org picker, and error
pages: near-black canvas, raised charcoal card, brand-primary CTA with
dark foreground, Inter, and a compact brand row. Drop the old blue CTA.

Open the sign-in link in a new tab so the pairing-status poll in the
consent tab survives the trip to apps/web. Name the page "Connect to
Kilo MCP" and make the restart link a secondary action.
Comment thread apps/web/src/scripts/mcp-catalog/catalog.ts Outdated
Pass KILO_API_KEY and KILO_ORG_ID from repo secrets alongside the optional
KILO_AUTH_CONTENT auth store, so the catalog jobs can run on an API key
instead of a personal CLI login.
Sync with main and regenerate pnpm-lock.yaml. Main downgraded the wrangler
catalog to 4.112.0 (#6005); the stale branch lockfile made --frozen-lockfile
fail on the PR merge ref with ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY.
Review found runKiloCompletion concatenated every part.type === 'text'
event, while the in-repo contract (auto-routing-benchmark/src/kilo-events.ts)
takes only completed text events (part.time.end set) and also accepts the
flattened evt.text shape.

- A streaming delta plus the final text produced garbled JSON.
- A flattened event produced no text, so the dump failed.

parseKiloCompletion now mirrors that contract. Add unit tests for the
delta, flattened, and malformed-line cases.
Use the shared kiloapps.io zone instead of kilosessions.ai. The issuer is
derived from the request URL, so the OAuth metadata advertises
https://mcp.kiloapps.io with no extra config.
The MCP catalog dump shells out to `kilo run`, which needs a Kilo API
token. Add POST /api/internal/mcp-catalog/token to exchange a dedicated
shared secret for a 1-hour token that belongs to the benchmarking service
account, instead of a maintainer's personal CLI credential.
Two changes to the catalog workflow:

- Run it on every PR and gate the work on a change-detection step, so
  `catalog (PR)` can be a required check without leaving unrelated PRs
  blocked on an expected-but-skipped workflow. Removes catalog drift.
- Mint a short-lived service-account token instead of reading a
  maintainer's personal CLI credential. Fork PRs still fail with the
  hand-written-summary recovery.
@iscekic
iscekic added this pull request to stack #6067 September 10, 2026 23:18
Comment thread services/kilo-mcp/src/index.ts
Comment thread .github/workflows/kilo-mcp-catalog.yml
Comment thread services/kilo-mcp/src/call.ts
- getKiloToken now filters by the token's org and resource indicator, not
  just user + client, so a token for one grant can never forward another
  grant's Kilo credential. Null-org identities match only null-org rows.
- VerifiedMcpToken carries its bound resource so the lookup can use it.
- callCatalogEndpoint rejects any input for a no-input catalog procedure
  before any upstream request.
- the merge catalog job fails when the dump changes catalog.json, so the
  Vectorize index cannot diverge from the deployed Worker bundle.
@iscekic
iscekic requested a review from pandemicsyn September 11, 2026 11:26
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