Conjure.NativeSkill behaviour (Conjure v0.1.1-alpha)

View Source

Behaviour for native Elixir skill modules.

Native skills are Elixir modules that implement this behaviour, allowing them to be executed directly in the BEAM without external processes. This enables type-safe, in-process skill execution with full access to the application's runtime context.

Tool Mapping

Native skill callbacks map to Claude's tool types:

Claude ToolNative CallbackPurpose
bash_toolexecute/2Run commands/logic
viewread/3Read resources
create_filewrite/3Create resources
str_replacemodify/4Update resources

Implementing a Native Skill

defmodule MyApp.Skills.CacheManager do
  @behaviour Conjure.NativeSkill

  @impl true
  def __skill_info__ do
    %{
      name: "cache-manager",
      description: "Manage application cache",
      allowed_tools: [:execute, :read]
    }
  end

  @impl true
  def execute("clear", _context) do
    :ok = MyApp.Cache.clear()
    {:ok, "Cache cleared successfully"}
  end

  def execute("stats", _context) do
    stats = MyApp.Cache.stats()
    {:ok, format_stats(stats)}
  end

  @impl true
  def read("keys", _context, _opts) do
    keys = MyApp.Cache.keys()
    {:ok, Enum.join(keys, "\n")}
  end
end

Usage

session = Conjure.Session.new_native([MyApp.Skills.CacheManager])

{:ok, response, session} = Conjure.Session.chat(
  session,
  "Clear the cache and show me the stats",
  &api_callback/1
)

Tool Definitions

Native skills automatically generate Claude tool definitions based on __skill_info__/0. The backend translates tool calls to callback invocations.

Context

Each callback receives a Conjure.ExecutionContext that provides:

  • Working directory for file operations
  • Allowed paths for security boundaries
  • Timeout configuration
  • Custom executor config (can store app-specific data)

Optional Callbacks

Only __skill_info__/0 is required. Implement only the callbacks your skill needs:

  • execute/2 - For command/action execution
  • read/3 - For reading resources
  • write/3 - For creating resources
  • modify/4 - For modifying resources

See Also

Summary

Callbacks

Return skill metadata.

Execute a command or action.

Read a resource.

Write/create a resource.

Functions

Get skill info from a module.

Check if a module implements the NativeSkill behaviour.

Build Claude tool definitions from a native skill module.

Types

context()

@type context() :: Conjure.ExecutionContext.t()

result()

@type result() :: {:ok, String.t()} | {:error, term()}

skill_info()

@type skill_info() :: %{
  name: String.t(),
  description: String.t(),
  allowed_tools: [tool()]
}

tool()

@type tool() :: :execute | :read | :write | :modify

Callbacks

__skill_info__()

@callback __skill_info__() :: skill_info()

Return skill metadata.

This callback is required and provides information about the skill including its name, description, and which tools it implements.

Example

def __skill_info__ do
  %{
    name: "database-query",
    description: "Execute read-only database queries",
    allowed_tools: [:execute, :read]
  }
end

execute(command, context)

(optional)
@callback execute(command :: String.t(), context()) :: result()

Execute a command or action.

This is the primary action callback, replacing bash_tool. Use it for performing operations, running queries, triggering actions, etc.

Example

def execute("query " <> sql, _context) do
  case MyApp.Repo.query(sql) do
    {:ok, result} -> {:ok, format_result(result)}
    {:error, err} -> {:error, err}
  end
end

modify(path, old_content, new_content, context)

(optional)
@callback modify(
  path :: String.t(),
  old_content :: String.t(),
  new_content :: String.t(),
  context()
) :: result()

Modify an existing resource.

Replaces str_replace tool. Use for updating existing resources, patching data, etc.

Example

def modify(path, old_content, new_content, _context) do
  content = File.read!(path)
  updated = String.replace(content, old_content, new_content)
  File.write!(path, updated)
  {:ok, "Modified #{path}"}
end

read(path, context, opts)

(optional)
@callback read(path :: String.t(), context(), opts :: keyword()) :: result()

Read a resource.

Replaces the view tool. Use for reading files, fetching data, getting resource state, etc.

Options

  • :offset - Starting position (for pagination)
  • :limit - Maximum amount to return

Example

def read("schema/" <> table, _context, _opts) do
  schema = MyApp.Repo.get_schema(table)
  {:ok, format_schema(schema)}
end

write(path, content, context)

(optional)
@callback write(path :: String.t(), content :: String.t(), context()) :: result()

Write/create a resource.

Replaces create_file tool. Use for creating new resources, storing data, writing files, etc.

Example

def write(path, content, _context) do
  case File.write(path, content) do
    :ok -> {:ok, "Created #{path}"}
    {:error, reason} -> {:error, reason}
  end
end

Functions

get_info(module)

@spec get_info(module()) :: {:ok, skill_info()} | {:error, :not_a_skill}

Get skill info from a module.

Returns {:ok, info} if the module implements __skill_info__/0, or {:error, :not_a_skill} otherwise.

implements?(module)

@spec implements?(module()) :: boolean()

Check if a module implements the NativeSkill behaviour.

Example

Conjure.NativeSkill.implements?(MyApp.Skills.CacheManager)
# => true

Conjure.NativeSkill.implements?(String)
# => false

tool_definitions(module)

@spec tool_definitions(module()) :: [map()]

Build Claude tool definitions from a native skill module.

Returns a list of tool definitions that can be passed to the Claude API. Only includes tools listed in allowed_tools.

Example

Conjure.NativeSkill.tool_definitions(MyApp.Skills.CacheManager)
# => [
#   %{
#     "name" => "cache_manager_execute",
#     "description" => "Execute a command for cache-manager skill",
#     "input_schema" => %{...}
#   },
#   ...
# ]