How to Configure and Add Support for Custom AI Provider Endpoints in Secure-Design
Developers can configure and add support for custom AI provider endpoints by implementing the AIProvider abstract class, registering the instance with the IProviderRegistry in the DI container, and exposing a VS Code configuration command that stores credentials in secure storage.
Secure-Design provides an extensible provider registry that abstracts AI services behind a unified strategy-pattern interface. This architecture allows developers to configure and add support for custom AI provider endpoints—whether self-hosted LLMs, private OpenAI-compatible APIs, or any HTTP-based model—without modifying core chat or canvas logic.
Step 1: Define a Provider Class
Create a new file in src/providers/<your-provider>.ts that extends the AIProvider base class defined in src/providers/types.ts. You must implement three core methods and define a static metadata object that describes the provider to the UI.
The metadata object specifies the provider ID, display name, configuration command ID, and required configuration keys:
static readonly metadata: ProviderMetadata = {
id: 'mycustom',
name: 'My Custom LLM',
configureCommand: 'secureDesign.configureMyCustom',
additionalConfigKeys: ['mycustom.baseUrl', 'mycustom.apiKey'],
description: 'Connects to a self-hosted LLM that follows the OpenAI-compatible REST spec.',
documentationUrl: 'https://example.com/docs/mycustom',
};
Implement the models property to expose available model IDs:
readonly models: ModelConfig[] = [
{ id: 'gpt-4-custom', displayName: 'GPT-4 Custom', isDefault: true },
{ id: 'gpt-3.5-custom', displayName: 'GPT-3.5 Custom' },
];
The createInstance() method constructs the actual SDK client. Retrieve configuration values from VS Code settings and return a LanguageModelV2 instance:
createInstance(params: ProviderInstanceParams): LanguageModelV2 {
const { model, config } = params;
const baseUrl = config.config.get<string>('mycustom.baseUrl')!;
const apiKey = config.config.get<string>('mycustom.apiKey')!;
return getSdkLanguageModel({
modelId: model,
provider: {
request: async (url, init) => {
const response = await fetch(`${baseUrl}/${url}`, {
...init,
headers: { ...init?.headers, Authorization: `Bearer ${apiKey}` },
});
return response.json();
},
},
});
}
Finally, implement validateCredentials() to ensure required fields are present before the provider appears active:
validateCredentials(config: VsCodeConfiguration) {
const baseUrl = config.config.get<string>('mycustom.baseUrl');
const apiKey = config.config.get<string>('mycustom.apiKey');
const missing = [];
if (!baseUrl) missing.push('baseUrl');
if (!apiKey) missing.push('apiKey');
return {
isValid: missing.length === 0,
error: missing.length ? `Missing ${missing.join(', ')}` : undefined,
};
}
Step 2: Register the Provider
In src/di/ServiceContainer.ts, import your provider class and register it with the IProviderRegistry singleton during initialization:
// src/di/ServiceContainer.ts
import { MyCustomProvider } from '../providers/myCustomProvider';
import type { IProviderRegistry } from '../providers/types';
// Inside initialize()
const providerRegistry: IProviderRegistry = {
_providers: new Map(),
register(p) {
this._providers.set((p.constructor as typeof AIProvider).metadata.id, p);
},
getProvider(id) { return this._providers.get(id); },
getAllProviders() { return Array.from(this._providers.values()); },
getAllModels() {
const models: ModelConfigWithProvider[] = [];
this.getAllProviders().forEach(p => {
p.models.forEach(m => models.push({
model: m,
provider: (p.constructor as typeof AIProvider).metadata
}));
});
return models;
},
};
providerRegistry.register(new MyCustomProvider());
this.services.set('providerRegistry', providerRegistry);
Once registered, downstream services like CustomAgentService (defined in src/services/customAgentService.ts) automatically discover your provider when resolving models for chat or design generation tasks.
Step 3: Expose Configuration UI
Add a VS Code command to collect endpoint credentials and store them securely.
First, declare the command in package.json:
{
"contributes": {
"commands": [
{
"command": "secureDesign.configureMyCustom",
"title": "Configure My Custom LLM"
}
]
}
}
Then implement the command handler, using SecureStorageService from src/services/secureStorageService.ts to encrypt sensitive data:
// src/commands/configureMyCustom.ts
import * as vscode from 'vscode';
import { SecureStorageService } from '../services/secureStorageService';
export function registerMyCustomCommand(context: vscode.ExtensionContext) {
const storage = new SecureStorageService(context.secrets);
const cmd = vscode.commands.registerCommand('secureDesign.configureMyCustom', async () => {
const baseUrl = await vscode.window.showInputBox({
prompt: 'Base URL for My Custom LLM (e.g., https://api.example.com/v1)'
});
const apiKey = await vscode.window.showInputBox({
prompt: 'API Key (will be stored securely)',
password: true
});
if (baseUrl && apiKey) {
await storage.store('mycustom.baseUrl', baseUrl);
await storage.store('mycustom.apiKey', apiKey);
vscode.window.showInformationMessage('My Custom LLM configured successfully');
}
});
context.subscriptions.push(cmd);
}
After configuration, the provider’s validateCredentials() method returns true, causing the model picker UI to display your custom models alongside built-in providers.
Summary
- Extend
AIProvider: Create a subclass insrc/providers/<name>.tswithmetadata,models,createInstance(), andvalidateCredentials(). - Register with DI: Import and register the provider in
src/di/ServiceContainer.tsusing theIProviderRegistrysingleton. - Secure configuration: Add a VS Code command in
package.jsonand implement credential storage viaSecureStorageServiceinsrc/services/secureStorageService.ts.
Once these steps are complete, the Secure-Design extension automatically surfaces your custom endpoint in the model picker and routes all LLM traffic through your implementation without requiring changes to chat or canvas logic.
Frequently Asked Questions
What is the minimum code required to implement a custom AI provider?
At minimum, you must create a class that extends AIProvider from src/providers/types.ts and implements three things: a static metadata object with id and configureCommand, a models array listing available model IDs, and a createInstance() method that returns a LanguageModelV2 instance. You should also implement validateCredentials() to ensure the UI only shows your provider when properly configured.
How does Secure-Design store API keys for custom providers?
The extension uses SecureStorageService defined in src/services/secureStorageService.ts, which wraps VS Code's built-in SecretStorage API. When you implement a configuration command, you instantiate SecureStorageService with context.secrets and call storage.store(key, value) to encrypt and persist credentials. These values are retrieved later via config.config.get() in the provider's createInstance() method.
Can I use a custom provider with existing chat features without modifying core code?
Yes. Once you register your provider with the IProviderRegistry in src/di/ServiceContainer.ts, downstream services like CustomAgentService automatically discover your models. The chat sidebar, model picker, and design generation features query the registry via getAllModels() and instantiate your provider through createInstance() without requiring any changes to the chat or canvas logic.
What configuration keys should I use for custom provider settings?
Follow the pattern <providerId>.<settingName> to avoid collisions. For example, if your provider ID is mycustom, use mycustom.baseUrl and mycustom.apiKey. List these keys in the additionalConfigKeys array of your metadata object so the configuration UI knows which settings to prompt for, and retrieve them in createInstance() using config.config.get<string>('mycustom.baseUrl').
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 →