Skip to content
KortixKortix
Esc
↑↓navigate↵open⌘Jpreview
On this page

Tools

The five Kortix tools, project tools you write in TypeScript or JavaScript, and which agent may call which.

A tool is a function the model calls during a turn. This page is for project authors who choose the tools a project loads, write their own, and limit them per agent. Tool selection and per-agent access live in kortix.yaml.

Three kinds of tools

Kind Names Comes from
Harness tools bash, read, write, edit, apply_patch, glob, grep, list, task, question, todowrite, webfetch, websearch, skill, lsp The harness, OpenCode or pi.
Kortix tools web_search, image_search, scrape_webpage, memory, show Kortix maintains them. You select them under tools:.
Project tools Any name you declare One module in your repo, declared under tools:.

Kortix tools and project tools are one module each. The sandbox runs the same module on both harnesses, with the same name, arguments, and output.

Kortix tool What it does
web_search Searches the web. Returns titles, URLs, snippets, and a short answer.
image_search Searches for images. Returns image URLs, titles, source pages, and dimensions.
scrape_webpage Fetches one or more pages and returns them as Markdown.
memory Views, creates, edits, renames, and deletes files under memory/, the project memory.
show Presents a file, image, URL, document, or text to the person in the session.

Choose Kortix tools

A new project lists all five under the top-level tools: key:

tools:
  web_search: kortix:web_search      # maintained by Kortix, updated automatically
  image_search: kortix:image_search
  scrape_webpage: kortix:scrape_webpage
  memory: kortix:memory
  show: kortix:show
  # lookup_order: tools/lookup_order.ts   # your own tool: a module in your repo

<name>: kortix:<name> keeps the tool Kortix maintains. A fix to it reaches the project with no change in the repo.

kortix.yaml Kortix tools a session gets
No tools: key, in the root file or in any imported file All five.
A tools: key in any file, even an empty one Only the Kortix tools it lists.

Project tools load in both cases.

  • Remove a Kortix tool: delete its line. Every agent of the project loses it. To hide a tool from one agent only, use agents.<name>.tools.
  • A skill that names a removed tool still loads. The model finds no such tool and uses other tools.
  • See what a session gets: run kortix tools ls from the project root.
$ kortix tools ls
NAME            KIND                 SOURCE
web_search      kortix (overridden)  tools/web_search.ts
scrape_webpage  kortix               kortix:scrape_webpage
memory          kortix               kortix:memory
show            kortix               kortix:show
lookup_order    project              tools/lookup_order.ts

Removed Kortix tools: image_search
Add `<name>: kortix:<name>` under `tools:` to get one back.

KIND is kortix, kortix (overridden) (a Kortix tool name with your module), or project. kortix tools ls --json prints { manifest, tools_key, tools: [{ name, kind, source }], removed }.

Rules for every entry under tools::

  • A name matches ^[a-z][a-z0-9_]{0,63}$: snake case, 64 characters at most.
  • A harness tool name (the table above, plus the OpenCode internals invalid, execute, and plan_exit) or a name that starts with pty_ is an error.
  • A value is kortix:<name> with the same name as the key, or a repo-relative module path.
  • A path ends in .ts, .js, .mts, .mjs, .cts, or .cjs. It does not start with / or - and has no . or .. segment. The folder is your choice.
  • An imported file may declare tools. A name declared in two files is an error.

Write a project tool

Project tools need kortix.yaml with kortix_version: 2 or 3.

Store the secret the tool reads

kortix secrets set WEATHER_API_KEY=-

The command reads the value from stdin. Grant the secret to each agent that calls the tool, in kortix.yaml: agents.<name>.secrets: all, or a list such as [WEATHER_API_KEY]. See Secrets.

Declare the name under env: in kortix.yaml too. In a kortix_version: 2 manifest, kortix validate and kortix ship report an undeclared grant as an error:

env:
  optional: [WEATHER_API_KEY]

Write the module

