Post

Guide: Setting up octopus-deploy-ai-steps in your Octopus instance

Guide: Setting up octopus-deploy-ai-steps in your Octopus instance

This post walks through setting up octopus-deploy-ai-steps — a reusable step template that drops Claude Code or GitHub Copilot CLI into any Octopus runbook or deployment process. By the end, you’ll have a working Octopus step that accepts a prompt, runs an AI agent against your environment, and publishes a markdown report as a deployment artifact.

This is the foundation PoC for the AI Pipelines proof-of-concepts. Later posts will pick up a specific scenario and walk through the implementation in detail.

What is octopus-deploy-ai-steps?

To test out the idea of headless agents in CD, I figured it would be cool to invoke an AI agent directly from an Octopus step. I wanted to see how the agent’s reasoning could be integrated into Octopus Deploy, and what the experience of using it would be like for an Octopus Deploy user. I built the octopus-deploy-ai-steps project in April 2026 to make it easy for anyone to do the same.

Fundamentally, it’s an Octopus Deploy step template that you can add to your Octopus instance, which gives you a new step type that runs an AI agent based on your prompt and configuration. It can be used across the workflow primitives that Octopus Deploy offers - runbooks, deployment processes, process templates, and project templates. You add it like any other step to your workflow, it expects some inputs which you can supply either directly or route it through a variable.

I highly recommend reviewing these docs before you get started:

Prerequisites

  • Octopus Deploy 2024.1 or later
  • An Anthropic API key (for Claude Code) or a GitHub token with GitHub Copilot scope (for GitHub Copilot CLI)
  • A Linux worker with Docker for container mode (recommended), or any worker with internet access for local mode
  • kubectl configured on the worker if you intend to use the agent for Kubernetes tasks
  • Familiarity with Octopus step templates and variable sets

How does it work?

Before configuring anything, here’s a quick primer on how this works if you haven’t read through the links in the prerequisites section. The step template is a shell script (Bash for Linux, PowerShell for Windows) that you point Octopus at via a Git source. When the step runs, it:

  1. Reads routing flags to determine which agent and execution mode to use
  2. Optionally installs dependencies (Node.js, the CLI tool) if running in local mode
  3. Writes an MCP configuration file if you’ve provided one
  4. Resolves the prompt — from inline text, a local file, or a GitHub URL
  5. Runs the AI CLI non-interactively with the prompt and any tool configuration
  6. Captures stdout and stderr to the working directory
  7. Publishes the output as an artifact attached to the Octopus deployment task
  8. Optionally cleans up the working directory

The routing is handled by two entry point scripts:

  • scripts/octo-ai.sh — Linux and macOS workers
  • scripts/octo-ai.ps1 — Windows workers

Three flags drive the behaviour:

FlagOptionsNotes
--ai-agentclaude or copilotWhich CLI to run
--oslinux or windowsDetermines which scripts are sourced
--modecontainer or localContainer pulls a pre-built image; local installs directly on the worker
flowchart TD
    Caller["Octopus Step\n--ai-agent --os --mode\n--prompt-content --mcp-configuration\n--allowed-tools --max-turns"]

    Entry["octo-ai.sh / octo-ai.ps1\nreads routing flags only\npasses all args verbatim"]

    InstallDeps["install-dependencies.sh / .ps1\n(local mode only)"]

    AgentScripts["claude-linux.sh · claude-windows.ps1\ncopilot-linux.sh · copilot-windows.ps1\nsources lib/common.*"]

    ResolvePrompt["resolve_prompt"]
    WriteMCP["write_mcp_config"]
    InstallNode["install_node + install_npm_tool\n(local mode only)"]

    AICLI["AI CLI\nclaude -p ...  /  copilot --prompt ..."]

    Output["stdout → output.json\nstderr → agent-stderr.log"]

    Artifacts["publish_all_artifacts\nPOST /api → Octopus artifact API"]

    Cleanup["cleanup_workdir\n(if --cleanup=true)"]

    Caller --> Entry
    Entry -->|"local + install-deps=true"| InstallDeps
    InstallDeps --> AgentScripts
    Entry --> AgentScripts
    AgentScripts --> ResolvePrompt & WriteMCP & InstallNode
    ResolvePrompt & WriteMCP & InstallNode --> AICLI
    AICLI --> Output
    Output --> Artifacts
    Artifacts --> Cleanup

Phase 1: Register the step template in Octopus

Octopus supports Git-sourced step templates, which means you can point directly at the repository rather than copying scripts into the library.

  1. In Octopus, go to Library → Step Templates → Add Step Template.
  2. Choose Run a Script as the base template type.
  3. Under Script Source, select Git repository.
  4. Under Authentication, you can choose to use Anonymous as the repository is public. Alternatively, if you have GitHub Connections already established in Octopus, you may use the appropriate option. In this guide, I use a “GitHub Connection” for auth.
  5. Set the repository URL to https://github.com/rohitnb/octopus-deploy-ai-steps.
  6. Set the branch to main.
  7. Set the script file to scripts/octo-ai.sh for Linux workers, or scripts/octo-ai.ps1 for Windows.

Configure Git repository source in Octopus step template

Set script path for the step template


Phase 2: Configure script parameters

The step template exposes a set of parameters that control everything from agent selection to cleanup behaviour.

  1. Under script parameters, construct the argument string using Octopus variable substitution syntax (#{…}) so each flag maps to a declared template parameter and avoids hardcoding values.

    1
    
    --ai-agent="#{OctoAI.AiAgent}" --os="#{OctoAI.Os}" --ai-agent-api-key="#{OctoAI.AiAgentApiKey}" --prompt-content="#{OctoAI.PromptContent}" --octopus-api-key="#{OctoAI.OctopusApiKey}" --octopus-server-url="#{OctoAI.OctopusServerUrl}" --octopus-space-name="#{OctoAI.OctopusSpaceName}" --gh-pat="#{OctoAI.GhPat}" --mcp-configuration='#{OctoAI.McpConfiguration}' --allowed-tools="#{OctoAI.AllowedTools}" --max-turns="#{OctoAI.MaxTurns}" --mode="#{OctoAI.Mode}" --install-dependencies="#{OctoAI.InstallDependencies}" --cleanup="#{OctoAI.Cleanup}" --dry-run="#{OctoAI.DryRun}" --octopus-task-id="#{OctoAI.OctopusTaskId}"
    

    Set script parameters for the step template

  2. Each variable within #{..} corresponds to a parameter that will be exposed to the consumers of the step template. I recommend exposing every parameter. Switch to the “Parameters” tab within Octopus and add the parameters as defined below. Feel free to adjust the names and descriptions to fit your team’s conventions and to provide clarity on what each parameter does.

    All parameters:

    Parameter/Variable nameLabelControl typeDefault valueNotes
    OctoAI.AiAgentAI AgentDrop-down listclaudeOptions: claude, copilot
    OctoAI.OsWorker OSDrop-down listlinuxOptions: linux, windows
    OctoAI.ModeModeDrop-down listcontainerOptions: container, local. See Execution modes.
    OctoAI.AiAgentApiKeyAI Agent API KeySensitive text box(empty)Anthropic API key or GitHub token. Bind to a sensitive variable.
    OctoAI.OctopusApiKeyOctopus API KeySensitive text box(empty)Octopus API key used to publish artifacts. Bind to a sensitive variable.
    OctoAI.OctopusServerUrlOctopus Server URLSingle line Text box(empty)Base URL, e.g. https://octopus.example.com
    OctoAI.OctopusSpaceNameOctopus Space NameText boxDefaultName of the space, not the ID.
    OctoAI.PromptContentPrompt ContentMulti line text box(empty)Inline text, local file path, or GitHub file URL.
    OctoAI.InstallDependenciesInstall DependenciesDrop DownfalseSet to true for local mode on a clean machine.
    OctoAI.GhPatGitHub PATSensitive text box(empty)Required for private GitHub prompts or the GitHub MCP server.
    OctoAI.McpConfigurationMCP ConfigurationText area{"mcpServers":{}}JSON MCP server config. Use $OCTOPUS_SERVER_URL, $OCTOPUS_API_KEY, $OCTOPUS_SPACE, and $GH_PAT as placeholders within the JSON — they are substituted at runtime.
    OctoAI.AllowedToolsAllowed ToolsText box(empty)Comma-separated tool list. Leave empty to use the agent default.
    OctoAI.MaxTurnsMax TurnsText box15Maximum agentic loop iterations.
    OctoAI.CleanupCleanup Working DirectoryDrop DownfalseDeletes the temporary working directory after the run. Set to false when debugging.
    OctoAI.DryRunDry RunDrop DownfalseSkips Octopus REST API calls and emits service messages instead. Useful for testing without publishing artifacts.
    OctoAI.OctopusTaskIdOctopus Task IDText box#{Octopus.Task.Id}Links published artifacts to the current deployment task. The built-in #{Octopus.Task.Id} variable is the correct default.

    Define parameters for the step template

  3. Under the Settings tab, set a name, logo, and description for the step template. This will help users identify it when adding it to their runbooks and deployment processes.

  4. Save the step template. It should now be available in the library for use in any project.


Phase 3: Store secrets in a variable set

A Variable Set lets you centralise all the configuration that is shared across multiple projects or deployments. You bind the variable set’s variables to the step template parameters instead of typing values into each project individually or needing to modify the process definition.

You don’t need to create variables for every parameter — only the ones that should be shared or that contain sensitive values. For example, you might choose to set OctoAI.Mode and OctoAI.MaxTurns directly in the step parameters since they are unlikely to change between runs, while storing API keys and server URLs in variables.

For simplicity, I recommend matching the variables with the parameters you defined in the step template, but you can choose any naming convention that works for your team. Just make sure to bind them correctly when configuring the step later.

  1. Go to Library → Variable Sets → Add Variable Set. Name it something like AI Pipeline Credentials.
  2. Add variables for your Anthropic API key, Octopus API key, and server URL.
  3. Mark all key and token variables as Sensitive — Octopus will mask them in task logs.
  4. In each project that uses the AI step, include this variable set under Variables → Library Variable Sets.

Connection Variables

Variable nameExample valueSensitive
OctoAI.AiAgentclaudeNo
OctoAI.AiAgentApiKeysk-ant-...Yes
OctoAI.OctopusApiKeyAPI-...Yes
OctoAI.OctopusServerUrlhttps://octopus.example.comNo
OctoAI.OctopusSpaceNameDefaultNo
OctoAI.GhPatgithub_pat_...Yes

Execution Variables

Variable nameExample valueSensitive
OctoAI.ModecontainerNo
OctoAI.OslinuxNo
OctoAI.MaxTurns15No
OctoAI.CleanupfalseNo
OctoAI.DryRunfalseNo
OctoAI.AllowedTools(empty — use agent default)No

Note: OctoAI.OctopusTaskId does not need a variable in the set — the parameter default (#{Octopus.Task.Id}) is resolved automatically by Octopus at run time.

MCP Server Configuration

Store your MCP server JSON in a single variable. Use the placeholder tokens ($OCTOPUS_SERVER_URL, etc.) rather than hardcoding values — the scripts substitute them at runtime.

Variable nameValue
OctoAI.McpConfiguration{"mcpServers":{"octopus":{"command":"npx","args":["-y","@octopusdeploy/mcp-server"],"env":{"OCTOPUS_URL":"$OCTOPUS_SERVER_URL","OCTOPUS_API_KEY":"$OCTOPUS_API_KEY","OCTOPUS_SPACE":"$OCTOPUS_SPACE"}}}}

Variable Set


Execution modes

Container mode (recommended for Linux workers with Docker) pulls a pre-built image from GHCR that includes Claude Code, Node.js, and kubectl.

Set --mode=container and leave --install-dependencies unset.

Local mode runs the scripts directly on the host. Useful when Docker isn’t available — a bare VM, a deployment target you don’t control fully, or a Windows worker. On first run, set --install-dependencies=true and the script will install Node.js and the CLI tool. Subsequent runs can set --install-dependencies=false to skip the installation step and save time.


Configure MCP servers (optional)

The step accepts MCP configuration as a JSON string. Here’s a sample:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "mcpServers": {
    "github": {
      "type": "http",
      "url": "https://api.githubcopilot.com/mcp",
      "headers": {
        "Authorization": "Bearer $GH_PAT"
      }
    },
    "octopus": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@octopusdeploy/mcp-server"],
      "env": {
        "OCTOPUS_SERVER_URL": "$OCTOPUS_SERVER_URL",
        "OCTOPUS_API_KEY": "$OCTOPUS_API_KEY",
        "OCTOPUS_SPACE": "$OCTOPUS_SPACE"
      }
    }
  }
}

