From a0f79a86496397280dfe808501036a24da316443 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:01:22 +0000 Subject: [PATCH 1/6] Add MCP conversation and memory context plumbing Co-authored-by: me --- packages/worker/src/mcp/index.ts | 8 +++ .../worker/src/mcp/mcp-server.mcp-e2e.test.ts | 68 ++++++++++++++++++ .../worker/src/mcp/tools/execute.node.test.ts | 6 ++ packages/worker/src/mcp/tools/execute.ts | 26 ++++++- .../worker/src/mcp/tools/open-generated-ui.ts | 11 +++ packages/worker/src/mcp/tools/search.ts | 18 +++++ .../worker/src/mcp/tools/tool-call-context.ts | 69 +++++++++++++++++++ 7 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 packages/worker/src/mcp/tools/tool-call-context.ts diff --git a/packages/worker/src/mcp/index.ts b/packages/worker/src/mcp/index.ts index f0b01b1e8f..658119b280 100644 --- a/packages/worker/src/mcp/index.ts +++ b/packages/worker/src/mcp/index.ts @@ -32,6 +32,8 @@ Quick start - Call 'search' first to discover what Kody can do (results include type 'capability', 'skill', 'app', or 'secret'). - Call 'execute' or 'meta_run_skill' next to run capability code. - Call 'open_generated_ui' when you want an interactive UI rendered in an MCP App host. +- The public MCP tools accept optional `conversationId` and `memory_context` fields. Clients should generate and reuse a short `conversationId` across related calls when possible; if omitted, Kody generates one and returns it in `structuredContent.conversationId`. +- Keep `memory_context` short, structured, and task-focused. It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. - Never ask the user to paste secrets, tokens, API keys, passwords, OAuth codes, or client secrets into chat. Use saved secrets when available, or use 'open_generated_ui' to collect and save sensitive values instead. - Use 'meta_save_skill' only for workflows that are reasonably repeatable—patterns you expect to run again with similar structure or inputs. Do not save one-off tasks, unique ad-hoc work, or highly bespoke requests as skills; run those with 'execute' instead. Use the optional 'collection' field to group related saved skills, and use 'meta_update_skill' to replace an existing skill's code in place. - When a saved skill declares parameters, pass values via meta_run_skill params; the codemode can read them from the params variable. @@ -47,6 +49,8 @@ ${domainInstructions} How to use search - Call the 'search' tool with a natural-language 'query' describing what you need (optional 'limit', 'detail'). +- Optionally pass `conversationId` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in `structuredContent.conversationId`. +- Optionally pass `memory_context` when you want to attach short, structured task context for future memory-aware behavior. - Narrow results by rephrasing 'query', or use the optional 'skill_collection' filter when you only want saved skills from one collection slug. - Saved skills appear when the MCP client provides an authenticated user; use 'meta_get_skill' for full skill code. - Use domain descriptions above as vocabulary hints in your query text. @@ -76,6 +80,8 @@ Destructive Cloudflare access How to use execute - The sandbox provides a 'codemode' object with async methods for each capability. +- Optionally pass `conversationId` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in `structuredContent.conversationId`. +- Optionally pass `memory_context` when you want to attach short, structured task context for future memory-aware behavior. - Use capability names discovered from search. - Pass one args object that matches the capability inputSchema. - Each capability call returns that capability's raw structured result value. @@ -96,6 +102,8 @@ How to use execute MCP App tools - Use 'open_generated_ui' when you want an interactive UI in MCP App compatible hosts. +- Optionally pass `conversationId` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in `structuredContent.conversationId`. +- Optionally pass `memory_context` when you want to attach short, structured task context for future memory-aware behavior. - Pass either inline source code with 'code' or reopen a saved app with 'app_id' (exactly one is allowed). - Prefer body-focused HTML fragments when possible, but full HTML documents are also supported. - Use generated UI whenever the user needs to enter a sensitive value. Do not ask the user to paste secrets or credentials into chat. diff --git a/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts b/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts index ffee669918..27e8e3efd3 100644 --- a/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts +++ b/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts @@ -40,6 +40,37 @@ test('mcp server returns built-in instructions and base server metadata', async ).toContain('"matches"') }) +test('mcp server search echoes provided conversationId and accepts memory_context', async () => { + await using database = await createTestDatabase() + await using server = await startDevServer(database.persistDir) + await using mcpClient = await createMcpClient(server.origin, database.user) + + const result = await mcpClient.client.callTool({ + name: 'search', + arguments: { + query: 'generated ui', + conversationId: 'searchctx1234', + memory_context: { + task: 'Find UI-related tools', + entities: ['generated ui'], + constraints: ['brief'], + }, + }, + }) + + const structuredResult = (result as CallToolResult).structuredContent as + | { + conversationId?: string + result?: { + matches?: Array + } + } + | undefined + + expect(structuredResult?.conversationId).toBe('searchctx1234') + expect(Array.isArray(structuredResult?.result?.matches)).toBe(true) +}) + test('mcp server saves and browses skill collections', async () => { await using database = await createTestDatabase() await using server = await startDevServer(database.persistDir) @@ -199,6 +230,36 @@ test('mcp server executes user code against codemode', async () => { expect(textOutput).toContain('hosted_url') }) +test('mcp server execute generates conversationId when omitted and accepts memory_context', async () => { + await using database = await createTestDatabase() + await using server = await startDevServer(database.persistDir) + await using mcpClient = await createMcpClient(server.origin, database.user) + + const result = await mcpClient.client.callTool({ + name: 'execute', + arguments: { + code: `async () => ({ ok: true })`, + memory_context: { + task: 'Return a small payload', + constraints: ['no side effects'], + }, + }, + }) + + const structuredResult = (result as CallToolResult).structuredContent as + | { + conversationId?: string + result?: { + ok?: boolean + } + } + | undefined + + expect(typeof structuredResult?.conversationId).toBe('string') + expect((structuredResult?.conversationId ?? '').length).toBeGreaterThan(0) + expect(structuredResult?.result?.ok).toBe(true) +}) + test('mcp server executes directly available codemode helpers', async () => { await using database = await createTestDatabase() await using server = await startDevServer(database.persistDir) @@ -362,11 +423,17 @@ test('mcp server opens generated ui with inline code and serves runtime resource name: 'open_generated_ui', arguments: { code: '

Hello Shell

Inline app content.

', + conversationId: 'uictx1234567', + memory_context: { + task: 'Render inline UI', + entities: ['generated ui'], + }, }, }) const structuredResult = (result as CallToolResult).structuredContent as | { + conversationId?: string appId?: string | null hostedUrl?: string | null renderSource?: string @@ -381,6 +448,7 @@ test('mcp server opens generated ui with inline code and serves runtime resource )?.text ?? '' expect(textOutput).toContain('Generated UI ready') + expect(structuredResult?.conversationId).toBe('uictx1234567') expect(structuredResult?.renderSource).toBe('inline_code') expect(appId).toBeNull() expect(hostedUrl).toBeNull() diff --git a/packages/worker/src/mcp/tools/execute.node.test.ts b/packages/worker/src/mcp/tools/execute.node.test.ts index de095e9d05..936e87ca51 100644 --- a/packages/worker/src/mcp/tools/execute.node.test.ts +++ b/packages/worker/src/mcp/tools/execute.node.test.ts @@ -22,4 +22,10 @@ test('execute tool description encourages fewer execute calls', async () => { expect(registerTool.mock.calls[0]?.[1]?.description).toContain( 'chain the capability calls there and return the final useful result', ) + expect(registerTool.mock.calls[0]?.[1]?.description).toContain( + 'Pass `conversationId` to group related calls across the same conversation.', + ) + expect(registerTool.mock.calls[0]?.[1]?.description).toContain( + 'Pass `memory_context` with short, structured task context', + ) }) diff --git a/packages/worker/src/mcp/tools/execute.ts b/packages/worker/src/mcp/tools/execute.ts index 45cbbaf4b2..f4778922c4 100644 --- a/packages/worker/src/mcp/tools/execute.ts +++ b/packages/worker/src/mcp/tools/execute.ts @@ -12,6 +12,11 @@ import { errorFields, logMcpEvent, } from '#mcp/observability.ts' +import { + conversationIdInputField, + memoryContextInputField, + resolveConversationId, +} from './tool-call-context.ts' const executeTool = { name: 'execute', @@ -24,7 +29,12 @@ To run a saved skill by id, prefer \`meta_run_skill\` with \`skill_id\` and optional \`params\`. If you need the saved code, call \`meta_get_skill\` and pass the returned code into this tool. -This tool accepts a single argument: \`{ "code": "async () => { ... }" }\`. +This tool requires \`code\` and also accepts optional tool-call context fields +such as \`conversationId\` and \`memory_context\`. + +Optional tool-call context: +- Pass \`conversationId\` to group related calls across the same conversation. Clients should generate and reuse a short value when possible. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Pass \`memory_context\` with short, structured task context for future memory-aware behavior. Keep it factual and concise rather than hidden reasoning. Available in your code: @@ -118,13 +128,23 @@ export async function registerExecuteTool(agent: McpRegistrationAgent) { code: z .string() .describe('JavaScript async arrow function to execute capabilities.'), + conversationId: conversationIdInputField, + memory_context: memoryContextInputField, }, annotations: executeTool.annotations, }, - async ({ code }: { code: string }) => { + async ({ + code, + conversationId, + }: { + code: string + conversationId?: string + memory_context?: z.infer + }) => { const startedAt = performance.now() const env = agent.getEnv() const callerContext = agent.getCallerContext() + const resolvedConversationId = resolveConversationId(conversationId) const { baseUrl, hasUser } = callerContextFields(callerContext) const { getCapabilityRegistryForContext } = await import('#mcp/capabilities/registry.ts') @@ -179,6 +199,7 @@ export async function registerExecuteTool(agent: McpRegistrationAgent) { }, ], structuredContent: { + conversationId: resolvedConversationId, error: errorMessage, errorDetails, logs: result.logs ?? [], @@ -206,6 +227,7 @@ export async function registerExecuteTool(agent: McpRegistrationAgent) { }, ], structuredContent: { + conversationId: resolvedConversationId, result: result.result, logs: result.logs ?? [], }, diff --git a/packages/worker/src/mcp/tools/open-generated-ui.ts b/packages/worker/src/mcp/tools/open-generated-ui.ts index f971cce39e..445839e3cd 100644 --- a/packages/worker/src/mcp/tools/open-generated-ui.ts +++ b/packages/worker/src/mcp/tools/open-generated-ui.ts @@ -4,6 +4,11 @@ import { z } from 'zod' import { generatedUiRuntimeResourceUri } from '#mcp/apps/generated-ui-runtime-html-entry.ts' import { createGeneratedUiAppSession } from '#mcp/generated-ui-app-session.ts' import { type McpRegistrationAgent } from '#mcp/mcp-registration-agent.ts' +import { + conversationIdInputField, + memoryContextInputField, + resolveConversationId, +} from '#mcp/tools/tool-call-context.ts' import { applyUiArtifactParameters, parseUiArtifactParameters, @@ -23,6 +28,8 @@ Behavior: - Use \`app_id\` to reopen previously saved UI source without sending that source code back through the model. - Saved apps can declare reusable parameters; pass runtime values via \`params\` and read them from \`kodyWidget.params\` after importing \`kodyWidget\` from \`@kody/ui-utils\`. - \`code\` may be a full HTML document or a fragment. +- Optional \`conversationId\` groups related MCP calls. Reuse the same value across follow-up tool calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Optional \`memory_context\` carries short, structured task context for future memory-aware behavior. Generated UI basics: - The runtime exposes module helpers from the \`@kody/ui-utils\` import-map alias; prefer \`import { kodyWidget } from '@kody/ui-utils'\`. @@ -69,6 +76,8 @@ const inputSchema = z .min(1) .optional() .describe('Optional short description for the current render session.'), + conversationId: conversationIdInputField, + memory_context: memoryContextInputField, params: z .record(z.string(), z.unknown()) .optional() @@ -102,6 +111,7 @@ export async function registerOpenGeneratedUiTool(agent: McpRegistrationAgent) { }, async (args) => { const callerContext = agent.getCallerContext() + const conversationId = resolveConversationId(args.conversationId) const appId = args.app_id ?? null const title = args.title ?? null const description = args.description ?? null @@ -142,6 +152,7 @@ export async function registerOpenGeneratedUiTool(agent: McpRegistrationAgent) { }) : null const structuredContent = { + conversationId, widget: 'generated_ui' as const, resourceUri: generatedUiRuntimeResourceUri, renderSource: appId ? ('saved_app' as const) : ('inline_code' as const), diff --git a/packages/worker/src/mcp/tools/search.ts b/packages/worker/src/mcp/tools/search.ts index ae1aa035e0..65dadcacbb 100644 --- a/packages/worker/src/mcp/tools/search.ts +++ b/packages/worker/src/mcp/tools/search.ts @@ -23,6 +23,11 @@ import { errorFields, logMcpEvent, } from '#mcp/observability.ts' +import { + conversationIdInputField, + memoryContextInputField, + resolveConversationId, +} from './tool-call-context.ts' const charsPerToken = 4 const maxTokens = 6_000 @@ -60,6 +65,12 @@ Pass a **query** string describing what you want to do. Results are ranked with Optional **limit** (default 15) caps how many results are returned. **detail: true** includes extra metadata (for skills: inferred capabilities, collection slug, etc.; for capabilities: JSON schemas where applicable). Optional **skill_collection** narrows saved skill results to one normalized collection/domain slug while still searching builtins, apps, and secrets normally. + Optional **conversationId** groups related calls across the same client +conversation. Clients should generate and reuse one when possible; Kody returns +one in \`structuredContent.conversationId\` when omitted. Optional +\`memory_context\` accepts short, structured task context for future +memory-aware behavior. + Example arguments: - \`{ "query": "saved interactive dashboard app", "limit": 10 }\` - \`{ "query": "github automation", "skill_collection": "release-engineering" }\` @@ -176,6 +187,8 @@ export async function registerSearchTool(agent: McpRegistrationAgent) { .boolean() .optional() .describe('Include full metadata / schemas when true.'), + conversationId: conversationIdInputField, + memory_context: memoryContextInputField, }, annotations: searchTool.annotations, }, @@ -184,8 +197,11 @@ export async function registerSearchTool(agent: McpRegistrationAgent) { skill_collection?: string limit?: number detail?: boolean + conversationId?: string + memory_context?: z.infer }) => { const startedAt = performance.now() + const conversationId = resolveConversationId(args.conversationId) const callerContext = agent.getCallerContext() const { baseUrl, hasUser } = callerContextFields(callerContext) const userId = callerContext.user?.userId ?? null @@ -272,6 +288,7 @@ export async function registerSearchTool(agent: McpRegistrationAgent) { return { content: [{ type: 'text', text: `Error: ${error.message}` }], structuredContent: { + conversationId, error: error.message, }, isError: true, @@ -309,6 +326,7 @@ export async function registerSearchTool(agent: McpRegistrationAgent) { }, ], structuredContent: { + conversationId, result: payload, }, } diff --git a/packages/worker/src/mcp/tools/tool-call-context.ts b/packages/worker/src/mcp/tools/tool-call-context.ts new file mode 100644 index 0000000000..907dfc4596 --- /dev/null +++ b/packages/worker/src/mcp/tools/tool-call-context.ts @@ -0,0 +1,69 @@ +import { z } from 'zod' + +const generatedConversationIdLength = 12 +const conversationIdAlphabet = '0123456789abcdefghjkmnpqrstvwxyz' + +const conversationIdDescription = + 'Optional short conversation identifier. Clients should generate and reuse the same value across related tool calls when possible. If omitted, Kody generates one and returns it in `structuredContent.conversationId`.' + +const memoryContextDescription = + 'Optional short, structured task context for future memory-aware behavior. Keep it brief and factual rather than hidden reasoning.' + +const memoryContextFieldDescription = + 'Optional task or goal summary for future memory retrieval.' + +const memoryContextListDescription = + 'Optional short phrases that identify important entities, constraints, or references for future memory retrieval.' + +export const conversationIdInputField = z + .string() + .min(1) + .max(64) + .optional() + .describe(conversationIdDescription) + +export const memoryContextInputField = z + .object({ + task: z + .string() + .min(1) + .max(300) + .optional() + .describe(memoryContextFieldDescription), + query: z + .string() + .min(1) + .max(300) + .optional() + .describe(memoryContextFieldDescription), + entities: z + .array(z.string().min(1).max(120)) + .max(8) + .optional() + .describe(memoryContextListDescription), + constraints: z + .array(z.string().min(1).max(120)) + .max(8) + .optional() + .describe(memoryContextListDescription), + }) + .optional() + .describe(memoryContextDescription) + +export function resolveConversationId( + conversationId: string | null | undefined, +) { + const normalizedConversationId = conversationId?.trim() ?? '' + if (normalizedConversationId) return normalizedConversationId + return generateConversationId() +} + +function generateConversationId() { + const bytes = crypto.getRandomValues( + new Uint8Array(generatedConversationIdLength), + ) + return Array.from( + bytes, + (byte) => conversationIdAlphabet[byte % conversationIdAlphabet.length], + ).join('') +} From 2a0aa6f7a4878a6a1ff34fea414eb51fcaabf9f3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:06:20 +0000 Subject: [PATCH 2/6] Fix MCP tool context docs and tests Co-authored-by: me --- packages/worker/src/mcp/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/worker/src/mcp/index.ts b/packages/worker/src/mcp/index.ts index 658119b280..d45b253127 100644 --- a/packages/worker/src/mcp/index.ts +++ b/packages/worker/src/mcp/index.ts @@ -32,8 +32,8 @@ Quick start - Call 'search' first to discover what Kody can do (results include type 'capability', 'skill', 'app', or 'secret'). - Call 'execute' or 'meta_run_skill' next to run capability code. - Call 'open_generated_ui' when you want an interactive UI rendered in an MCP App host. -- The public MCP tools accept optional `conversationId` and `memory_context` fields. Clients should generate and reuse a short `conversationId` across related calls when possible; if omitted, Kody generates one and returns it in `structuredContent.conversationId`. -- Keep `memory_context` short, structured, and task-focused. It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. +- The public MCP tools accept optional \`conversationId\` and \`memory_context\` fields. Clients should generate and reuse a short \`conversationId\` across related calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Keep \`memory_context\` short, structured, and task-focused. It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. - Never ask the user to paste secrets, tokens, API keys, passwords, OAuth codes, or client secrets into chat. Use saved secrets when available, or use 'open_generated_ui' to collect and save sensitive values instead. - Use 'meta_save_skill' only for workflows that are reasonably repeatable—patterns you expect to run again with similar structure or inputs. Do not save one-off tasks, unique ad-hoc work, or highly bespoke requests as skills; run those with 'execute' instead. Use the optional 'collection' field to group related saved skills, and use 'meta_update_skill' to replace an existing skill's code in place. - When a saved skill declares parameters, pass values via meta_run_skill params; the codemode can read them from the params variable. @@ -49,8 +49,8 @@ ${domainInstructions} How to use search - Call the 'search' tool with a natural-language 'query' describing what you need (optional 'limit', 'detail'). -- Optionally pass `conversationId` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in `structuredContent.conversationId`. -- Optionally pass `memory_context` when you want to attach short, structured task context for future memory-aware behavior. +- Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Optionally pass \`memory_context\` when you want to attach short, structured task context for future memory-aware behavior. - Narrow results by rephrasing 'query', or use the optional 'skill_collection' filter when you only want saved skills from one collection slug. - Saved skills appear when the MCP client provides an authenticated user; use 'meta_get_skill' for full skill code. - Use domain descriptions above as vocabulary hints in your query text. @@ -80,8 +80,8 @@ Destructive Cloudflare access How to use execute - The sandbox provides a 'codemode' object with async methods for each capability. -- Optionally pass `conversationId` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in `structuredContent.conversationId`. -- Optionally pass `memory_context` when you want to attach short, structured task context for future memory-aware behavior. +- Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Optionally pass \`memory_context\` when you want to attach short, structured task context for future memory-aware behavior. - Use capability names discovered from search. - Pass one args object that matches the capability inputSchema. - Each capability call returns that capability's raw structured result value. @@ -102,8 +102,8 @@ How to use execute MCP App tools - Use 'open_generated_ui' when you want an interactive UI in MCP App compatible hosts. -- Optionally pass `conversationId` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in `structuredContent.conversationId`. -- Optionally pass `memory_context` when you want to attach short, structured task context for future memory-aware behavior. +- Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Optionally pass \`memory_context\` when you want to attach short, structured task context for future memory-aware behavior. - Pass either inline source code with 'code' or reopen a saved app with 'app_id' (exactly one is allowed). - Prefer body-focused HTML fragments when possible, but full HTML documents are also supported. - Use generated UI whenever the user needs to enter a sensitive value. Do not ask the user to paste secrets or credentials into chat. From e4d680ec9adda5e9ef7563f37e1f9f9ff54b6aa2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:35:09 +0000 Subject: [PATCH 3/6] Use memoryContext casing in MCP tools Co-authored-by: me --- packages/worker/src/mcp/index.ts | 10 +++++----- packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts | 10 +++++----- packages/worker/src/mcp/tools/execute.node.test.ts | 2 +- packages/worker/src/mcp/tools/execute.ts | 8 ++++---- packages/worker/src/mcp/tools/open-generated-ui.ts | 4 ++-- packages/worker/src/mcp/tools/search.ts | 10 +++++----- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/worker/src/mcp/index.ts b/packages/worker/src/mcp/index.ts index d45b253127..04eac46a5f 100644 --- a/packages/worker/src/mcp/index.ts +++ b/packages/worker/src/mcp/index.ts @@ -32,8 +32,8 @@ Quick start - Call 'search' first to discover what Kody can do (results include type 'capability', 'skill', 'app', or 'secret'). - Call 'execute' or 'meta_run_skill' next to run capability code. - Call 'open_generated_ui' when you want an interactive UI rendered in an MCP App host. -- The public MCP tools accept optional \`conversationId\` and \`memory_context\` fields. Clients should generate and reuse a short \`conversationId\` across related calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Keep \`memory_context\` short, structured, and task-focused. It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. +- The public MCP tools accept optional \`conversationId\` and \`memoryContext\` fields. Clients should generate and reuse a short \`conversationId\` across related calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. +- Keep \`memoryContext\` short, structured, and task-focused. It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. - Never ask the user to paste secrets, tokens, API keys, passwords, OAuth codes, or client secrets into chat. Use saved secrets when available, or use 'open_generated_ui' to collect and save sensitive values instead. - Use 'meta_save_skill' only for workflows that are reasonably repeatable—patterns you expect to run again with similar structure or inputs. Do not save one-off tasks, unique ad-hoc work, or highly bespoke requests as skills; run those with 'execute' instead. Use the optional 'collection' field to group related saved skills, and use 'meta_update_skill' to replace an existing skill's code in place. - When a saved skill declares parameters, pass values via meta_run_skill params; the codemode can read them from the params variable. @@ -50,7 +50,7 @@ ${domainInstructions} How to use search - Call the 'search' tool with a natural-language 'query' describing what you need (optional 'limit', 'detail'). - Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optionally pass \`memory_context\` when you want to attach short, structured task context for future memory-aware behavior. +- Optionally pass \`memoryContext\` when you want to attach short, structured task context for future memory-aware behavior. - Narrow results by rephrasing 'query', or use the optional 'skill_collection' filter when you only want saved skills from one collection slug. - Saved skills appear when the MCP client provides an authenticated user; use 'meta_get_skill' for full skill code. - Use domain descriptions above as vocabulary hints in your query text. @@ -81,7 +81,7 @@ Destructive Cloudflare access How to use execute - The sandbox provides a 'codemode' object with async methods for each capability. - Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optionally pass \`memory_context\` when you want to attach short, structured task context for future memory-aware behavior. +- Optionally pass \`memoryContext\` when you want to attach short, structured task context for future memory-aware behavior. - Use capability names discovered from search. - Pass one args object that matches the capability inputSchema. - Each capability call returns that capability's raw structured result value. @@ -103,7 +103,7 @@ How to use execute MCP App tools - Use 'open_generated_ui' when you want an interactive UI in MCP App compatible hosts. - Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optionally pass \`memory_context\` when you want to attach short, structured task context for future memory-aware behavior. +- Optionally pass \`memoryContext\` when you want to attach short, structured task context for future memory-aware behavior. - Pass either inline source code with 'code' or reopen a saved app with 'app_id' (exactly one is allowed). - Prefer body-focused HTML fragments when possible, but full HTML documents are also supported. - Use generated UI whenever the user needs to enter a sensitive value. Do not ask the user to paste secrets or credentials into chat. diff --git a/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts b/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts index 27e8e3efd3..aa196dc965 100644 --- a/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts +++ b/packages/worker/src/mcp/mcp-server.mcp-e2e.test.ts @@ -40,7 +40,7 @@ test('mcp server returns built-in instructions and base server metadata', async ).toContain('"matches"') }) -test('mcp server search echoes provided conversationId and accepts memory_context', async () => { +test('mcp server search echoes provided conversationId and accepts memoryContext', async () => { await using database = await createTestDatabase() await using server = await startDevServer(database.persistDir) await using mcpClient = await createMcpClient(server.origin, database.user) @@ -50,7 +50,7 @@ test('mcp server search echoes provided conversationId and accepts memory_contex arguments: { query: 'generated ui', conversationId: 'searchctx1234', - memory_context: { + memoryContext: { task: 'Find UI-related tools', entities: ['generated ui'], constraints: ['brief'], @@ -230,7 +230,7 @@ test('mcp server executes user code against codemode', async () => { expect(textOutput).toContain('hosted_url') }) -test('mcp server execute generates conversationId when omitted and accepts memory_context', async () => { +test('mcp server execute generates conversationId when omitted and accepts memoryContext', async () => { await using database = await createTestDatabase() await using server = await startDevServer(database.persistDir) await using mcpClient = await createMcpClient(server.origin, database.user) @@ -239,7 +239,7 @@ test('mcp server execute generates conversationId when omitted and accepts memor name: 'execute', arguments: { code: `async () => ({ ok: true })`, - memory_context: { + memoryContext: { task: 'Return a small payload', constraints: ['no side effects'], }, @@ -424,7 +424,7 @@ test('mcp server opens generated ui with inline code and serves runtime resource arguments: { code: '

Hello Shell

Inline app content.

', conversationId: 'uictx1234567', - memory_context: { + memoryContext: { task: 'Render inline UI', entities: ['generated ui'], }, diff --git a/packages/worker/src/mcp/tools/execute.node.test.ts b/packages/worker/src/mcp/tools/execute.node.test.ts index 936e87ca51..82dafc935c 100644 --- a/packages/worker/src/mcp/tools/execute.node.test.ts +++ b/packages/worker/src/mcp/tools/execute.node.test.ts @@ -26,6 +26,6 @@ test('execute tool description encourages fewer execute calls', async () => { 'Pass `conversationId` to group related calls across the same conversation.', ) expect(registerTool.mock.calls[0]?.[1]?.description).toContain( - 'Pass `memory_context` with short, structured task context', + 'Pass `memoryContext` with short, structured task context', ) }) diff --git a/packages/worker/src/mcp/tools/execute.ts b/packages/worker/src/mcp/tools/execute.ts index f4778922c4..69aea31a0e 100644 --- a/packages/worker/src/mcp/tools/execute.ts +++ b/packages/worker/src/mcp/tools/execute.ts @@ -30,11 +30,11 @@ optional \`params\`. If you need the saved code, call \`meta_get_skill\` and pass the returned code into this tool. This tool requires \`code\` and also accepts optional tool-call context fields -such as \`conversationId\` and \`memory_context\`. +such as \`conversationId\` and \`memoryContext\`. Optional tool-call context: - Pass \`conversationId\` to group related calls across the same conversation. Clients should generate and reuse a short value when possible. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Pass \`memory_context\` with short, structured task context for future memory-aware behavior. Keep it factual and concise rather than hidden reasoning. +- Pass \`memoryContext\` with short, structured task context for future memory-aware behavior. Keep it factual and concise rather than hidden reasoning. Available in your code: @@ -129,7 +129,7 @@ export async function registerExecuteTool(agent: McpRegistrationAgent) { .string() .describe('JavaScript async arrow function to execute capabilities.'), conversationId: conversationIdInputField, - memory_context: memoryContextInputField, + memoryContext: memoryContextInputField, }, annotations: executeTool.annotations, }, @@ -139,7 +139,7 @@ export async function registerExecuteTool(agent: McpRegistrationAgent) { }: { code: string conversationId?: string - memory_context?: z.infer + memoryContext?: z.infer }) => { const startedAt = performance.now() const env = agent.getEnv() diff --git a/packages/worker/src/mcp/tools/open-generated-ui.ts b/packages/worker/src/mcp/tools/open-generated-ui.ts index 445839e3cd..e896944932 100644 --- a/packages/worker/src/mcp/tools/open-generated-ui.ts +++ b/packages/worker/src/mcp/tools/open-generated-ui.ts @@ -29,7 +29,7 @@ Behavior: - Saved apps can declare reusable parameters; pass runtime values via \`params\` and read them from \`kodyWidget.params\` after importing \`kodyWidget\` from \`@kody/ui-utils\`. - \`code\` may be a full HTML document or a fragment. - Optional \`conversationId\` groups related MCP calls. Reuse the same value across follow-up tool calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optional \`memory_context\` carries short, structured task context for future memory-aware behavior. +- Optional \`memoryContext\` carries short, structured task context for future memory-aware behavior. Generated UI basics: - The runtime exposes module helpers from the \`@kody/ui-utils\` import-map alias; prefer \`import { kodyWidget } from '@kody/ui-utils'\`. @@ -77,7 +77,7 @@ const inputSchema = z .optional() .describe('Optional short description for the current render session.'), conversationId: conversationIdInputField, - memory_context: memoryContextInputField, + memoryContext: memoryContextInputField, params: z .record(z.string(), z.unknown()) .optional() diff --git a/packages/worker/src/mcp/tools/search.ts b/packages/worker/src/mcp/tools/search.ts index 65dadcacbb..3deeeae72b 100644 --- a/packages/worker/src/mcp/tools/search.ts +++ b/packages/worker/src/mcp/tools/search.ts @@ -66,9 +66,9 @@ Pass a **query** string describing what you want to do. Results are ranked with Optional **limit** (default 15) caps how many results are returned. **detail: true** includes extra metadata (for skills: inferred capabilities, collection slug, etc.; for capabilities: JSON schemas where applicable). Optional **skill_collection** narrows saved skill results to one normalized collection/domain slug while still searching builtins, apps, and secrets normally. Optional **conversationId** groups related calls across the same client -conversation. Clients should generate and reuse one when possible; Kody returns -one in \`structuredContent.conversationId\` when omitted. Optional -\`memory_context\` accepts short, structured task context for future + conversation. Clients should generate and reuse one when possible; Kody returns + one in \`structuredContent.conversationId\` when omitted. Optional +\`memoryContext\` accepts short, structured task context for future memory-aware behavior. Example arguments: @@ -188,7 +188,7 @@ export async function registerSearchTool(agent: McpRegistrationAgent) { .optional() .describe('Include full metadata / schemas when true.'), conversationId: conversationIdInputField, - memory_context: memoryContextInputField, + memoryContext: memoryContextInputField, }, annotations: searchTool.annotations, }, @@ -198,7 +198,7 @@ export async function registerSearchTool(agent: McpRegistrationAgent) { limit?: number detail?: boolean conversationId?: string - memory_context?: z.infer + memoryContext?: z.infer }) => { const startedAt = performance.now() const conversationId = resolveConversationId(args.conversationId) From 7bdaa186ea3d6c1ef4e8fe20a8f644b3c71df79a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:36:48 +0000 Subject: [PATCH 4/6] Consolidate MCP memory guidance Co-authored-by: me --- packages/worker/src/mcp/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/worker/src/mcp/index.ts b/packages/worker/src/mcp/index.ts index 04eac46a5f..405da77fcb 100644 --- a/packages/worker/src/mcp/index.ts +++ b/packages/worker/src/mcp/index.ts @@ -33,7 +33,9 @@ Quick start - Call 'execute' or 'meta_run_skill' next to run capability code. - Call 'open_generated_ui' when you want an interactive UI rendered in an MCP App host. - The public MCP tools accept optional \`conversationId\` and \`memoryContext\` fields. Clients should generate and reuse a short \`conversationId\` across related calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Keep \`memoryContext\` short, structured, and task-focused. It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. +- Memory context: + - Keep \`memoryContext\` short, structured, and task-focused. + - It is reserved for future memory-aware behavior and is not persisted or used for retrieval yet in this phase. - Never ask the user to paste secrets, tokens, API keys, passwords, OAuth codes, or client secrets into chat. Use saved secrets when available, or use 'open_generated_ui' to collect and save sensitive values instead. - Use 'meta_save_skill' only for workflows that are reasonably repeatable—patterns you expect to run again with similar structure or inputs. Do not save one-off tasks, unique ad-hoc work, or highly bespoke requests as skills; run those with 'execute' instead. Use the optional 'collection' field to group related saved skills, and use 'meta_update_skill' to replace an existing skill's code in place. - When a saved skill declares parameters, pass values via meta_run_skill params; the codemode can read them from the params variable. @@ -50,7 +52,6 @@ ${domainInstructions} How to use search - Call the 'search' tool with a natural-language 'query' describing what you need (optional 'limit', 'detail'). - Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optionally pass \`memoryContext\` when you want to attach short, structured task context for future memory-aware behavior. - Narrow results by rephrasing 'query', or use the optional 'skill_collection' filter when you only want saved skills from one collection slug. - Saved skills appear when the MCP client provides an authenticated user; use 'meta_get_skill' for full skill code. - Use domain descriptions above as vocabulary hints in your query text. @@ -81,7 +82,6 @@ Destructive Cloudflare access How to use execute - The sandbox provides a 'codemode' object with async methods for each capability. - Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optionally pass \`memoryContext\` when you want to attach short, structured task context for future memory-aware behavior. - Use capability names discovered from search. - Pass one args object that matches the capability inputSchema. - Each capability call returns that capability's raw structured result value. @@ -103,7 +103,6 @@ How to use execute MCP App tools - Use 'open_generated_ui' when you want an interactive UI in MCP App compatible hosts. - Optionally pass \`conversationId\` and reuse it on later related tool calls. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optionally pass \`memoryContext\` when you want to attach short, structured task context for future memory-aware behavior. - Pass either inline source code with 'code' or reopen a saved app with 'app_id' (exactly one is allowed). - Prefer body-focused HTML fragments when possible, but full HTML documents are also supported. - Use generated UI whenever the user needs to enter a sensitive value. Do not ask the user to paste secrets or credentials into chat. From 8768b8ce92b3dbbf4aeb761374259c29b3d44cf1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:42:02 +0000 Subject: [PATCH 5/6] Trim redundant MCP tool input docs Co-authored-by: me --- packages/worker/src/mcp/tools/execute.node.test.ts | 8 ++++---- packages/worker/src/mcp/tools/execute.ts | 7 ------- packages/worker/src/mcp/tools/open-generated-ui.ts | 2 -- packages/worker/src/mcp/tools/search.ts | 6 ------ 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/packages/worker/src/mcp/tools/execute.node.test.ts b/packages/worker/src/mcp/tools/execute.node.test.ts index 82dafc935c..260edf9056 100644 --- a/packages/worker/src/mcp/tools/execute.node.test.ts +++ b/packages/worker/src/mcp/tools/execute.node.test.ts @@ -22,10 +22,10 @@ test('execute tool description encourages fewer execute calls', async () => { expect(registerTool.mock.calls[0]?.[1]?.description).toContain( 'chain the capability calls there and return the final useful result', ) - expect(registerTool.mock.calls[0]?.[1]?.description).toContain( - 'Pass `conversationId` to group related calls across the same conversation.', + expect(registerTool.mock.calls[0]?.[1]?.description).not.toContain( + 'Pass `conversationId`', ) - expect(registerTool.mock.calls[0]?.[1]?.description).toContain( - 'Pass `memoryContext` with short, structured task context', + expect(registerTool.mock.calls[0]?.[1]?.description).not.toContain( + 'Pass `memoryContext`', ) }) diff --git a/packages/worker/src/mcp/tools/execute.ts b/packages/worker/src/mcp/tools/execute.ts index 69aea31a0e..26bf48dd49 100644 --- a/packages/worker/src/mcp/tools/execute.ts +++ b/packages/worker/src/mcp/tools/execute.ts @@ -29,13 +29,6 @@ To run a saved skill by id, prefer \`meta_run_skill\` with \`skill_id\` and optional \`params\`. If you need the saved code, call \`meta_get_skill\` and pass the returned code into this tool. -This tool requires \`code\` and also accepts optional tool-call context fields -such as \`conversationId\` and \`memoryContext\`. - -Optional tool-call context: -- Pass \`conversationId\` to group related calls across the same conversation. Clients should generate and reuse a short value when possible. If omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Pass \`memoryContext\` with short, structured task context for future memory-aware behavior. Keep it factual and concise rather than hidden reasoning. - Available in your code: type CapabilityArgs = Record; diff --git a/packages/worker/src/mcp/tools/open-generated-ui.ts b/packages/worker/src/mcp/tools/open-generated-ui.ts index e896944932..608d9467cd 100644 --- a/packages/worker/src/mcp/tools/open-generated-ui.ts +++ b/packages/worker/src/mcp/tools/open-generated-ui.ts @@ -28,8 +28,6 @@ Behavior: - Use \`app_id\` to reopen previously saved UI source without sending that source code back through the model. - Saved apps can declare reusable parameters; pass runtime values via \`params\` and read them from \`kodyWidget.params\` after importing \`kodyWidget\` from \`@kody/ui-utils\`. - \`code\` may be a full HTML document or a fragment. -- Optional \`conversationId\` groups related MCP calls. Reuse the same value across follow-up tool calls when possible; if omitted, Kody generates one and returns it in \`structuredContent.conversationId\`. -- Optional \`memoryContext\` carries short, structured task context for future memory-aware behavior. Generated UI basics: - The runtime exposes module helpers from the \`@kody/ui-utils\` import-map alias; prefer \`import { kodyWidget } from '@kody/ui-utils'\`. diff --git a/packages/worker/src/mcp/tools/search.ts b/packages/worker/src/mcp/tools/search.ts index 3deeeae72b..bbcb216711 100644 --- a/packages/worker/src/mcp/tools/search.ts +++ b/packages/worker/src/mcp/tools/search.ts @@ -65,12 +65,6 @@ Pass a **query** string describing what you want to do. Results are ranked with Optional **limit** (default 15) caps how many results are returned. **detail: true** includes extra metadata (for skills: inferred capabilities, collection slug, etc.; for capabilities: JSON schemas where applicable). Optional **skill_collection** narrows saved skill results to one normalized collection/domain slug while still searching builtins, apps, and secrets normally. - Optional **conversationId** groups related calls across the same client - conversation. Clients should generate and reuse one when possible; Kody returns - one in \`structuredContent.conversationId\` when omitted. Optional -\`memoryContext\` accepts short, structured task context for future -memory-aware behavior. - Example arguments: - \`{ "query": "saved interactive dashboard app", "limit": 10 }\` - \`{ "query": "github automation", "skill_collection": "release-engineering" }\` From fce204c6659381fcecd9156997e5340b1ccc821a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 30 Mar 2026 13:46:15 +0000 Subject: [PATCH 6/6] Remove low-value execute description assertions Co-authored-by: me --- packages/worker/src/mcp/tools/execute.node.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/worker/src/mcp/tools/execute.node.test.ts b/packages/worker/src/mcp/tools/execute.node.test.ts index 260edf9056..de095e9d05 100644 --- a/packages/worker/src/mcp/tools/execute.node.test.ts +++ b/packages/worker/src/mcp/tools/execute.node.test.ts @@ -22,10 +22,4 @@ test('execute tool description encourages fewer execute calls', async () => { expect(registerTool.mock.calls[0]?.[1]?.description).toContain( 'chain the capability calls there and return the final useful result', ) - expect(registerTool.mock.calls[0]?.[1]?.description).not.toContain( - 'Pass `conversationId`', - ) - expect(registerTool.mock.calls[0]?.[1]?.description).not.toContain( - 'Pass `memoryContext`', - ) })