Skip to content

Add @kody/codemode-utils and rename generated UI utils alias - #88

Closed
kentcdodds wants to merge 8 commits into
mainfrom
cursor/kody-utils-module-changes-0c3f
Closed

kentcdodds wants to merge 8 commits into
mainfrom
cursor/kody-utils-module-changes-0c3f

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Mar 29, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • rename the generated UI runtime alias from @kody/utils to @kody/ui-utils
  • add execute-sandbox support for importing @kody/codemode-utils with refreshAccessToken() and createAuthenticatedFetch()
  • keep normal execute snippets on DynamicWorkerExecutor, while using a focused bridge only for @kody/codemode-utils
  • harden execute module detection and connector host enforcement
  • add focused tests covering the renamed alias, execute sandbox behavior, and the new codemode utils path

Testing

  • targeted node Vitest coverage for executor behavior and generated UI import map / OAuth guide copy
  • targeted MCP E2E coverage for execute + ui_save_app
  • repo TypeScript check

Closes #83.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added OAuth token refresh and authenticated API fetch helpers for connector-based authentication workflows.
    • Enhanced code execution with improved validation and error handling.
  • Documentation

    • Clarified MCP server code execution requirements to support modules with imports.
    • Updated generated UI helper import guidance.
  • Bug Fixes

    • Improved error message clarity for unresolved secret placeholders.
    • Enhanced result serialization for structured content output.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Mar 29, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The pull request renames the @kody/utils module to @kody/ui-utils across generated UI runtime references, introduces a new @kody/codemode-utils module with token refresh and authenticated fetch utilities, refactors createExecuteExecutor to return an object with an explicit execute method that routes code execution through a provider-bridge path or legacy path, and updates documentation strings and test assertions accordingly.

Changes

Cohort / File(s) Summary
Generated UI Runtime Module Rename
packages/worker/client/mcp-apps/generated-ui-runtime-contract.ts, packages/worker/client/mcp-apps/generated-ui-widget-runtime.ts, packages/worker/src/app/saved-ui-hosted-html.node.test.ts, packages/worker/src/mcp/generated-ui-app-base-url.node.test.ts, packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts
Module specifier changed from @kody/utils to @kody/ui-utils in runtime contract, import map assertions, and error messages.
New Codemode Utils Helper Module
packages/worker/src/mcp/tools/codemode-utils.ts
New module exporting codemodeUtilsModuleSpecifier constant and buildCodemodeUtilsModuleSource() function. Generates runtime code for token refresh and authenticated fetch utilities: refreshAccessToken(providerName) reads connector configs via provider bridge, performs token exchange with refresh token secrets, and createAuthenticatedFetch(providerName) wraps fetch to inject Bearer tokens automatically.
Executor Core Refactoring
packages/worker/src/mcp/executor.ts, packages/worker/src/mcp/executor.node.test.ts
createExecuteExecutor now returns object with execute(code, providersOrFns) method instead of directly returning DynamicWorkerExecutor. Adds conditional routing: rejects top-level import/export code; routes code referencing codemode utils through executeDynamicWithKodyUtilsBridge with provider validation, dynamic module wrapping, timeout handling via Promise.race, console capture, and JSON serialization-safe error formatting. Falls back to legacy DynamicWorkerExecutor path for code without codemode utils references. Includes new test loader and execution assertion.
Tool Documentation & Validation Updates
packages/worker/src/mcp/tools/execute.ts, packages/worker/src/mcp/tools/open-generated-ui.ts, packages/worker/src/mcp/generated-ui-api.ts, packages/worker/src/mcp/index.ts
Updated user-facing guidance and schema descriptions: execute tool now allows dynamic import() within functions instead of requiring async arrow functions; open-generated-ui references @kody/ui-utils in parameter docs; error messages reference correct module names. Added toJsonSafeForMcpStructuredContent() helper to ensure execute tool results serialize safely for MCP/workerd structured content.
OAuth Guide & Test Expectations
packages/worker/src/mcp/capabilities/coding/generated-ui-oauth-guide.ts, packages/worker/src/mcp/capabilities/coding/generated-ui-oauth-guide.node.test.ts
All embedded OAuth flow examples, client ID loading, and callback handling code updated to reference kodyWidget from @kody/ui-utils. Test assertions verify import inclusion and exclusion of prior @kody/utils reference. Capability metadata keywords updated for correct module discovery.
Capability Schema Descriptions
packages/worker/src/mcp/capabilities/apps/ui-save-app.ts, packages/worker/src/mcp/capabilities/apps/ui-update-app.ts
Zod schema descriptions updated to reference kodyWidget.params being imported from @kody/ui-utils instead of @kody/utils. No validation logic or handler behavior changed.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #54: Updates run-codemode-registry to resolve the codemode provider before calling executor.execute—directly related to the executor refactoring that now expects resolved providers as a parameter.
  • PR #76: Modifies the generated UI runtime contract and import-map usage (same generatedUiRuntimeModuleSpecifier and runtime helper imports being changed in this PR).
  • PR #65: Touches the hosted saved-UI HTML generation and import-map rendering that directly interacts with the renamed module specifier introduced here.