The step substitutes any ${VARIABLE} placeholders in the JSON with values from the Octopus environment before writing the config file. Store the raw JSON in an Octopus variable and bind it to the MCP Configuration parameter.


Restrict tool access with an allowlist

By default, the agent can call any tool available to it. For runbooks where you want the agent to read and reason but not take write actions, pass a comma-separated allowlist to --allowed-tools.

For a read-only Kubernetes inspection runbook:

1
kubectl,octopus_api_get

For a runbook that should only read logs:

1
kubectl_logs,kubectl_describe

The allowlist is enforced by the CLI tool itself — the agent will not attempt calls outside it. This is the primary safety mechanism for limiting agent scope in production runbooks.


Known limitations

Prompt quality is everything. The step template is a delivery mechanism — it gets the agent to the environment with the right credentials and tools. What the agent actually does depends entirely on the prompt. Vague prompts produce vague reports. The investment in prompt design pays off in report quality, and it’s worth treating prompts as versioned artifacts just like the scripts themselves. A GitHub URL prompt source makes this easy.

Container mode image updates are manual. The pre-built image on GHCR is pinned by the step configuration, not auto-updated. When Claude Code or Copilot CLI release significant updates, you’ll need to pull a newer image tag and update the step configuration. There’s no automatic signal that this is worth doing — it’s a gap worth closing with a periodic review process.

MCP server stability varies. Some MCP servers are well-maintained; others are community projects that may lag behind API changes in the underlying service. If an MCP server is a critical part of your runbook, test it separately from the AI step and treat it as a dependency with its own reliability profile.

Local mode creates drift risk. If you’re running multiple workers in local mode, CLI versions may diverge across workers over time unless you have a process for keeping them in sync. Container mode sidesteps this entirely.

Artifact size isn’t bounded. A very long-running agent on a complex diagnostic task can produce a large report. Octopus doesn’t enforce a size limit on artifacts, but large artifacts slow down the task summary UI. Setting --max-turns caps the agent’s reasoning depth and keeps reports to a manageable size.

This post is licensed under CC BY 4.0 by the author.