Skip to main content
The dotbot MCP server auto-discovers tools at startup by scanning the systems/mcp/tools/ directory for subdirectories that contain both a metadata.yaml and a script.ps1. There is no registration step — dropping the right files into the right folder is all that is needed. This same discovery mechanism applies to tools added by workflows and stacks installed in your project.

How tool discovery works

When the MCP server starts, it walks every subdirectory under systems/mcp/tools/ (and under .bot/workflows/*/tools/ for workflow and stack tools). For each subdirectory that contains both metadata.yaml and script.ps1, the server:
  1. Reads the tool name and input schema from metadata.yaml
  2. Dot-sources script.ps1 to load the Invoke-* function into memory
  3. Registers the tool with the MCP protocol handler
No changes to the server itself are needed. Restart the MCP server process (by restarting your AI tool) to pick up new or modified tools.

Directory structure

Each tool lives in its own folder under systems/mcp/tools/. Use kebab-case for the folder name:
systems/mcp/tools/
└── your-tool-name/
    ├── metadata.yaml   # Tool name, description, and input schema
    ├── script.ps1      # PowerShell implementation function
    └── test.ps1        # Test file (optional but recommended)
For workflow or stack-scoped tools, use the same three-file structure under:
.bot/workflows/<workflow-name>/tools/<tool-name>/
The server discovers tools from both locations automatically.

Naming conventions

The three files follow different case conventions that must be consistent with each other:
LocationConventionExample
Folder namekebab-casesend-notification
name field in metadata.yamlsnake_casesend_notification
Function name in script.ps1Invoke- + PascalCaseInvoke-SendNotification
The server derives the function name to call from the name field in metadata.yaml by capitalizing each underscore-separated segment and prepending Invoke-. If the naming is inconsistent, the tool will load but fail at call time with a function-not-found error.

The required files

metadata.yaml

Defines the tool’s name, description, and JSON Schema for its input parameters. The following is the real metadata.yaml for the built-in task_create tool, which you can use as a reference for field structure:
name: task_create
description: Create a new task in the todo folder with structured metadata
inputSchema:
  type: object
  additionalProperties: true
  properties:
    name:
      type: string
      description: Brief, descriptive name for the task
    description:
      type: string
      description: Detailed description of what the task does and why it's needed
    category:
      type: string
      description: Task category (validated at runtime via settings.default.json)
    priority:
      type: integer
      description: Priority from 1 (highest) to 100 (lowest), default is 50
      minimum: 1
      maximum: 100
    effort:
      type: string
      description: Effort estimate (XS=1 day, S=2-3 days, M=1 week, L=2 weeks, XL=3+ weeks)
      enum: [XS, S, M, L, XL]
    dependencies:
      type: array
      description: List of task IDs that must be completed before this one
      items:
        type: string
    acceptance_criteria:
      type: array
      description: List of criteria that must be met for the task to be considered complete
      items:
        type: string
  required: [name, description]
Mark all parameters the AI must supply in the required array. Parameters not in required are treated as optional.

script.ps1

Contains a single PowerShell function named Invoke-YourToolName. The function receives a [hashtable]$Arguments parameter and returns a hashtable with the result data. The MCP server serializes the returned hashtable to JSON before sending it back to the AI tool.
function Invoke-YourToolName {
    param(
        [hashtable]$Arguments
    )

    # Extract arguments from the hashtable
    $input1 = $Arguments['input1']
    $input2 = $Arguments['input2']

    # Access the project root (set by the MCP server at startup)
    $tasksDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks"

    # Return a hashtable — the server serializes it to JSON
    return @{
        result = "processed $input1"
        count  = $input2
    }
}
The MCP server sets $global:DotbotProjectRoot before loading tool scripts. Use this variable to build paths to .bot/workspace/, .bot/.control/, or any other project-relative location — do not hardcode paths.

test.ps1

Tests the tool by sending a JSON-RPC request to a running MCP server process. Including a test file is optional but recommended, especially for tools that modify project state.
#!/usr/bin/env pwsh
param(
    [Parameter(Mandatory)]
    [System.Diagnostics.Process]$Process
)

. "$PSScriptRoot\..\..\dotbot-mcp-helpers.ps1"

Write-Host "Test: your tool name" -ForegroundColor Yellow

$response = Send-McpRequest -Process $Process -Request @{
    jsonrpc = '2.0'
    id      = 1
    method  = 'tools/call'
    params  = @{
        name      = 'your_tool_name'
        arguments = @{
            input1 = 'test value'
            input2 = 42
        }
    }
}

$result = $response.result.content[0].text | ConvertFrom-Json
Write-Host "Result: $($result.result)" -ForegroundColor Green

Creating a custom tool

The following example creates a tool called project_summary that returns a count of tasks in each lifecycle state.
1

Create the tool folder

Inside systems/mcp/tools/, create a new directory using kebab-case:
New-Item -ItemType Directory systems/mcp/tools/project-summary
2

Write metadata.yaml

Create systems/mcp/tools/project-summary/metadata.yaml with the snake_case tool name, a description, and the input schema. For a tool with no required inputs, use an empty properties object:
name: project_summary
description: Return a count of tasks in each lifecycle state
inputSchema:
  type: object
  additionalProperties: false
  properties: {}
  required: []
3

Write script.ps1

Create systems/mcp/tools/project-summary/script.ps1 with the Invoke-ProjectSummary function:
function Invoke-ProjectSummary {
    param(
        [hashtable]$Arguments
    )

    $tasksDir = Join-Path $global:DotbotProjectRoot ".bot\workspace\tasks"
    $counts = @{ todo = 0; analysing = 0; analysed = 0; 'in-progress' = 0; done = 0 }

    Get-ChildItem -Path $tasksDir -Filter "*.json" | ForEach-Object {
        $task = Get-Content $_.FullName | ConvertFrom-Json
        if ($counts.ContainsKey($task.status)) {
            $counts[$task.status]++
        }
    }

    return $counts
}
4

Restart the MCP server

The server loads tools at startup. Restart your AI tool (or the MCP server process) to pick up the new tool. Ask your AI tool to list available dotbot tools — project_summary should appear in the list.

Tool name collision warning

Tool name collisions are possible if two installed workflows or stacks define a tool with the same name field in their metadata.yaml. When a collision occurs, the last tool loaded wins, which may produce unexpected behavior. Use unique, workflow-prefixed names (for example, jira_repo_clone rather than repo_clone) to avoid conflicts.