Auto-Discovery of Agents, Plugins, and Tools — FabrCore's Enhanced Registry
As FabrCore systems grow — more agents, more plugins, more standalone tools — keeping track of what is available and what each component does becomes a real challenge. The enhanced FabrCoreRegistry solves this with three metadata attributes, a collision detection system, and a Discovery API that exposes the full registry at runtime.
Rich Metadata with Three Attributes
FabrCore has always used [Description] from System.ComponentModel for short summaries. The enhanced registry adds three FabrCore-specific attributes that provide richer context about what a component does, how to use it, and whether it should be visible.
| Attribute | Targets | Multiplicity | Purpose |
|---|---|---|---|
[Description("...")] | Class or Method | One | Short summary (standard .NET attribute) |
[FabrCoreCapabilities("...")] | Class or Method | One | Detailed description of what the component provides |
[FabrCoreNote("...")] | Class or Method | Multiple | Usage instructions, prerequisites, or limitations |
[FabrCoreHidden] | Class or Method | One | Hides from discovery endpoint (still usable) |
[FabrCoreCapabilities] describes what the component can do in detail. Think of it as the extended description that an orchestrator agent reads when deciding whether to delegate work to a particular agent or use a particular plugin. [FabrCoreNote] is repeatable and captures prerequisites, warnings, and usage constraints. [FabrCoreHidden] keeps internal components out of the public registry while still allowing them to be used by agents that know their alias.
Here is how these attributes look on an agent:
[AgentAlias("job-agent")]
[Description("Manufacturing job management agent")]
[FabrCoreCapabilities("Manages manufacturing jobs — lookup, status tracking, priority changes, and ship date queries.")]
[FabrCoreNote("Requires a job number in the user's context before most tools will work.")]
[FabrCoreNote("Do not use for quoting or estimating — use the quotes-agent instead.")]
public class JobAgent : FabrCoreAgentProxy
{
// Constructor, OnInitialize, OnMessage...
}
And on a plugin with its tool methods:
[PluginAlias("weather")]
[Description("Real-time weather data plugin")]
[FabrCoreCapabilities("Current conditions, forecasts, and severe weather alerts for any location.")]
[FabrCoreNote("Requires weather:ApiKey in Args.")]
[FabrCoreNote("Rate limited to 60 requests per minute per agent.")]
public class WeatherPlugin : IFabrCorePlugin
{
[Description("Get current weather conditions for a location")]
public async Task<string> GetCurrentWeather(
[Description("City name or coordinates")] string location)
{ /* ... */ }
[Description("Get a 5-day weather forecast for a location")]
public async Task<string> GetForecast(
[Description("City name or coordinates")] string location)
{ /* ... */ }
}
Standalone tools support the same attributes on individual methods:
[ToolAlias("format-currency")]
[Description("Formats a decimal as USD currency")]
[FabrCoreCapabilities("Currency formatting for US locale.")]
[FabrCoreNote("Only supports USD — do not use for international currencies.")]
public static string FormatCurrency(
[Description("Amount to format")] decimal amount)
{
return amount.ToString("C");
}
Collision Detection for Duplicate Registrations
When multiple assemblies are scanned, it is possible for two types to register with the same alias. In earlier versions, this was a silent last-one-wins situation that caused unpredictable behavior. The enhanced registry now detects collisions at startup and reports them.
If two agent classes both use [AgentAlias("my-agent")], the registry logs a warning at startup and includes the collision in the Discovery API response:
{
"collisions": [
{
"alias": "my-agent",
"category": "agent",
"types": [
"ProjectA.Agents.MyAgent",
"ProjectB.Agents.MyAgent"
]
}
]
}
Each collision entry includes the alias, the category (agent, plugin, or tool), and all competing type names. The last type scanned wins the alias at runtime, but the collision is surfaced so developers can fix it before it causes routing issues.
This is especially important in larger systems where multiple teams contribute agent assemblies. Without collision detection, a duplicate alias could silently override an existing agent, routing messages to the wrong implementation.
The Discovery API Endpoint
All registry metadata is exposed at runtime through GET /fabrcoreapi/discovery. This endpoint returns every registered agent, plugin, and tool along with their full metadata — aliases, descriptions, capabilities, notes, and tool method signatures.
{
"agents": [
{
"typeName": "MyProject.Agents.JobAgent",
"aliases": ["job-agent"],
"description": "Manufacturing job management agent",
"capabilities": "Manages manufacturing jobs...",
"notes": [
"Requires a job number in context.",
"Do not use for quoting."
],
"methods": []
}
],
"plugins": [
{
"typeName": "MyProject.Plugins.WeatherPlugin",
"aliases": ["weather"],
"description": "Real-time weather data plugin",
"capabilities": "Current conditions, forecasts...",
"notes": ["Requires weather:ApiKey in Args."],
"methods": [
{ "name": "GetCurrentWeather", "description": "Get current weather conditions" },
{ "name": "GetForecast", "description": "Get a 5-day forecast" }
]
}
],
"tools": [
{
"typeName": "MyProject.Tools.UtilityTools.FormatCurrency",
"aliases": ["format-currency"],
"description": "Formats a decimal as USD currency",
"capabilities": "Currency formatting for US locale.",
"notes": ["Only supports USD."],
"methods": [
{ "name": "FormatCurrency", "description": "Formats a decimal as USD currency" }
]
}
]
}
Components decorated with [FabrCoreHidden] are excluded from this response. They remain fully functional — agents can use them, messages can route to them — but they do not appear in the discovery listing. This is useful for internal infrastructure agents that should not be visible to end users or orchestrator agents.
How Discovery Aids Agent Orchestration
The real power of rich registry metadata is in multi-agent orchestration. When a supervisor agent needs to decide which specialist to delegate work to, it can query the Discovery API and use capabilities and notes to make informed routing decisions.
Consider an orchestrator that receives a user request and needs to route it to the right agent. Instead of hardcoding agent aliases and hoping the comments stay up to date, the orchestrator can read the registry:
[AgentAlias("orchestrator")]
public class OrchestratorAgent : FabrCoreAgentProxy
{
public override async Task OnInitialize()
{
var tools = await ResolveConfiguredToolsAsync();
tools.Add(AIFunctionFactory.Create(DelegateToAgent));
var result = await CreateChatClientAgent(
config.Models ?? "default",
threadId: config.Handle ?? fabrcoreAgentHost.GetHandle(),
tools: tools);
_agent = result.Agent;
_session = result.Session;
}
[Description("Delegate a task to a specialist agent by handle")]
private async Task<string> DelegateToAgent(
[Description("Agent alias to delegate to")] string agentAlias,
[Description("The task to delegate")] string task)
{
var request = new AgentMessage
{
ToHandle = agentAlias,
Message = task
};
var reply = await fabrcoreAgentHost
.SendAndReceiveMessage(request);
return reply.Message ?? "(no response)";
}
}
The orchestrator's system prompt can include the discovery data, giving the LLM full visibility into available agents, their capabilities, and their constraints. The [FabrCoreNote] entries are particularly valuable here — they tell the LLM when not to use an agent, preventing misrouted requests before they happen.
This pattern also makes the system self-documenting. As teams add new agents and plugins with proper metadata, the orchestrator's routing knowledge grows automatically without any changes to the orchestrator itself. The registry becomes the single source of truth for what the system can do.
The combination of [FabrCoreCapabilities] for what a component does, [FabrCoreNote] for how and when to use it, [FabrCoreHidden] for controlling visibility, and collision detection for catching configuration errors gives FabrCore a discovery system that scales with the complexity of the agent network.
Built with FabrCore on .NET 10.
Builder of FabrCore and OpenCaddis.