Buffaly Logo
How-To / Extensions

Add a C# DLL Tool

Expose high-trust compiled C# assemblies, native .NET APIs, and third-party SDKs directly to the Buffaly agent runtime. Learn how to write structured tools, configure imports, and project them as discoverable agent actions.

Choosing the right capability boundary

While many agent workflows can be expressed as prompt guidance, deterministic logic (like system commands, database reads, file compression, and API calls) should be compiled in C#. Use this guide to choose the optimal abstraction:

Scenario Recommended Choice Why
Stateless, deterministic process logic C# DLL Tool Compiled C# runs fast, has strict timeouts, and guarantees type safety.
Stable review, drafting, or checklist rules Prompt Skill Easier to iterate on using conversational guidance than hard code.
Custom script that combines multiple tools ProtoScript Action Organizes high-level agent logic and loops within the active project.
System with configuration, lifecycle, or state Service Boundary The service manages active connections and configurations.

Repository tool architecture

In the Buffaly codebase, tools are organized into specific, testable .NET projects under the naming scheme Buffaly.Agent.Tools.*. These projects target .NET 9 and compile into independent DLL assemblies:

FileSystem Tools Compiled

Static file operations, Ripgrep integrations, and range reading. Assembly: Buffaly.Agent.Tools.FileSystem.dll.

System Operations Compiled

Low-overhead command-line and fast PowerShell 7 process hosting. Assembly: Buffaly.Agent.Tools.Process.dll.

Secrets Vault Compiled

Resolves offline database passwords and API tokens securely. Assembly: Buffaly.Agent.Tools.Secrets.dll.

Relational DB Compiled

Performs database schemas audits and query testing under SQL Server. Assembly: Buffaly.Agent.Tools.SqlServer.dll.

Expected C# class structure

Keep your C# class helper static, highly focused, and type-safe. Avoid silent exceptions and return clear diagnostic strings:

MyDomainTools.cs C#
namespace Buffaly.Agent.Tools.MyDomain;

public static class MyDomainTools
{
	public static string DescribeConfiguredThing(string name)
	{
		if (string.IsNullOrWhiteSpace(name))
		{
			return "Error: name parameter is required.";
		}

		// Execute deterministic C# SDK, command, or filesystem logic
		return "Configured thing: " + name.Trim();
	}
}

Projecting C# into ProtoScript actions

To let the agent semantically discover and invoke your compiled helper method, bind the binary to your ProtoScript project:

Step A: Add assemblies to imports

Add reference and import keys inside content/projects/OpsAgent/Imports.pts to load the class structure:

reference "lib/Buffaly.Agent.Tools.MyDomain.dll" Buffaly.Agent.Tools.MyDomain;
import Buffaly.Agent.Tools.MyDomain Buffaly.Agent.Tools.MyDomain.MyDomainTools MyDomainTools;
Step B: Implement a ProtoScript action wrapper

Create a wrapped skill action. Use semantic InfinitivePhrase decorators so the agent can discover it by intent:

[SemanticProgram.InfinitivePhrase("to describe a configured thing")]
prototype ToDescribeConfiguredThing extends MyDomainSkillAction
{
	Description = @"Describe one configured thing by name using the MyDomain C# tool.";

	function Execute(string name) : String
	{
		return MyDomainTools.DescribeConfiguredThing(name);
	}
}
Important: a public C# method by itself is not a discoverable user-facing tool. Ordinary tools are exposed through referenced assemblies, ProtoScript imports, action wrappers, semantic phrases, and the tool registrar. Do not assume dropping a DLL into a folder automatically creates an agent action.

The easiest way: ask Buffaly to create it

You do not need to perform every edit manually. Ask Buffaly to inspect existing tool projects first, create the smallest matching compiled helper, wrap it with a ProtoScript action, validate discovery, verify encoding-safe examples, and commit only intentional files.

New deterministic validator"Add a C# DLL-backed tool for validating internal ticket IDs. Inspect existing Buffaly.Agent.Tools.* projects first, wrap it with a ProtoScript action, build, validate discovery, and commit only intentional files."
Extend an existing helper"Extend the filesystem tool with a safe helper for checking whether a documentation article path is under the wiki root. Follow local patterns, update the ProtoScript wrapper, validate, and report the commit hash."
Credential-safe SDK boundary"Create a compiled helper for calling this SDK. Do not put credentials in code or prompt files. Use user secrets or configuration for secrets, expose only one user-facing action first, and document how to verify it."

Recommended implementation workflow

Follow this process to add or extend a compiled capability in your local workspace:

1. Compile DLL

Write your C# static tool, target .NET 9 in your csproj, compile it, and place the output DLL in the project's library folder.

2. Bind Wrapper

Add reference and import keys under Imports.pts, and implement a prototype action with a semantic phrase.

3. Test Action

Compile the ProtoScript project, reload the runtime environment, and ask the agent to semantically discover your action.

Operational validation checklist

Before finalizing your custom tool development, confirm that all layers are properly integrated and healthy:

Assembly Build: The target class compiles with 0 errors and exists under content/projects/OpsAgent/lib/.
ProtoScript Reference: The assembly's imports, methods, and classes parse cleanly without compiler exceptions.
Dynamic Discovery: The agent can semantically discover the action by prompt intent (e.g., "to describe a configured thing").
Runtime Load: Loading the action makes a callable tool available in the session, and a safe test call returns the expected result or a clear explicit error.
Secret Safety: No credentials are committed to source, docs, prompt files, ProtoScript literals, or tests.
Clean Commits: Staged files contain only the C# tools, the ProtoScript wrapper, and test code - excluding temporary bin/obj folders or configuration zips.

Troubleshooting

Buffaly cannot find the new actionCheck that the ProtoScript action exists, compiles, and has strong infinitive phrases. The registrar builds tool schema from the action prototype and Execute(...) method, not from a raw C# method alone.
The action loads but failsConfirm the DLL was built and copied to the exact path used by the reference line, and check for missing dependent DLLs.
The method needs credentialsUse the configured secrets path and existing secret-aware patterns. Do not store secrets in source, Markdown, prompt files, ProtoScript literals, or tests.
The capability has stateUse a service boundary when the capability owns identity, lifecycle, state, configuration, or multiple related methods.
The runtime is stalePrefer typed ProtoScript authoring tools when available. After changing imports or action wrappers, compile or validate the ProtoScript project, then reload the action or reset the runtime before testing discovery again.

Next Up