mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
- Restricted CORS to localhost origins to prevent remote code execution (RCE) attacks. - Updated MCP server configuration handling to enforce security warnings when adding or importing servers. - Introduced a SecurityWarningDialog to inform users about potential risks associated with server commands and configurations. - Ensured that only serverId is accepted for testing server connections, preventing arbitrary command execution. These changes improve the overall security posture of the MCP server management and usage.
199 lines
6.4 KiB
TypeScript
199 lines
6.4 KiB
TypeScript
/**
|
|
* Claude Provider - Executes queries using Claude Agent SDK
|
|
*
|
|
* Wraps the @anthropic-ai/claude-agent-sdk for seamless integration
|
|
* with the provider architecture.
|
|
*/
|
|
|
|
import { query, type Options } from '@anthropic-ai/claude-agent-sdk';
|
|
import { BaseProvider } from './base-provider.js';
|
|
import type {
|
|
ExecuteOptions,
|
|
ProviderMessage,
|
|
InstallationStatus,
|
|
ModelDefinition,
|
|
} from './types.js';
|
|
|
|
export class ClaudeProvider extends BaseProvider {
|
|
getName(): string {
|
|
return 'claude';
|
|
}
|
|
|
|
/**
|
|
* Execute a query using Claude Agent SDK
|
|
*/
|
|
async *executeQuery(options: ExecuteOptions): AsyncGenerator<ProviderMessage> {
|
|
const {
|
|
prompt,
|
|
model,
|
|
cwd,
|
|
systemPrompt,
|
|
maxTurns = 20,
|
|
allowedTools,
|
|
abortController,
|
|
conversationHistory,
|
|
sdkSessionId,
|
|
} = options;
|
|
|
|
// Build Claude SDK options
|
|
// MCP permission logic - determines how to handle tool permissions when MCP servers are configured.
|
|
// This logic mirrors buildMcpOptions() in sdk-options.ts but is applied here since
|
|
// the provider is the final point where SDK options are constructed.
|
|
const hasMcpServers = options.mcpServers && Object.keys(options.mcpServers).length > 0;
|
|
// Default to true for autonomous workflow. Security is enforced when adding servers
|
|
// via the security warning dialog that explains the risks.
|
|
const mcpAutoApprove = options.mcpAutoApproveTools ?? true;
|
|
const mcpUnrestricted = options.mcpUnrestrictedTools ?? true;
|
|
const defaultTools = ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch'];
|
|
|
|
// Determine permission mode based on settings
|
|
const shouldBypassPermissions = hasMcpServers && mcpAutoApprove;
|
|
// Determine if we should restrict tools (only when no MCP or unrestricted is disabled)
|
|
const shouldRestrictTools = !hasMcpServers || !mcpUnrestricted;
|
|
|
|
const sdkOptions: Options = {
|
|
model,
|
|
systemPrompt,
|
|
maxTurns,
|
|
cwd,
|
|
// Only restrict tools if explicitly set OR (no MCP / unrestricted disabled)
|
|
...(allowedTools && shouldRestrictTools && { allowedTools }),
|
|
...(!allowedTools && shouldRestrictTools && { allowedTools: defaultTools }),
|
|
// When MCP servers are configured and auto-approve is enabled, use bypassPermissions
|
|
permissionMode: shouldBypassPermissions ? 'bypassPermissions' : 'default',
|
|
// Required when using bypassPermissions mode
|
|
...(shouldBypassPermissions && { allowDangerouslySkipPermissions: true }),
|
|
abortController,
|
|
// Resume existing SDK session if we have a session ID
|
|
...(sdkSessionId && conversationHistory && conversationHistory.length > 0
|
|
? { resume: sdkSessionId }
|
|
: {}),
|
|
// Forward settingSources for CLAUDE.md file loading
|
|
...(options.settingSources && { settingSources: options.settingSources }),
|
|
// Forward sandbox configuration
|
|
...(options.sandbox && { sandbox: options.sandbox }),
|
|
// Forward MCP servers configuration
|
|
...(options.mcpServers && { mcpServers: options.mcpServers }),
|
|
};
|
|
|
|
// Build prompt payload
|
|
let promptPayload: string | AsyncIterable<any>;
|
|
|
|
if (Array.isArray(prompt)) {
|
|
// Multi-part prompt (with images)
|
|
promptPayload = (async function* () {
|
|
const multiPartPrompt = {
|
|
type: 'user' as const,
|
|
session_id: '',
|
|
message: {
|
|
role: 'user' as const,
|
|
content: prompt,
|
|
},
|
|
parent_tool_use_id: null,
|
|
};
|
|
yield multiPartPrompt;
|
|
})();
|
|
} else {
|
|
// Simple text prompt
|
|
promptPayload = prompt;
|
|
}
|
|
|
|
// Execute via Claude Agent SDK
|
|
try {
|
|
const stream = query({ prompt: promptPayload, options: sdkOptions });
|
|
|
|
// Stream messages directly - they're already in the correct format
|
|
for await (const msg of stream) {
|
|
yield msg as ProviderMessage;
|
|
}
|
|
} catch (error) {
|
|
console.error('[ClaudeProvider] ERROR: executeQuery() error during execution:', error);
|
|
console.error('[ClaudeProvider] ERROR stack:', (error as Error).stack);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Detect Claude SDK installation (always available via npm)
|
|
*/
|
|
async detectInstallation(): Promise<InstallationStatus> {
|
|
// Claude SDK is always available since it's a dependency
|
|
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
|
|
const status: InstallationStatus = {
|
|
installed: true,
|
|
method: 'sdk',
|
|
hasApiKey,
|
|
authenticated: hasApiKey,
|
|
};
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* Get available Claude models
|
|
*/
|
|
getAvailableModels(): ModelDefinition[] {
|
|
const models = [
|
|
{
|
|
id: 'claude-opus-4-5-20251101',
|
|
name: 'Claude Opus 4.5',
|
|
modelString: 'claude-opus-4-5-20251101',
|
|
provider: 'anthropic',
|
|
description: 'Most capable Claude model',
|
|
contextWindow: 200000,
|
|
maxOutputTokens: 16000,
|
|
supportsVision: true,
|
|
supportsTools: true,
|
|
tier: 'premium' as const,
|
|
default: true,
|
|
},
|
|
{
|
|
id: 'claude-sonnet-4-20250514',
|
|
name: 'Claude Sonnet 4',
|
|
modelString: 'claude-sonnet-4-20250514',
|
|
provider: 'anthropic',
|
|
description: 'Balanced performance and cost',
|
|
contextWindow: 200000,
|
|
maxOutputTokens: 16000,
|
|
supportsVision: true,
|
|
supportsTools: true,
|
|
tier: 'standard' as const,
|
|
},
|
|
{
|
|
id: 'claude-3-5-sonnet-20241022',
|
|
name: 'Claude 3.5 Sonnet',
|
|
modelString: 'claude-3-5-sonnet-20241022',
|
|
provider: 'anthropic',
|
|
description: 'Fast and capable',
|
|
contextWindow: 200000,
|
|
maxOutputTokens: 8000,
|
|
supportsVision: true,
|
|
supportsTools: true,
|
|
tier: 'standard' as const,
|
|
},
|
|
{
|
|
id: 'claude-haiku-4-5-20251001',
|
|
name: 'Claude Haiku 4.5',
|
|
modelString: 'claude-haiku-4-5-20251001',
|
|
provider: 'anthropic',
|
|
description: 'Fastest Claude model',
|
|
contextWindow: 200000,
|
|
maxOutputTokens: 8000,
|
|
supportsVision: true,
|
|
supportsTools: true,
|
|
tier: 'basic' as const,
|
|
},
|
|
] satisfies ModelDefinition[];
|
|
return models;
|
|
}
|
|
|
|
/**
|
|
* Check if the provider supports a specific feature
|
|
*/
|
|
supportsFeature(feature: string): boolean {
|
|
const supportedFeatures = ['tools', 'text', 'vision', 'thinking'];
|
|
return supportedFeatures.includes(feature);
|
|
}
|
|
}
|