// tools/weather_now.ts
export default {
  description: 'Current weather for a city: temperature in °C and conditions.',
  parameters: {
    type: 'object',
    properties: { city: { type: 'string', description: 'City name, e.g. "Berlin"' } },
    required: ['city'],
  },
  async execute(args, context) {
    const key = context.env.WEATHER_API_KEY
    if (!key) throw new Error('WEATHER_API_KEY is not set. Run `kortix secrets set WEATHER_API_KEY=-` and grant it to this agent.')
    const url = `https://api.weather.example.com/v1/current?city=${encodeURIComponent(args.city)}`
    const response = await fetch(url, { headers: { authorization: `Bearer ${key}` }, signal: context.signal })
    if (!response.ok) throw new Error(`weather API answered ${response.status}: ${await response.text()}`)
    const data = await response.json()
    return { city: args.city, temperature_c: data.temp_c, conditions: data.conditions }
  },
}

Declare it in kortix.yaml

env:
  optional: [WEATHER_API_KEY]
tools:
  # … the Kortix tools you keep
  weather_now: tools/weather_now.ts
agents:
  my-agent:
    secrets: [WEATHER_API_KEY]

Then test it.

The default export is a plain object. It imports nothing from a harness.

Field Rule
description A non-empty string. The model reads it to decide when to call the tool.
parameters JSON Schema of the arguments, with type: 'object'. The model receives it as written, so list required arguments in required. pi checks the arguments against it before execute runs. Check required values in execute too.
execute(args, context) May be async. Returns a string, or any JSON value, which the model receives as indented JSON. A thrown error is the tool result, and the model reads its message.

context carries:

Field Value
sessionId The Kortix session the call belongs to.
agent The agent that made the call.
directory The project checkout the session works in, /workspace. Resolve relative paths against it.
env The sandbox environment plus the secrets this agent receives, with their current values. Read a secret as context.env.NAME. A secret changed after the sandbox started is in context.env, not in process.env.
signal An AbortSignal that aborts when the turn stops. Pass it to fetch.
  • Output size. An output over 49 KiB or 1,990 lines is saved whole to a file in the sandbox temp directory. The model receives the head, the size, and the file path. Return what the model needs, not a raw dump.
  • Errors. Throw a message that says what to change. The model reads it and can act on it.
  • Imports. A module may import node:* modules, Bun APIs, and relative files. TypeScript runs without a build step. A package resolves only from a node_modules next to the module or above it. The sandbox installs no package for a tool, so use fetch for HTTP APIs.
  • Load failures. A module that does not load is skipped, and the other tools still load. Causes: a missing file, no default export, parameters that is not an object schema, an error at import, or a package that does not resolve. The sandbox logs [tools] project tools that did not load with the reason for each tool.

Give each agent its tools

agents.<name>.tools decides which tools an agent’s model sees. It names harness, Kortix, and project tools alike.

agents:
  kortix:
    tools: all                                    # every tool (the default)
  researcher:
    tools: [read, grep, web_search, weather_now]  # only these
  support:
    tools:
      exclude: [bash, write, edit]                # every tool but these
  greeter:
    tools: none                                   # no tool
Value The agent gets
Omitted, or all Every tool.
none No tool.
A list Only the tools listed. A list that contains * is all.
{ exclude: [...] } Every tool except the ones listed.
{ bash: false, … } The map form: false removes a tool.
  • Visibility, not permission. A hidden tool is also denied if the model calls it. For a visible tool, the agent’s permission rules decide allow, ask, or deny. See Agents & permissions.
  • Kortix tools. An agent list cannot add a Kortix tool the project does not load. List it under the top-level tools: first.
  • Connector tools follow agents.<name>.connectors, not this list. See Connectors.
  • Subagents. On pi, a subagent gets only the tools that both its own list and the parent agent’s list allow. On OpenCode, a subagent gets the tools of its own list. To stop an agent from delegating, leave task out of its list.
  • OpenCode groups. On OpenCode, bash also covers the pty_* terminal tools. write, edit, and apply_patch share one permission: listing one shows all three, and excluding one hides all three. pi has separate write and edit tools.
  • Typos. kortix validate warns about a name that is not a harness, Kortix, or project tool, and about a Kortix tool the project does not load. A typo in an exclude list leaves the real tool on.

