How OpenMAIC Handles Capability Discovery for External Providers: Architecture and Code Walkthrough
OpenMAIC discovers available external providers by loading YAML configuration and environment variables at startup, resolving concrete capability objects only for properly configured and non-disabled services, and dynamically registering corresponding tools in the agent runtime.
The OpenMAIC repository implements a robust capability discovery system that determines which external services—such as LLMs, TTS, ASR, image generation, and web search—are operational at deployment time. This process ensures that the agent runtime exposes only functional tools by validating provider configurations against server-side credentials and operator-level disable flags.
Configuration Loading and Environment Resolution
OpenMAIC initiates capability discovery by ingesting provider settings from server-providers.yml and associated environment variables. The system maps specific environment variable prefixes to provider identifiers, enabling seamless integration of API keys and base URLs without code changes.
YAML Structure and Environment Mapping
The configuration loader in lib/server/provider-config.ts defines the ServerConfig type and environment mapping constants such as LLM_ENV_MAP and WEB_SEARCH_ENV_MAP. These maps translate environment variables like OPENAI_API_KEY into provider IDs (e.g., openai) and TAVILY_API_KEY into tavily. When the server starts, it parses the YAML file and overlays environment variables to construct a complete server-side configuration object.
// lib/server/provider-config.ts defines the mapping structure
const WEB_SEARCH_ENV_MAP = {
'TAVILY_API_KEY': 'tavily',
'SERPER_API_KEY': 'serper',
// Additional providers...
} as const;
The ServerConfig Type
The ServerConfig interface aggregates provider-specific blocks, including sections for web-search, tts, image, and video. Each block contains the provider ID, API key, optional base URL, and model lists. This centralized configuration serves as the source of truth for all subsequent capability resolution steps.
Capability Resolution and Validation
After loading configurations, OpenMAIC executes capability resolvers that validate whether a specific service is usable. These resolvers check for the presence of required credentials and respect global disable switches before instantiating capability objects.
The Web Search Resolver Pattern
The resolveWebSearchCapability function in lib/server/agent-runtime/web-search.ts exemplifies the resolution pattern. It inspects the loaded configuration, verifies that the provider is not force-disabled, and returns a capability object containing the validated API key and endpoint. If validation fails or the provider is disabled, the resolver returns null, signaling that the capability is unavailable.
import { resolveWebSearchCapability } from '@/lib/server/agent-runtime/web-search';
const capability = resolveWebSearchCapability();
if (capability) {
// Register the tool – the model can now call `web_search`
registerTool(buildWebSearchTool(capability));
}
Force-Off Controls for Operators
Operators can globally disable specific providers across all users regardless of individual settings. The DISABLE_ENV_MAPS in lib/server/provider-config.ts processes environment variables following the pattern <CAPABILITY>_<PREFIX>_ENABLED=false. For example, setting TTS_ELEVENLABS_ENABLED=false forces the TTS resolver to return null, preventing tool registration even if valid API keys exist in the configuration.
// lib/server/provider-config.ts (lines 35-38 concept)
const isForceDisabled = (providerId: string) => {
return process.env[`TTS_${providerId.toUpperCase()}_ENABLED`] === 'false';
};
Tool Registration in the Agent Runtime
The agent runtime, specifically in lib/server/agent-runtime/runner.ts, imports capability resolvers and conditionally registers tools based on their return values. A tool is added to the agent's toolset only when its corresponding resolver returns a non-null capability object. This gating mechanism guarantees that models cannot invoke unavailable services, as the tool simply does not exist in the runtime context when its capability is null.
// lib/server/agent-runtime/runner.ts
const search = resolveWebSearchCapability();
if (search) {
tools.web_search = buildWebSearchTool(search);
}
// If search is null, the web_search tool remains undefined
Server-Managed vs. Client-Provided Credentials
OpenMAIC distinguishes between server-managed providers and unmanaged providers to maintain security boundaries. When a provider is configured server-side (server-managed), the resolveApiKey function in lib/server/provider-config.ts returns only the server-side credential, ignoring any client-supplied key. For unmanaged providers lacking server configuration, the system accepts client-provided keys, enabling per-session overrides without exposing server credentials.
import { resolveApiKey } from '@/lib/server/provider-config';
// Server key takes precedence for managed providers
const apiKey = resolveApiKey('openai', clientSuppliedKey);
// Returns server env key if present, otherwise falls back to client key
This credential isolation is verified in the unit tests (provider-config.test.ts, lines 25-33), ensuring that server credentials never leak to client contexts while still allowing flexibility for user-provided configurations.
Summary
- Configuration Loading: OpenMAIC reads
server-providers.ymland environment variables via mappings inlib/server/provider-config.tsto build theServerConfigobject. - Capability Resolution: Resolvers like
resolveWebSearchCapabilityvalidate configurations and check force-disable flags, returningnullfor unavailable services. - Dynamic Tool Registration: The agent runtime in
runner.tsregisters tools only when resolvers return valid capability objects, ensuring the model sees only functional services. - Operator Controls: Environment variables following the
<CAPABILITY>_<PROVIDER>_ENABLED=falsepattern allow global disabling of specific providers. - Credential Precedence: Server-managed API keys always override client inputs, while unmanaged providers accept client keys for per-session flexibility.
Frequently Asked Questions
How does OpenMAIC determine if an external provider is available?
OpenMAIC determines availability through a three-stage process: first, it loads configuration from server-providers.yml and environment variables; second, capability resolvers validate credentials and check disable flags; finally, if a resolver returns a non-null capability object, the provider is considered available and its tool is registered in the runtime. Resolvers return null when configurations are missing or disabled.
Can operators disable specific providers across all users?
Yes. Operators can set environment variables using the pattern <CAPABILITY>_<PROVIDER>_ENABLED=false (e.g., WEB_SEARCH_TAVILY_ENABLED=false or TTS_ELEVENLABS_ENABLED=false). These flags are processed by the DISABLE_ENV_MAPS logic in lib/server/provider-config.ts, forcing resolvers to return null and preventing tool registration regardless of individual user settings or valid API keys.
What happens when a capability resolver returns null?
When a resolver returns null, the corresponding tool is not registered in the agent runtime. In lib/server/agent-runtime/runner.ts, the code checks the resolver output before calling registerTool(); if the capability is null, the tool property remains undefined. Consequently, the language model cannot generate function calls for that service because the tool does not exist in the available toolset.
How does OpenMAIC handle API key precedence between server and client?
The resolveApiKey function in lib/server/provider-config.ts implements a precedence hierarchy: for server-managed providers (those with credentials in server environment variables), the server key is authoritative and client-supplied keys are ignored. For unmanaged providers lacking server configuration, the system accepts client-provided keys. This ensures credentials remain server-side while allowing per-session overrides for unsupported providers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →