Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/guides/integration-bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This guide is about **ordering**. The goal is to finish the integration setup
and prove it works **before** you save or present downstream packages or package
apps that depend on it.

Agents should use this guide with `search` results for saved integrations,
secret references, and capability details before exploring local repository
source for package-app patterns.

## What counts as an integration bootstrap

Use this workflow when the requested result depends on any of the following:
Expand Down
5 changes: 5 additions & 0 deletions docs/use/first-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ integration, value, or secret reference, then run work through **execute**.
alias, `job_run_now` can trigger an existing job immediately for debugging or
catch-up runs, and `job_update` / `job_delete` let you correct or remove an
existing scheduled job by id.
- **Bootstrap integration-backed work before building.** When a package, package
app, or workflow depends on OAuth, a saved secret, or a third-party API, use
`search` and the `kody_official_guide` `integration_bootstrap` guide first.
Confirm the integration or secret exists, run a cheap authenticated smoke test
in `execute`, then build the downstream artifact.
- **Ask for natural-language goals**, for example: “Search Kody for GitHub pull
request automation” or “Find a saved package for Cloudflare DNS helpers.”
- **Do not paste secrets in chat.** Use saved secrets, generated UI, or the
Expand Down
6 changes: 6 additions & 0 deletions docs/use/packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ Use the package app model when the package needs:
- hosted callback URLs
- package-owned backend behavior

When a package app depends on OAuth, saved secrets, or a third-party API, run
the integration bootstrap first: use `search` for the saved integration or
secret reference, load `kody_official_guide` with
`guide: "integration_bootstrap"`, and complete a minimal authenticated `execute`
smoke test before treating the app as ready.

Treat package apps like Worker-style modules:

- app code lives in the package repo
Expand Down
6 changes: 6 additions & 0 deletions docs/use/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,9 @@ Use **search** as the default way to discover whether an integration or secret
already exists before switching to **execute**. Runtime code inside **execute**
can call **`codemode.secret_list(...)`** when it needs secret metadata, but
**search** is the primary discovery path.

For integration-backed packages, package apps, or workflows, pair that discovery
with the official `integration_bootstrap` guide. Inspect the relevant
`integration` or `secret` entity, run one cheap authenticated **execute** smoke
test, then build the downstream artifact. If setup is missing, load the official
OAuth or secret-backed setup guide that matches the auth path.
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@ test('offline search returns provided specs without depending on global ranks',
name: 'kody_official_guide',
domain: 'coding',
description:
'Load official Kody guides: integration_bootstrap, secret_backed_integration, oauth (/connect/oauth), generated_ui_oauth (hosted package app), connect_secret.',
'Load official Kody guides: integration_bootstrap, secret_backed_integration, integration_backed_app, oauth (/connect/oauth), generated_ui_oauth (hosted package app), connect_secret, package_service_pattern.',
keywords: [
'integration bootstrap',
'secret backed integration',
'integration backed app',
'oauth',
'generated ui',
'redirect uri',
'provider registration',
'secret',
'package service',
],
readOnly: true,
idempotent: true,
Expand All @@ -85,16 +87,18 @@ test('offline search returns provided specs without depending on global ranks',
enum: [
'integration_bootstrap',
'secret_backed_integration',
'integration_backed_app',
'oauth',
'generated_ui_oauth',
'connect_secret',
'package_service_pattern',
],
},
},
required: ['guide'],
},
inputTypeDefinition:
'type KodyOfficialGuideInput = {\n\tguide: "integration_bootstrap" | "secret_backed_integration" | "oauth" | "generated_ui_oauth" | "connect_secret"\n}',
'type KodyOfficialGuideInput = {\n\tguide: "integration_bootstrap" | "secret_backed_integration" | "integration_backed_app" | "oauth" | "generated_ui_oauth" | "connect_secret" | "package_service_pattern"\n}',
},
} satisfies Record<string, CapabilitySpec>
const env = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ const ctx = {
},
}

test('kody_official_guide description surfaces integration bootstrap flow', () => {
expect(kodyOfficialGuideCapability.description).toContain(
'Prefer this capability plus `search` results',
)
expect(kodyOfficialGuideCapability.description).toContain(
'guide: "integration_bootstrap"',
)
expect(kodyOfficialGuideCapability.description).toContain(
'saved `integration` / `secret` entities',
)
expect(kodyOfficialGuideCapability.description).toContain(
'cheap authenticated smoke test before building',
)
expect(kodyOfficialGuideCapability.description).toContain(
'Available guides (order matters',
)
})

test('kody_official_guide returns markdown when fetch succeeds', async () => {
const originalFetch = globalThis.fetch
const url = buildKodyOfficialGuideUrlForTest('integration_bootstrap')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,27 @@ function buildCapabilityDescription(): string {
const g = kodyOfficialGuideCatalog[id]
return `- \`${id}\`: ${g.summary}`
})
const intro = [
'Load an official Kody guide from the kody GitHub repository (markdown).',
'Prefer this capability plus `search` results over local repo spelunking',
'when Kody auth or integration behavior is already documented.',
'**For third-party integrations that will power a package, package app, or workflow,',
'use `guide: "integration_bootstrap"` first.**',
'It covers checking saved `integration` / `secret` entities',
'and running the cheap authenticated smoke test before building.',
'For non-OAuth APIs backed by saved secrets,',
'then use `guide: "secret_backed_integration"` as the default recipe.',
'After the smoke test passes and you are ready to build a package app,',
'use `guide: "integration_backed_app"` for the default package-app pattern.',
'For OAuth mechanics, then use `guide: "oauth"` (standard `/connect/oauth` path).',
'Use `generated_ui_oauth` only for custom package-app OAuth.',
'For API keys/PATs, use `connect_secret` for secret collection.',
'For package-native long-lived service work built on `kody.services`,',
'use `package_service_pattern`.',
'If you are unsure, **call this capability** with the right `guide` instead of guessing.',
].join(' ')
return [
'Load an official Kody guide from the kody GitHub repository (markdown). **For third-party integrations that will power a package, package app, or workflow, use `guide: "integration_bootstrap"` first.** For non-OAuth APIs backed by saved secrets, then use `guide: "secret_backed_integration"` as the default recipe. After the smoke test passes and you are ready to build a package app, use `guide: "integration_backed_app"` for the default package-app pattern. For OAuth mechanics, then use `guide: "oauth"` (standard `/connect/oauth` path). Use `generated_ui_oauth` only for custom package-app OAuth. For API keys/PATs, use `connect_secret` for secret collection. For package-native long-lived service work built on `kody.services`, use `package_service_pattern`. If you are unsure, **call this capability** with the right `guide` instead of guessing.',
intro,
'',
'Available guides (order matters—start with `integration_bootstrap` for integration-dependent work):',
...lines,
Expand Down
12 changes: 12 additions & 0 deletions packages/worker/src/mcp/server-instructions.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,15 @@ test('surfaces remote connector identifiers and compacts long descriptions', ()
expect(longConnectorLine).not.toContain(longDescription)
expect(longConnectorDescription.length).toBeLessThanOrEqual(240)
})

test('surfaces integration-backed workflow before package construction', () => {
const instructions = buildMcpServerInstructions(null)

expect(instructions).toContain('Integration-backed work')
expect(instructions).toContain('guide: "integration_bootstrap"')
expect(instructions).toContain('integration` or `secret` entity')
expect(instructions).toContain('cheap authenticated `execute` smoke test')
expect(instructions).toContain('before local repo exploration')
expect(instructions).toContain('`oauth` for `/connect/oauth`')
expect(instructions).toContain('`secret_backed_integration`')
})
3 changes: 2 additions & 1 deletion packages/worker/src/mcp/server-instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Conventions
- Do not ask the user to paste secrets in chat; use saved secrets or \`open_generated_ui\`.
- \`package_save\`: create or replace a repo-backed saved package rooted at \`package.json\`. Standard package exports define the package surface. \`package.json#kody\` contains Kody-specific metadata such as tags, optional app config, and package-owned jobs.
- \`package_get\` / \`package_list\` / \`package_delete\`: inspect or manage saved packages for the signed-in user.
- Integration-backed work: use \`search\` and official guides before local repo exploration. For packages, package apps, or workflows that depend on third-party auth, first call \`kody_official_guide\` with \`guide: "integration_bootstrap"\`, confirm the required \`integration\` or \`secret\` entity exists through \`search\`, run a cheap authenticated \`execute\` smoke test, then build. If setup is missing, load \`oauth\` for \`/connect/oauth\`, \`connect_secret\` for secret collection, and \`secret_backed_integration\` for the default non-OAuth recipe.
- \`job_list\` / \`job_get\`: inspect the signed-in user's scheduled jobs, recent run outcomes, and current per-user alarm state when debugging scheduling issues. Pass \`includeCode: true\` to \`job_get\` when you need the stored repo-backed job source entrypoint and code.
- \`job_schedule\`: schedule a repo-backed job for the signed-in user without creating a saved package first. Supports one-off, interval, and cron schedules.
- \`job_schedule_once\`: compatibility wrapper for one-off repo-backed jobs when you only need a single run time.
Expand Down Expand Up @@ -92,7 +93,7 @@ search
execute
- Single ESM module string with a default export such as \`export default async function main(input = {}) { ... }\`; \`params\` are passed as the first argument. Import runtime APIs from \`kody:runtime\`. Example: \`import { codemode, refreshAccessToken, createAuthenticatedFetch, oauthClientCredentials, secretHeaders, workflows } from 'kody:runtime'\`. Built-in capabilities returned by \`search\` are available through \`codemode\`: use \`await codemode.capability_id(input)\` for valid identifier names or \`await codemode["capability-id"](input)\` for non-identifier ids. \`workflows.create\` can queue either inline \`code\` or a saved-package \`exportName\` through the shared Workflow hub. Use \`secretHeaders.basic(...)\` or \`oauthClientCredentials(...)\` for client-credentials Basic Auth instead of asking users to precompute a Basic header. Prefer one \`execute\` when the plan is clear. Full rules for \`fetch\`, placeholders, \`secret_list\` / \`value_get\`, and \`x-kody-secret\`: see the \`execute\` tool description.
- Cross-package imports use specifiers such as \`kody:@scope/my-package/export-name\`. For dynamic current-version calls inside package runtime code or authenticated execute calls, prefer \`packages.invokeChecked({ kodyId, exportName, params })\` or \`packages.check(...)\` followed by \`packages.invoke(check.invoke)\`; use static imports for library-like bundled snapshots. Saved package names must be scoped (\`@scope/<leaf>\`) and the leaf segment must match \`kody.id\`. Package jobs are owned by packages, ad hoc jobs can be scheduled with \`job_schedule\`, and package apps are optional package surfaces.
- Official how-to guides from the Kody repo: if a requested package or workflow depends on a third-party integration, secrets, or OAuth, call \`kody_official_guide\` with \`guide: "integration_bootstrap"\` before building the package. Then load the relevant setup guide: \`oauth\` for standard third-party OAuth (\`/connect/oauth\`), \`connect_secret\` for secret collection, and \`secret_backed_integration\` for the default non-OAuth secret-backed recipe after bootstrap. If unsure, \`search\` for this capability and load the right guide before implementing.
- Official how-to guides from the Kody repo: if a requested package, package app, or workflow depends on a third-party integration, secrets, or OAuth, call \`kody_official_guide\` with \`guide: "integration_bootstrap"\` before building. If unsure, \`search\` for this capability and load the right guide before implementing.
- Do not save or present an auth-dependent package as complete until \`search\` shows the required integration or secret reference exists and a minimal authenticated \`execute\` smoke test succeeds.

open_generated_ui
Expand Down
36 changes: 34 additions & 2 deletions packages/worker/src/mcp/tools/execute.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function mockPerformanceSequence(...values: Array<number>) {
})
}

async function getExecuteHandler(
async function getExecuteRegistration(
callerContext: {
baseUrl: string
user: null | {
Expand All @@ -66,9 +66,27 @@ async function getExecuteHandler(
} as never)

expect(registerTool).toHaveBeenCalledTimes(1)
const [name, , handler] = registerTool.mock.calls[0] ?? []
const [name, rawOptions, handler] = registerTool.mock.calls[0] ?? []
expect(name).toBe('execute')
expect(typeof handler).toBe('function')
const options = rawOptions as { description: string }
return { options, handler }
}

async function getExecuteHandler(
callerContext: {
baseUrl: string
user: null | {
userId: string
email?: string
displayName?: string
}
} = {
baseUrl: 'https://example.com',
user: null,
},
) {
const { handler } = await getExecuteRegistration(callerContext)
return handler as (input: {
code: string
storageId?: string
Expand All @@ -95,6 +113,20 @@ async function getExecuteHandler(
}>
}

test('execute tool description surfaces integration-backed smoke-test flow', async () => {
const { options } = await getExecuteRegistration()

expect(options.description).toContain('integration-backed packages')
expect(options.description).toContain(
"kody_official_guide({ guide: 'integration_bootstrap' })",
)
expect(options.description).toContain(
'cheap read-only authenticated smoke test',
)
expect(options.description).toContain('before `package_save`')
expect(options.description).toContain('`oauth`, `connect_secret`')
})

test('execute tool passes through raw MCP content blocks in success responses', async () => {
const handler = await getExecuteHandler()
mockPerformanceSequence(100, 142)
Expand Down
2 changes: 2 additions & 0 deletions packages/worker/src/mcp/tools/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Never ask the user to paste credentials in chat; use generated UI to collect or

Prefer one \`execute\` when the workflow is clear; split calls when you need new user input or a changed plan.

For integration-backed packages, package apps, or workflows, use \`search\` and \`kody_official_guide({ guide: 'integration_bootstrap' })\` before building. Confirm the needed \`integration\` or \`secret\` entity exists, then run a cheap read-only authenticated smoke test in \`execute\` (for example a profile/viewer endpoint) before \`package_save\`, package app work, or workflow scheduling. If credentials are missing, load the matching official guide: \`oauth\`, \`connect_secret\`, or \`secret_backed_integration\`.

Example:

\`import { codemode } from 'kody:runtime'
Expand Down
21 changes: 19 additions & 2 deletions packages/worker/src/mcp/tools/search-handler.node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const { registerSearchTool } = await import('./search.ts')

const mockPerformanceNow = vi.spyOn(performance, 'now')

async function getSearchHandler() {
async function getSearchRegistration() {
const registerTool = vi.fn()

await registerSearchTool({
Expand All @@ -85,9 +85,15 @@ async function getSearchHandler() {
} as never)

expect(registerTool).toHaveBeenCalledTimes(1)
const [name, , handler] = registerTool.mock.calls[0] ?? []
const [name, rawOptions, handler] = registerTool.mock.calls[0] ?? []
expect(name).toBe('search')
expect(typeof handler).toBe('function')
const options = rawOptions as { description: string }
return { options, handler }
}

async function getSearchHandler() {
const { handler } = await getSearchRegistration()
return handler as (input: {
query?: string
entity?: string
Expand All @@ -113,6 +119,17 @@ async function getSearchHandler() {
}>
}

test('search tool description surfaces integration-backed discovery flow', async () => {
vi.clearAllMocks()
const { options } = await getSearchRegistration()

expect(options.description).toContain('Integration-backed packages')
expect(options.description).toContain('kody_official_guide')
expect(options.description).toContain('guide: "integration_bootstrap"')
expect(options.description).toContain('integration` or `secret` entities')
expect(options.description).toContain('authenticated `execute` smoke test')
})

test('search tool reports timing metadata across success and error flows', async () => {
vi.clearAllMocks()
const handler = await getSearchHandler()
Expand Down
7 changes: 7 additions & 0 deletions packages/worker/src/mcp/tools/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,13 @@ Secrets: never raw in results; use
Persisted values use \`codemode.value_get\` / \`codemode.value_list\`. Integrations
use \`codemode.integration_get\` / \`codemode.integration_list\`.

Integration-backed packages, package apps, and workflows: search for the provider
and \`kody_official_guide\`, inspect exact \`integration\` or \`secret\` entities,
load \`guide: "integration_bootstrap"\`, and continue only after a cheap
authenticated \`execute\` smoke test succeeds. If setup is missing, use the guide
that matches the auth path: \`oauth\`, \`connect_secret\`, or
\`secret_backed_integration\`.

If results look incomplete: \`meta_list_capabilities\` (full registry) or
\`meta_list_remote_connector_status\` (remote connectors).

Expand Down
Loading