Poem

🐰 From utils old to new names bright,
We split the path: UI-utils, Codemode's might!
Bridge providers, tokens flow,
Execute returns what we bestow—
Fresh names, fresh code, futures align!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: adding @kody/codemode-utils and renaming the generated UI utils alias from @kody/utils to @kody/ui-utils.
Linked Issues check ✅ Passed All requirements from #83 are met: @kody/codemode-utils module added with refreshAccessToken() and createAuthenticatedFetch() implementations, @kody/utils renamed to @kody/ui-utils throughout codebase with updated import maps and documentation.
Out of Scope Changes check ✅ Passed All changes are directly related to the stated objectives: module renaming, new codemode-utils module, import map updates, test updates, documentation updates, and executor sandbox enhancements to support the new module.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/kody-utils-module-changes-0c3f

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
};
}\`

\`import { refreshAccessToken } from '@kody/codemode-utils'

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@cursoragent this won't work because the code should be in an arrow function. Instead show a dynamic import with await

@cursor cursor Bot Mar 29, 2026 •

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.

Summary

  • Updated the execute tool example to keep the code inside an async arrow function and use const { refreshAccessToken } = await import('@kody/codemode-utils'), matching Kent’s review feedback and the intended execute usage. execute.ts:72-87

Testing

  • ✅ npx tsc -p tsconfig.json --noEmit
Open in Web Open in Cursor 

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@kentcdodds
kentcdodds marked this pull request as ready for review March 29, 2026 03:11
@github-actions

github-actions Bot commented Mar 29, 2026 •

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-88.kentcdodds.workers.dev

Worker: kody-pr-88
D1: kody-pr-88-db
KV: kody-pr-88-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
packages/worker/src/mcp/executor.ts (1)

76-84: Module detection heuristic may have false positives.

The regex ^\s*import\s and ^\s*export\s will match comments containing these words (e.g., // import something). Consider using the AST-based detection from wrapImportedExecuteCode earlier, or ensure comments are handled.

💡 Consider using acorn parse for initial detection too
 function buildUserEntryModuleSource(code: string) {
 	const trimmed = code.trim()
-	const looksLikeModule =
-		/^\s*import\s/m.test(trimmed) || /^\s*export\s/m.test(trimmed)
-	if (looksLikeModule) {
-		return wrapImportedExecuteCode(trimmed)
+	try {
+		const parsed = acorn.parse(trimmed, {
+			ecmaVersion: 'latest',
+			sourceType: 'module',
+		})
+		const hasImportsOrExports = parsed.body.some(
+			(node) =>
+				node.type === 'ImportDeclaration' ||
+				node.type === 'ExportDefaultDeclaration' ||
+				node.type === 'ExportNamedDeclaration' ||
+				node.type === 'ExportAllDeclaration',
+		)
+		if (hasImportsOrExports) {
+			return wrapImportedExecuteCode(trimmed)
+		}
+	} catch {
+		// Fall back to arrow function wrapping if parse fails
 	}
 	return `export default ${normalizeCode(code)};`
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.ts` around lines 76 - 84, The
module-detection regex in buildUserEntryModuleSource can false-positive on
imports/exports inside comments; replace the simple regex check with the same
AST-based detection used by wrapImportedExecuteCode (or run acorn.parse on the
trimmed code and test the AST for
ImportDeclaration/ExportNamedDeclaration/ExportDefaultDeclaration/ExportAllDeclaration
nodes) so comments are ignored, then call wrapImportedExecuteCode when the AST
shows module syntax; otherwise return the existing export default using
normalizeCode.
packages/worker/client/mcp-apps/generated-ui-runtime-contract.ts (1)