Eject or override a Kortix tool

kortix tools eject <name> gives the project its own copy of a Kortix tool:

$ kortix tools eject web_search
  ✓  Ejected web_search
  wrote   tools/web_search.ts (the Kortix source, now the project's to change)
  set     kortix.yaml: tools.web_search: tools/web_search.ts

Next:
  1. Edit tools/web_search.ts. Keep the default export: { description, parameters, execute(args, context) }.
  2. kortix validate
  3. git add tools/web_search.ts kortix.yaml && git commit -m "Own the web_search tool"
  4. Open a change request: git push origin HEAD && kortix cr open --title "Own the web_search tool"
Sessions load the copy after the change request merges.

In a local checkout, give kortix cr open the branch you pushed: --head <branch>. Inside a sandbox the CLI fills it in.

  • It writes the source the sandbox runs to tools/<name>.ts. The copy imports only node:* modules, so it runs as it is. web_search, image_search, and scrape_webpage call the Kortix API with KORTIX_API_URL and KORTIX_TOKEN from context.env. memory and show work on files in the project checkout.
  • It sets <name>: tools/<name>.ts in the file that declares the entry, the root file or an imported file. The line ends with # ejected from kortix:<name>. Other lines and comments stay.
  • With no tools: key, it creates one and also lists the other four Kortix tools as kortix:<name>, so the eject removes nothing.
  • It refuses when tools/<name>.ts exists or when the tool already points at another module. --force overwrites.
  • It refuses a name that is not a Kortix tool, and a project without kortix.yaml at kortix_version 2 or 3.

Kortix does not update an ejected copy. To override without a copy, point the Kortix tool name at any module of yours:

tools:
  web_search: integrations/search/web_search.ts

The module replaces the Kortix tool on every harness. Keep the name, the arguments, and the output shape, so skills that name the tool and the web app’s view of it still work.

To return to the maintained tool, set the line back to <name>: kortix:<name> and delete the module.

Test a tool

A tool change reaches sessions when it reaches the default branch, usually through a change request. New sessions then load the change, and running sessions pick it up as their config converges. On pi, a change to a project tool or to the Kortix tool list restarts the runtime in place while no turn runs. The session keeps its messages. See How a change takes effect.

Validate

kortix validate checks names, paths, missing module files, and kortix: values. It warns when a tools: key lists no Kortix tool.

Check the list

kortix tools ls shows each tool a session gets and the Kortix tools the project removed.

Merge

Push a branch and open a change request on it: git push origin HEAD:add-weather-now, then kortix cr open --head add-weather-now --title "Add weather_now". Merge it with kortix cr merge <cr> or on the Review page.

Call it once

kortix sessions new --wait --prompt "Call the weather_now tool once with city Berlin and print its output."

Read the result

kortix sessions log <session_id> prints the session’s messages, with each tool call, its status, and its output. A tool that did not load is not offered to the model. A tool that throws answers with its error message.

Tools in harnesses/opencode/tools

OpenCode also loads tool files from the tools/ folder of its config directory (opencode.config_dir, harnesses/opencode in a new project). pi does not read that folder.

  • On OpenCode, a tool in that folder wins over a Kortix tool or project tool of the same name. Delete copies of Kortix tools from it, so the maintained tools run.
  • To run an OpenCode tool on both harnesses, port it to a project tool:
    1. Move the file out of the folder, for example to tools/<name>.ts.
    2. Replace tool({ description, args, execute }) with the plain default export object. Write args as JSON Schema under parameters.
    3. Read secrets from context.env.
    4. Declare it under tools: in kortix.yaml.

Was this page helpful?