Skip to content
22 changes: 9 additions & 13 deletions packages/worker/src/agent-turn/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { z } from 'zod'
import { type McpCallerContext } from '@kody-internal/shared/chat.ts'
import { getCapabilityRegistryForContext } from '#mcp/capabilities/registry.ts'
import {
buildSavedPackageSearchRows,
loadDownRemoteConnectorStatuses,
loadOptionalSearchRows,
resolveSearchMemoryContext,
Expand Down Expand Up @@ -56,19 +57,13 @@ export async function createAgentTurnToolSet(input: {
input.env.APP_DB,
{ userId: userId! },
)
return savedPackages.map((savedPackage) => ({
record: savedPackage,
projection: {
name: savedPackage.name,
kodyId: savedPackage.kodyId,
description: savedPackage.description,
tags: savedPackage.tags,
searchText: savedPackage.searchText,
hasApp: savedPackage.hasApp,
exports: [],
jobs: [],
},
}))
const packageRows = await buildSavedPackageSearchRows({
env: input.env,
baseUrl: input.callerContext.baseUrl,
userId: userId!,
records: savedPackages,
})
return packageRows
},
loadUserSecrets: () =>
listUserSecretsForSearch({
Expand Down Expand Up @@ -110,6 +105,7 @@ export async function createAgentTurnToolSet(input: {
return {
offline: result.offline,
warnings: optionalRows.warnings,
guidance: result.guidance,
memories: memoryToolContext
? {
surfaced: memoryToolContext.memories,
Expand Down
6 changes: 6 additions & 0 deletions packages/worker/src/mcp/tools/search-format.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ test('search markdown and entity detail formatting preserve structured behavior'
const markdown = formatSearchMarkdown({
baseUrl: 'http://localhost',
warnings: [],
guidance:
'Inspect connector detail with `search({ entity: "github:connector" })` next.',
matches: [
{
type: 'value',
Expand Down Expand Up @@ -56,6 +58,8 @@ test('search markdown and entity detail formatting preserve structured behavior'

expect(markdown).toContain('`user:preferred_repo:value`')
expect(markdown).toContain('`https://api.github.com`')
expect(markdown).toContain('## Recommended next step')
expect(markdown).toContain('`search({ entity: "github:connector" })`')

expect(
toSlimStructuredMatches({
Expand Down Expand Up @@ -116,6 +120,8 @@ test('search markdown and entity detail formatting preserve structured behavior'
flow: 'confidential',
apiBaseUrl: 'https://api.github.com',
requiredHosts: ['api.github.com'],
nextStep:
'Inspect connector detail with search({ entity: "github:connector" }) and then run a minimal authenticated execute smoke test before building or calling integration-backed code.',
},
])

Expand Down
13 changes: 13 additions & 0 deletions packages/worker/src/mcp/tools/search-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type SearchResultStructuredContent = {
matches: Array<SlimSearchMatch>
offline: boolean
warnings: Array<string>
guidance?: string
telemetry?: {
intent: {
task: string
Expand Down Expand Up @@ -102,6 +103,7 @@ export type SlimSearchMatch =
tags: Array<string>
hasApp: boolean
hostedUrl: string | null
nextStep?: string
}
| {
type: 'secret'
Expand Down Expand Up @@ -133,6 +135,7 @@ export type SlimSearchMatch =
flow: string
apiBaseUrl: string | null
requiredHosts: Array<string>
nextStep?: string
}

export type SearchEntityDetailStructured =
Expand Down Expand Up @@ -357,6 +360,7 @@ export function formatSearchMarkdown(input: {
warnings: Array<string>
baseUrl: string
includePreamble?: boolean
guidance?: string
memories?: {
surfaced: Array<{
category: string | null
Expand Down Expand Up @@ -401,6 +405,10 @@ export function formatSearchMarkdown(input: {
}
}

if (input.guidance?.trim()) {
lines.push('', '## Recommended next step', '', input.guidance.trim())
}

for (const match of input.matches) {
lines.push('', ...formatMatchBlock(match, input.baseUrl))
}
Expand Down Expand Up @@ -498,6 +506,9 @@ export function toSlimStructuredMatches(input: {
}
}
if (match.type === 'package') {
const nextStep = match.hasApp
? `Open the app with open_generated_ui({ kody_id: "${match.kodyId}" }) or inspect package detail with search({ entity: "${match.kodyId}:package" }).`
: `Inspect package detail with search({ entity: "${match.kodyId}:package" }) to review exports, then import the needed entry from "${buildPackageImportSpecifier(match.kodyId, '.')}".`
return {
type: 'package',
id: match.kodyId,
Expand All @@ -514,6 +525,7 @@ export function toSlimStructuredMatches(input: {
hostedUrl: match.hasApp
? buildPackageHostedUrl(input.baseUrl, match.kodyId)
: null,
nextStep,
}
}
if (match.type === 'value') {
Expand Down Expand Up @@ -541,6 +553,7 @@ export function toSlimStructuredMatches(input: {
flow: match.flow,
apiBaseUrl: match.apiBaseUrl,
requiredHosts: match.requiredHosts,
nextStep: `Inspect connector detail with search({ entity: "${match.connectorName}:connector" }) and then run a minimal authenticated execute smoke test before building or calling integration-backed code.`,
}
}
return {
Expand Down
Loading
Loading