46-53: Consider a temporary deprecated alias for backward compatibility.

Line 46 switches the runtime alias globally, and Lines 51-53 emit only that key. If existing saved apps still import @kody/utils, they will fail at runtime after rollout. Consider mapping both aliases during migration.

Suggested migration-safe diff
 export const generatedUiRuntimeModuleSpecifier = '@kody/ui-utils' as const
+export const deprecatedGeneratedUiRuntimeModuleSpecifier = '@kody/utils' as const

 export function buildGeneratedUiRuntimeImportMap(runtimeScriptHref: string) {
 	const importMapJson = escapeInlineScriptSource(
 		JSON.stringify({
 			imports: {
 				[generatedUiRuntimeModuleSpecifier]: runtimeScriptHref,
+				[deprecatedGeneratedUiRuntimeModuleSpecifier]: runtimeScriptHref,
 			},
 		}),
 	)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/client/mcp-apps/generated-ui-runtime-contract.ts` around
lines 46 - 53, buildGeneratedUiRuntimeImportMap currently only maps the new
alias stored in generatedUiRuntimeModuleSpecifier to runtimeScriptHref which
will break saved apps importing the old alias; update the function to also emit
a deprecated alias mapping (e.g., '@kody/utils') pointing to the same
runtimeScriptHref so both import keys are present during migration — you can add
a new constant like deprecatedUiRuntimeModuleSpecifier and include both
[generatedUiRuntimeModuleSpecifier] and [deprecatedUiRuntimeModuleSpecifier] in
the imports object returned by buildGeneratedUiRuntimeImportMap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/mcp/executor.ts`:
- Line 216: Update the compatibilityDate value to the current runtime date:
locate the compatibilityDate key (currently '2025-06-01') in the configuration
object in executor.ts and change its string value to the current date (e.g.,
'2026-03-29') so the Workers runtime uses up-to-date feature availability.
- Around line 214-231: The TypeScript error occurs because entrypoint (from
input.loader.get(...).getEntrypoint()) is typed as Fetcher<undefined> and lacks
the custom evaluate method; fix by asserting a proper type that exposes evaluate
(e.g., the dynamic module's executor interface or Fetcher<CodeExecutor>) so you
can call entrypoint.evaluate(dispatchers) and get an ExecuteResult; update the
getEntrypoint() call or the entrypoint variable declaration to include the
correct generic/type assertion referencing input.loader, getEntrypoint,
entrypoint, evaluate, and ExecuteResult (or the CodeExecutor export) so
TypeScript recognizes the evaluate method.

In `@packages/worker/src/mcp/tools/codemode-utils.ts`:
- Around line 60-63: The connector routing metadata parsed into
requiredHosts/allowedHosts is never enforced in createAuthenticatedFetch,
allowing bearer-authenticated calls to arbitrary hosts; update
createAuthenticatedFetch to validate outbound request URLs against the parsed
routing policy (e.g., enforce a baseUrl prefix and/or match the request host
against value.requiredHosts or allowedHosts) and reject or rewrite requests that
don't comply; locate the routing data created where requiredHosts is filtered
and pass it into (or reference it from) createAuthenticatedFetch (the fetch
wrapper) so every fetch call verifies host/base-URL constraints before sending
the Authorization header.

---

Nitpick comments:
In `@packages/worker/client/mcp-apps/generated-ui-runtime-contract.ts`:
- Around line 46-53: buildGeneratedUiRuntimeImportMap currently only maps the
new alias stored in generatedUiRuntimeModuleSpecifier to runtimeScriptHref which
will break saved apps importing the old alias; update the function to also emit
a deprecated alias mapping (e.g., '@kody/utils') pointing to the same
runtimeScriptHref so both import keys are present during migration — you can add
a new constant like deprecatedUiRuntimeModuleSpecifier and include both
[generatedUiRuntimeModuleSpecifier] and [deprecatedUiRuntimeModuleSpecifier] in
the imports object returned by buildGeneratedUiRuntimeImportMap.

In `@packages/worker/src/mcp/executor.ts`:
- Around line 76-84: The module-detection regex in buildUserEntryModuleSource
can false-positive on imports/exports inside comments; replace the simple regex
check with the same AST-based detection used by wrapImportedExecuteCode (or run
acorn.parse on the trimmed code and test the AST for
ImportDeclaration/ExportNamedDeclaration/ExportDefaultDeclaration/ExportAllDeclaration
nodes) so comments are ignored, then call wrapImportedExecuteCode when the AST
shows module syntax; otherwise return the existing export default using
normalizeCode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0f4f5037-7504-41b0-b196-26726044bd18

📥 Commits

Reviewing files that changed from the base of the PR and between 46e8e7f and bd52ce1.

📒 Files selected for processing (16)
  • packages/worker/client/mcp-apps/generated-ui-runtime-contract.ts
  • packages/worker/client/mcp-apps/generated-ui-widget-runtime.ts
  • packages/worker/src/app/saved-ui-hosted-html.node.test.ts
  • packages/worker/src/mcp/capabilities/apps/ui-save-app.ts
  • packages/worker/src/mcp/capabilities/apps/ui-update-app.ts
  • packages/worker/src/mcp/capabilities/coding/generated-ui-oauth-guide.node.test.ts
  • packages/worker/src/mcp/capabilities/coding/generated-ui-oauth-guide.ts
  • packages/worker/src/mcp/executor.ts
  • packages/worker/src/mcp/generated-ui-api.ts
  • packages/worker/src/mcp/generated-ui-app-base-url.node.test.ts
  • packages/worker/src/mcp/index.ts
  • packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts
  • packages/worker/src/mcp/run-codemode-registry.workers.test.ts
  • packages/worker/src/mcp/tools/codemode-utils.ts
  • packages/worker/src/mcp/tools/execute.ts
  • packages/worker/src/mcp/tools/open-generated-ui.ts

Comment thread packages/worker/src/mcp/executor.ts Outdated

const entrypoint = input.loader
.get(`codemode-${crypto.randomUUID()}`, () => ({
compatibilityDate: '2025-06-01',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if there are other compatibility dates used in the codebase for reference
rg -n "compatibilityDate" --type ts -C1

Repository: kentcdodds/kody

Length of output: 552


🏁 Script executed:

#!/bin/bash
# Get full context around line 216 in executor.ts
sed -n '200,230p' packages/worker/src/mcp/executor.ts

Repository: kentcdodds/kody

Length of output: 944


🏁 Script executed:

#!/bin/bash
# Search for any comments or documentation about compatibility dates
rg -i "compatibility" packages/worker/src/ -A 2 -B 2 --type ts | head -50

Repository: kentcdodds/kody

Length of output: 1329


🏁 Script executed:

#!/bin/bash
# Look for any config files that might set or reference compatibility dates
fd -e json -e toml -e yaml -e yml packages/worker | xargs grep -l "2025" 2>/dev/null || echo "No matches"

Repository: kentcdodds/kody

Length of output: 452


🌐 Web query:

Cloudflare Workers compatibility date format valid dates 2025-06-01

💡 Result:

The compatibility date for Cloudflare Workers uses the YYYY-MM-DD date format (e.g., "2022-04-05"). The date "2025-06-01" follows this format exactly and is a valid past date as of today (2026-03-29). Official documentation consistently shows examples in this format, and Wrangler validates dates, rejecting invalid ones (e.g., future dates like "2024-11-05" in 2024 caused issues). Compatibility dates must be in the past or present; the runtime supports all historical dates from 2021-11-02 onward indefinitely. Flags enable at specific dates up to 2026-04-01, confirming support for 2025 dates. Use the latest date like "2026-03-29" for new projects to opt into all changes.

Citations:


Update compatibility date to current version.

The compatibility date 2025-06-01 is outdated. Since we are in March 2026, this date is approximately 9 months old. For optimal compatibility and access to recent runtime features, update this to the current date (e.g., 2026-03-29). Cloudflare Workers supports all historical dates from 2021-11-02 onward, so there is no risk in updating to a newer date.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.ts` at line 216, Update the
compatibilityDate value to the current runtime date: locate the
compatibilityDate key (currently '2025-06-01') in the configuration object in
executor.ts and change its string value to the current date (e.g., '2026-03-29')
so the Workers runtime uses up-to-date feature availability.

Comment thread packages/worker/src/mcp/tools/codemode-utils.ts
Comment thread packages/worker/src/mcp/tools/codemode-utils.ts
Comment thread packages/worker/src/mcp/executor.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/worker/src/mcp/executor.ts (1)

229-246: ⚠️ Potential issue | 🔴 Critical

Type the loaded entrypoint before calling evaluate (TS2339 blocker).

Line [244] returns a fetcher-typed stub by default, so Line [246] fails typecheck when calling evaluate.

Proposed fix
+type CodeExecutorEntrypoint = {
+	evaluate(dispatchers: Record<string, ToolDispatcher>): Promise<ExecuteResult>
+}
+
 const entrypoint = input.loader
 	.get(`codemode-${crypto.randomUUID()}`, () => ({
 		compatibilityDate: '2025-06-01',
 		compatibilityFlags: ['nodejs_compat'],
 		mainModule: 'executor.js',
 		modules: {
 			[`${codemodeUtilsModuleSpecifier}`]: {
 				js: buildCodemodeUtilsModuleSource(),
 			},
 			'user-entry.js': buildUserEntryModuleSource(input.code),
 			'user-code.js': buildUserCodeRunnerModuleSource(),
 			'executor.js': executorModuleSource,
 		},
 		globalOutbound: input.globalOutbound,
 	}))
-	.getEntrypoint()
+	.getEntrypoint() as unknown as CodeExecutorEntrypoint
 
 const response = (await entrypoint.evaluate(dispatchers)) as ExecuteResult
#!/bin/bash
set -euo pipefail

echo "1) Show the call site that triggers TS2339:"
sed -n '229,248p' packages/worker/src/mcp/executor.ts

echo
echo "2) Inspect WorkerLoader/entrypoint typing in generated worker types:"
fd -i "worker-configuration.d.ts" packages/worker --exec rg -n -C2 'getEntrypoint'

echo
echo "3) Locate any existing TS2339 signal references:"
rg -n -C2 "TS2339|Property 'evaluate' does not exist"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.ts` around lines 229 - 246, The entrypoint
returned by input.loader.get(...).getEntrypoint() is currently typed as the
fetcher stub so calling entrypoint.evaluate causes TS2339; update the typing at
the call site by casting or narrowing the result to the proper Worker Entrypoint
type before calling evaluate (e.g. assert the type that exposes evaluate on the
object returned by getEntrypoint), or change the loader/getEntrypoint typing to
return the concrete entrypoint type; modify the expression assigning entrypoint
(the chain starting at input.loader.get(...).getEntrypoint()) so entrypoint is
typed to include evaluate and then call entrypoint.evaluate(dispatchers) as
ExecuteResult.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/mcp/executor.ts`:
- Around line 159-181: The provider-name validation must also block
prototype/property collision names and avoid prototype-polluting plain objects:
add "__proto__", "prototype", and "constructor" to the reservedNames set (in
addition to '__dispatchers','__logs','__providers') and keep the syntactic check
via validIdentifier and duplicate check via seenNames; also replace uses of
plain object literals used as maps for providers/logs with objects created via
Object.create(null) (i.e., create maps with null-prototype) where dynamic writes
occur so assigning provider names cannot mutate prototypes.

---

Duplicate comments:
In `@packages/worker/src/mcp/executor.ts`:
- Around line 229-246: The entrypoint returned by
input.loader.get(...).getEntrypoint() is currently typed as the fetcher stub so
calling entrypoint.evaluate causes TS2339; update the typing at the call site by
casting or narrowing the result to the proper Worker Entrypoint type before
calling evaluate (e.g. assert the type that exposes evaluate on the object
returned by getEntrypoint), or change the loader/getEntrypoint typing to return
the concrete entrypoint type; modify the expression assigning entrypoint (the
chain starting at input.loader.get(...).getEntrypoint()) so entrypoint is typed
to include evaluate and then call entrypoint.evaluate(dispatchers) as
ExecuteResult.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b36fac44-186d-4e30-b0c5-ef0c848e3951

📥 Commits

Reviewing files that changed from the base of the PR and between bd52ce1 and 92be1cb.

📒 Files selected for processing (2)
  • packages/worker/src/mcp/executor.ts
  • packages/worker/src/mcp/tools/codemode-utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/worker/src/mcp/tools/codemode-utils.ts

Comment thread packages/worker/src/mcp/executor.ts Outdated
Comment on lines +159 to +181
const reservedNames = new Set(['__dispatchers', '__logs', '__providers'])
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/
const seenNames = new Set<string>()
for (const provider of input.providers) {
if (reservedNames.has(provider.name)) {
return {
result: undefined,
error: `Provider name "${provider.name}" is reserved`,
}
}
if (!validIdentifier.test(provider.name)) {
return {
result: undefined,
error: `Provider name "${provider.name}" is not a valid JavaScript identifier`,
}
}
if (seenNames.has(provider.name)) {
return {
result: undefined,
error: `Duplicate provider name "${provider.name}"`,
}
}
seenNames.add(provider.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Current provider-name validation and dynamic map writes:"
sed -n '159,227p' packages/worker/src/mcp/executor.ts

echo
echo "2) Trace provider name usage/definition points:"
rg -n -C3 --type=ts '\bResolvedProvider\b|provider\.name|name:\s*string'

Repository: kentcdodds/kody

Length of output: 50372


🏁 Script executed:

# Verify the exact locations and check for other plain object usages
grep -n "Object.create\|const.*=.*{}" packages/worker/src/mcp/executor.ts | head -20

Repository: kentcdodds/kody

Length of output: 273


Harden provider-name handling against prototype-key collisions.

Provider names like __proto__, constructor, and prototype pass validation at lines 159-181 because the validIdentifier regex only checks for syntactic validity, not reserved property names. Subsequent dynamic writes at lines 194 and 215 using plain {} objects can mutate prototypes unexpectedly.

Proposed fix
-	const reservedNames = new Set(['__dispatchers', '__logs', '__providers'])
+	const reservedNames = new Set([
+		'__dispatchers',
+		'__logs',
+		'__providers',
+		'__proto__',
+		'prototype',
+		'constructor',
+	])
@@
-		'    const __providers = {};',
+		'    const __providers = Object.create(null);',
@@
-	const dispatchers = {} as Record<string, ToolDispatcher>
+	const dispatchers: Record<string, ToolDispatcher> = Object.create(null)

Also applies to: 194-199, 215-223

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.ts` around lines 159 - 181, The
provider-name validation must also block prototype/property collision names and
avoid prototype-polluting plain objects: add "__proto__", "prototype", and
"constructor" to the reservedNames set (in addition to
'__dispatchers','__logs','__providers') and keep the syntactic check via
validIdentifier and duplicate check via seenNames; also replace uses of plain
object literals used as maps for providers/logs with objects created via
Object.create(null) (i.e., create maps with null-prototype) where dynamic writes
occur so assigning provider names cannot mutate prototypes.

Comment thread packages/worker/src/mcp/capabilities/apps/ui-save-app.ts Outdated
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/mcp/executor.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Comment thread packages/worker/src/mcp/executor.ts Outdated
cursoragent and others added 2 commits March 29, 2026 04:11
- Root cause: always attaching the virtual @kody/codemode-utils module to the
  WorkerLoader graph broke RPC (DataCloneError / MCP serialization), so
  structuredContent was missing and app_id looked undefined in E2E.
- Use @cloudflare/codemode DynamicWorkerExecutor for normal snippets; only merge
  the codemode-utils module when the code references it.
- When utils are referenced, run a small fork of the codemode executor template
  that sets globalThis.__kodyProviders so requireCodemode() works.
- Reject top-level import/export with a clear error (multi-module loader + RPC
  is not supported); document dynamic import() in the execute tool.
- JSON-round-trip structuredContent.result in the execute tool for MCP safety.
- Fix node mock for export default class; remove flaky workers integration test.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/worker/src/mcp/executor.ts (1)

83-111: ⚠️ Potential issue | 🟡 Minor

Add prototype-polluting property names to reserved set.

Provider names like __proto__, constructor, and prototype pass validation because the validIdentifier regex only checks syntactic validity. These should be blocked to prevent prototype pollution when building dynamic objects.

🛡️ Proposed fix
 function validateExecuteProviders(
 	providers: Array<ResolvedProvider>,
 ): ExecuteResult | null {
-	const reservedNames = new Set(['__dispatchers', '__logs'])
+	const reservedNames = new Set([
+		'__dispatchers',
+		'__logs',
+		'__proto__',
+		'prototype',
+		'constructor',
+	])
 	const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.ts` around lines 83 - 111, The
validateExecuteProviders function currently only reserves '__dispatchers' and
'__logs' but allows prototype-polluting names; update the reservedNames set in
validateExecuteProviders to include '__proto__', 'constructor', 'prototype' (and
any other Object prototype keys you deem necessary) so names that would mutate
object prototypes are rejected, keeping the existing error-return behavior
(results with error messages) when provider.name matches one of these reserved
keys.
🧹 Nitpick comments (2)
packages/worker/src/mcp/executor.node.test.ts (1)

152-163: Fix ESLint consistent-type-imports violation.

The typeof import('cloudflare:workers').exports syntax is flagged by ESLint. Use a type-only import or type alias instead.

♻️ Proposed fix
+import type { exports as WorkerExportsType } from 'cloudflare:workers'
+
 // ... in test ...
 		exports: {
 			CodemodeFetchGateway() {
 				return {} as Fetcher
 			},
-		} as typeof import('cloudflare:workers').exports,
+		} as typeof WorkerExportsType,

Alternatively, if module augmentation is complex, you can use a simple inline type:

 		exports: {
 			CodemodeFetchGateway() {
 				return {} as Fetcher
 			},
-		} as typeof import('cloudflare:workers').exports,
+		} as { CodemodeFetchGateway: () => Fetcher },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.node.test.ts` around lines 152 - 163,
Replace the inline value assertion using typeof import(...) for the exports
property with a type-only import or alias to satisfy ESLint; add a top-level
type import (e.g., import type { exports as CloudflareExports } from
'cloudflare:workers') and then cast the mock exports object in this test (the
exports object returned for CodemodeFetchGateway / Fetcher) to that
CloudflareExports type instead of using typeof
import('cloudflare:workers').exports; update the test file's exports: {
CodemodeFetchGateway() { return {} as Fetcher } } assertion to use the new
imported type.
packages/worker/src/mcp/executor.ts (1)

113-141: Generated code creates null-prototype objects where applicable.

The proxy-based provider bindings are correctly scoped. However, line 116 generates const __kodyProviders = {}; which creates a regular object. While the risk is lower in the sandboxed worker context since __kodyProviders is not used as a lookup map with untrusted keys, using Object.create(null) would be more defensive.

♻️ Proposed fix for defense-in-depth
 function buildKodyUtilsProviderSetupLines(
 	providers: Array<ResolvedProvider>,
 ): Array<string> {
-	const lines: Array<string> = ['    const __kodyProviders = {};']
+	const lines: Array<string> = ['    const __kodyProviders = Object.create(null);']
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/worker/src/mcp/executor.ts` around lines 113 - 141, The created
__kodyProviders object uses a normal object literal which has a prototype;
change the initialization in buildKodyUtilsProviderSetupLines so __kodyProviders
is created with no prototype (use Object.create(null)) to avoid inherited keys,
keep the rest of the function identical (the Proxy constructions for each
provider p.name, the assignments to __kodyProviders[p.name], and the final
globalThis.__kodyProviders export should remain unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/worker/src/mcp/executor.ts`:
- Around line 72-78: The type mismatch comes from passing mergedModules
(Record<string, WorkerLoaderModule | string> | undefined) into the
DynamicWorkerExecutor which expects Record<string, string> | undefined; in the
branch where codeReferencesCodemodeUtils(code) is false modules is never
populated so pass undefined directly to DynamicWorkerExecutor (or remove the
mergedModules intermediate and pass undefined into the constructor) so that the
constructor's modules parameter matches the expected type; update the code that
constructs new DynamicWorkerExecutor({ loader: input.env.LOADER, timeout:
90_000, globalOutbound, modules: ... }) to use modules: undefined when
codeReferencesCodemodeUtils(code) is false and keep execute(code,
providersOrFns) unchanged.

---

Duplicate comments:
In `@packages/worker/src/mcp/executor.ts`:
- Around line 83-111: The validateExecuteProviders function currently only
reserves '__dispatchers' and '__logs' but allows prototype-polluting names;
update the reservedNames set in validateExecuteProviders to include '__proto__',
'constructor', 'prototype' (and any other Object prototype keys you deem
necessary) so names that would mutate object prototypes are rejected, keeping
the existing error-return behavior (results with error messages) when
provider.name matches one of these reserved keys.

---

Nitpick comments:
In `@packages/worker/src/mcp/executor.node.test.ts`:
- Around line 152-163: Replace the inline value assertion using typeof
import(...) for the exports property with a type-only import or alias to satisfy
ESLint; add a top-level type import (e.g., import type { exports as
CloudflareExports } from 'cloudflare:workers') and then cast the mock exports
object in this test (the exports object returned for CodemodeFetchGateway /
Fetcher) to that CloudflareExports type instead of using typeof
import('cloudflare:workers').exports; update the test file's exports: {
CodemodeFetchGateway() { return {} as Fetcher } } assertion to use the new
imported type.

In `@packages/worker/src/mcp/executor.ts`:
- Around line 113-141: The created __kodyProviders object uses a normal object
literal which has a prototype; change the initialization in
buildKodyUtilsProviderSetupLines so __kodyProviders is created with no prototype
(use Object.create(null)) to avoid inherited keys, keep the rest of the function
identical (the Proxy constructions for each provider p.name, the assignments to
__kodyProviders[p.name], and the final globalThis.__kodyProviders export should
remain unchanged).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 504f16b0-d9a9-47b2-b254-4912b7507174

📥 Commits

Reviewing files that changed from the base of the PR and between c8d945f and 9535fe7.

📒 Files selected for processing (3)
  • packages/worker/src/mcp/executor.node.test.ts
  • packages/worker/src/mcp/executor.ts
  • packages/worker/src/mcp/tools/execute.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/worker/src/mcp/tools/execute.ts

Comment thread packages/worker/src/mcp/executor.ts

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Fix All in Cursor

.string()
.describe('JavaScript async arrow function to execute capabilities.'),
.describe(
'JavaScript execute code. Use an async arrow function, or top-level imports with normal statements/returns.',

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.

Schema description contradicts actual top-level import rejection

High Severity

The inputSchema.code.describe text says "Use an async arrow function, or top-level imports with normal statements/returns," but codeNeedsModuleRunner in executor.ts explicitly rejects top-level imports with an error message. The longer tool description (lines 65–67) correctly says "Do not use top-level import or export." This contradiction in the schema description will cause LLMs to generate code with top-level imports that the executor immediately rejects.

Additional Locations (1)
Fix in Cursor Fix in Web


export async function createAuthenticatedFetch(providerName) {
const connector = await readConnectorConfig(providerName)
const accessToken = await refreshAccessToken(providerName)

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.

Double connector config fetch in createAuthenticatedFetch

Low Severity

createAuthenticatedFetch calls readConnectorConfig(providerName) on line 256, then calls refreshAccessToken(providerName) on line 257, which internally calls readConnectorConfig(providerName) again on line 207. This results in two redundant async API calls (connector_get or value_get) for the same provider name when only one is needed.

Fix in Cursor Fix in Web

if (parsed) {
return parsed
}
}

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.

Proxy defeats typeof feature detection in codemode-utils

Medium Severity

The typeof codemode.connector_get === 'function' guard in readConnectorConfig is meant to gracefully fall back to value_get when connector_get isn't available. However, codemode is a Proxy (from buildKodyUtilsProviderSetupLines) whose get trap returns an async function for every property access. This makes the typeof check always true, so the call always goes through. If the dispatcher lacks connector_get, it throws a hard error instead of falling through to the value_get fallback path.

Additional Locations (1)
Fix in Cursor Fix in Web

globalOutbound,
modules: mergedDynamicExecutorModules,
})
return executor.execute(code, providersOrFns)

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.

Dead code: dynamicExecutorModules branch is never reached

Low Severity

dynamicExecutorModules and mergedDynamicExecutorModules are populated only when codeReferencesCodemodeUtils(code) is true (line 57), but that same condition (line 72) routes to the bridge path instead. The standard DynamicWorkerExecutor path (line 82–88) only runs when the condition is false, meaning mergedDynamicExecutorModules is always undefined when it's used.

Fix in Cursor Fix in Web

@kentcdodds kentcdodds closed this Mar 29, 2026
@kody-bot
kody-bot deleted the cursor/kody-utils-module-changes-0c3f branch March 29, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add @kody/codemode-utils module and rename @kody/utils to @kody/ui-utils

2 participants