Octopus
Tool Registration
Tools are registered with the MCPToolRegistry during agent initialisation. Tools can be registered globally (available to all agents), per-agent (specific agents only), or dynamically at runtime. This page covers all three registration patterns.
MCPTool Class
public class MCPTool
{
public ToolDefinition Schema { get; init; } = new();
// Handler receives raw JSON input from the LLM + execution context
public Func<JsonElement, ConversationContext, CancellationToken, Task<string>>
Handler { get; init; } = (_, _, _) => Task.FromResult("{}");
}
public class ConversationContext
{
public Guid AgentId { get; init; }
public Guid ConversationId { get; init; }
public string UserId { get; init; } = string.Empty;
public Guid TenantId { get; init; }
public IServiceProvider Services { get; init; } = default!;
}
Pattern 1: Plugin-Level Registration (Global)
Tools registered in a plugin's OnRegister are available to all agents that have that plugin active:
public class HRToolsPlugin : IOctopusPlugin
{
public void OnRegister(IServiceCollection services, OctopusConfig config)
{
services.AddSingleton<ILeaveService, LeaveService>();
}
public Task OnStartAsync(IServiceProvider sp, CancellationToken ct)
{
var registry = sp.GetRequiredService<MCPToolRegistry>();
var leave = sp.GetRequiredService<ILeaveService>();
registry.Register(new MCPTool
{
Schema = new ToolDefinition
{
Name = "get_leave_balance",
Description = "Returns remaining annual leave days for an employee.",
InputSchema = JsonDocument.Parse("""
{
"type": "object",
"properties": {
"employeeId": { "type": "string", "description": "Employee ID" }
},
"required": ["employeeId"]
}
""")
},
Handler = async (input, ctx, ct) =>
{
string empId = input.GetProperty("employeeId").GetString()!;
int days = await leave.GetBalanceAsync(empId, ctx.TenantId, ct);
return JsonSerializer.Serialize(new { employeeId = empId, remainingDays = days });
}
});
return Task.CompletedTask;
}
}
Pattern 2: Per-Agent Registration via Admin UI
In the agents-app, administrators assign tool groups to specific agents. Tool group assignments are stored in Octopus_AgentToolGroups and resolved at agent load time:
// Agent tool group assignment (stored in SQL)
POST /api/octopus/agents/{agentId}/tool-groups
{
"toolGroupId": "tg_hr_leave",
"enabled": true
}
// At runtime, AgentComposite.ToolRegistry contains only the tools
// assigned to that agent — not all globally registered tools.
Pattern 3: Dynamic Registration
Tools can be registered or unregistered while the application is running. This supports scenarios like feature flags or per-tenant tool availability:
// Register a tool at runtime (e.g. when a feature is enabled)
public class FeatureFlagToolRegistrar
{
private readonly MCPToolRegistry _registry;
private readonly IFeatureFlags _flags;
public async Task SyncToolsForAgentAsync(Guid agentId, CancellationToken ct)
{
if (await _flags.IsEnabledAsync("BetaExpenseReports", agentId, ct))
{
_registry.RegisterForAgent(agentId, new MCPTool
{
Schema = ExpenseReportToolDefinition,
Handler = ExpenseReportHandler
});
}
else
{
_registry.UnregisterForAgent(agentId, "submit_expense_report");
}
}
}
Registration Scope Summary
| Scope | Who Sees the Tool | When to Use |
|---|---|---|
| Global (plugin-level) | All agents with that plugin | Core tools used by most agents |
| Per-agent (admin UI) | Only the configured agent | Specialist tools for specific agent roles |
| Dynamic (runtime) | Controlled at runtime per agent | Feature flags, per-tenant capabilities, A/B testing |