diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..b37cc7d6 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "name": "Agent Workshop", + "image": "mcr.microsoft.com/devcontainers/javascript-node:20", + "postCreateCommand": "npm install", + "forwardPorts": [3000], + "features": { + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss", + "esbenp.prettier-vscode" + ], + "settings": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ] + } + } + } +} diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..ecd104cd --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +fund=false +audit=false +progress=false +loglevel=verbose diff --git a/LICENSE b/LICENSE index a856666b..46865ebb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,9 @@ MIT License -Copyright (c) 2024 Agent Workshop Contributors +Copyright (c) 2025-2026 Dakota Kim / reasoning.software (MadWatch LLC) + +build-an-agent and create-agent-app are the intellectual property of +reasoning.software (MadWatch LLC), created by Dakota Kim. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,8 +12,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +1. The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +2. Any use, fork, or derivative work of this Software must provide clear and + prominent credit to Dakota Kim as the original creator, including a link to + the original repository (https://github.com/GhostScientist/build-an-agent) + where reasonably possible. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, diff --git a/README.md b/README.md index ff0eaa53..4d687775 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ # Agent Workshop +> **build-an-agent / create-agent-app** is the intellectual property of **[reasoning.software](https://reasoning.software) (MadWatch LLC)**, created by **[Dakota Kim](https://github.com/GhostScientist)**. All use, forks, and derivative works must provide clear credit to the original creator. See [LICENSE](./LICENSE) for details. + +--- + Build custom AI agents with the Claude Agent SDK or OpenAI Agents SDK. Choose your approach: - **[Web UI](./agent-workshop-app)** - Visual builder with live code preview -- **[CLI](./create-agent-app)** - Interactive terminal wizard (`npx build-agent-app`) +- **[CLI](./create-agent-app)** - Interactive terminal wizard (`npx build-agent-app`) — or run without installing: `npx build-agent-app@latest` ## Quick Start ### CLI (Recommended) ```bash -npx build-agent-app my-agent +npx build-agent-app@latest my-agent cd my-agent cp .env.example .env # Add your API key npm run build @@ -67,4 +71,4 @@ Full documentation is available in the web UI at `/docs` or see the [agent-works ## License -MIT +MIT - Copyright (c) 2025-2026 Dakota Kim / reasoning.software (MadWatch LLC). See [LICENSE](./LICENSE). diff --git a/agent-workshop-app/src/app/layout.tsx b/agent-workshop-app/src/app/layout.tsx index 27d7b010..7576fe70 100644 --- a/agent-workshop-app/src/app/layout.tsx +++ b/agent-workshop-app/src/app/layout.tsx @@ -9,7 +9,7 @@ export const metadata: Metadata = { title: 'Agent Workshop - Build AI Agents in Minutes', description: 'Transform your ideas into powerful AI agents. Choose your domain, configure tools, and download a complete agent project ready for deployment.', keywords: ['AI agents', 'Claude', 'OpenAI', 'agent builder', 'automation', 'no-code'], - authors: [{ name: 'Agent Workshop Team' }], + authors: [{ name: 'Dakota Kim / reasoning.software (MadWatch LLC)' }], openGraph: { title: 'Agent Workshop - Build AI Agents in Minutes', description: 'From idea to agent in minutes. Build specialized AI assistants for any domain.', diff --git a/agent-workshop-app/src/app/page.tsx b/agent-workshop-app/src/app/page.tsx index bc62069c..0e17f5cc 100644 --- a/agent-workshop-app/src/app/page.tsx +++ b/agent-workshop-app/src/app/page.tsx @@ -195,6 +195,22 @@ export default function HomePage() { + {/* IP Attribution Banner */} +
+
+

+ build-an-agent / create-agent-app is the intellectual property of{' '} + + reasoning.software + {' '} + (MadWatch LLC), created by{' '} + + Dakota Kim + +

+
+
+ {/* Hero Section */}
@@ -237,6 +253,13 @@ export default function HomePage() { OpenAI OpenAI Agents SDK
+
+ 🤗 + HuggingFace Tiny Agents + + ✨ NEW + +

Generated agents include sophisticated slash command workflows for multi-step operations. - Built on Claude Agent SDK and OpenAI Agents API with MCP tools, streaming, and configurable security. + Built on Claude Agent SDK, OpenAI Agents API, and HuggingFace Tiny Agents with MCP tools, streaming, and configurable security.

-
+
{features.map((feature, index) => ( -
+ {feature.isNew && ( + + ✨ NEW + + )} +

{feature.title}

-

+

{feature.description}

+
+ {feature.providers?.map(provider => ( + + {provider === 'claude' ? 'Claude' : provider === 'openai' ? 'OpenAI' : '🤗 Tiny Agents'} + + ))} +
))}
@@ -852,19 +898,31 @@ export default function HomePage() {

Free, open-source tool for generating AI agent CLIs

+

+ Intellectual property of{' '} + + reasoning.software + {' '} + (MadWatch LLC) +

- Built by{' '} + Created by{' '} Dakota Kim

- © {new Date().getFullYear()} Build-An-Agent Workshop. Made with ❤️ for the AI community. + © 2025-{new Date().getFullYear()} Dakota Kim / reasoning.software (MadWatch LLC). MIT License.

@@ -878,16 +936,26 @@ const features = [ title: 'Multi-Step Workflows', description: 'Domain-specific slash commands like /literature-review, /code-audit, /invoice-batch orchestrate complex multi-step processes. Template variables, retry logic, and error handling built-in.', icon: CodeBracketIcon, + providers: ['claude', 'openai'], }, { title: 'Configurable Security', description: 'Claude Code-style permission system with interactive prompts for file operations, command execution, and network requests. Users approve high-risk actions before they execute.', icon: CogIcon, + providers: ['claude', 'openai'], }, { title: 'SDK-Native & Customizable', - description: 'Built on official Claude Agent SDK and OpenAI Agents API with MCP tool integration. Download complete TypeScript source. Modify prompts, add tools, or extend workflows from day one.', + description: 'Download complete TypeScript source code. Full control over prompts, tools, and workflows. Extend with custom business logic from day one.', icon: RocketLaunchIcon, + providers: ['claude', 'openai'], + }, + { + title: 'Zero-Build Tiny Agents', + description: 'Create lightweight agents with just JSON + markdown. No build step, no dependencies. Run instantly with npx, share to the HuggingFace community, and use powerful open-source models.', + icon: SparklesIcon, + providers: ['huggingface'], + isNew: true, }, ] diff --git a/agent-workshop-app/src/components/preview/ProjectPreview.tsx b/agent-workshop-app/src/components/preview/ProjectPreview.tsx index d66953a6..822a199b 100644 --- a/agent-workshop-app/src/components/preview/ProjectPreview.tsx +++ b/agent-workshop-app/src/components/preview/ProjectPreview.tsx @@ -24,7 +24,10 @@ interface ProjectPreviewProps { export function ProjectPreview({ project, onClose, onDownload }: ProjectPreviewProps) { const [selectedFile, setSelectedFile] = useState(project.files[0]) - const [activeTab, setActiveTab] = useState<'files' | 'overview' | 'security'>('files') + // Default to overview tab for HuggingFace to show usage instructions first + const [activeTab, setActiveTab] = useState<'files' | 'overview' | 'security'>( + project.config.sdkProvider === 'huggingface' ? 'overview' : 'files' + ) const getFileIcon = (file: GeneratedFile) => { switch (file.type) { @@ -172,8 +175,32 @@ export function ProjectPreview({ project, onClose, onDownload }: ProjectPreviewP initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: 20 }} - className="flex flex-1 overflow-hidden" + className="flex flex-1 flex-col overflow-hidden" > + {/* Quick Start Banner for HuggingFace */} + {project.config.sdkProvider === 'huggingface' && ( +
+
+
+ 🤗 +
+

Ready to run!

+ + npx @huggingface/tiny-agents run . + +
+
+ +
+
+ )} + +
{/* File Tree */}
@@ -258,6 +285,7 @@ export function ProjectPreview({ project, onClose, onDownload }: ProjectPreviewP
)}
+
)} @@ -327,7 +355,9 @@ export function ProjectPreview({ project, onClose, onDownload }: ProjectPreviewP {/* Build Instructions */}
-

Setup Instructions

+

+ {project.config.sdkProvider === 'huggingface' ? 'Quick Start' : 'Setup Instructions'} +

{project.metadata.buildInstructions.map((instruction, index) => (
@@ -340,36 +370,127 @@ export function ProjectPreview({ project, onClose, onDownload }: ProjectPreviewP
))}
+ {project.config.sdkProvider === 'huggingface' && ( +

+ No build step required! Your agent is ready to run instantly. +

+ )}
- {/* Dependencies */} -
-

Dependencies

-
-
-

Production Dependencies

-
- {Object.entries(project.metadata.dependencies).map(([name, version]) => ( -
- {name} - {version} -
- ))} + {/* Share with Community (HuggingFace only) */} + {project.config.sdkProvider === 'huggingface' && ( +
+
+ 🤗 +
+

Share with the Community!

+

Add your agent to the official tiny-agents dataset

-
-

Development Dependencies

-
- {Object.entries(project.metadata.devDependencies).map(([name, version]) => ( -
- {name} - {version} + + {/* Your Agent Files */} +
+

Your Agent Files

+
+ agent.json + PROMPT.md + EXAMPLES.md (optional) +
+
+ + {/* Folder Structure Visualization */} +
+

Folder Structure

+
+
tiny-agents/tiny-agents/
+
<your-username>/
+
{project.config.projectName}/
+
agent.json
+
PROMPT.md
+
+
+ + {/* Submission Steps */} +
+

How to Submit

+
+
+
1
+
+ Go to the dataset and click "+ Contribute""New pull request" + Open tiny-agents dataset →
- ))} +
+
+
2
+
+ After creating the PR, you'll see git instructions to push your files: +
+
# Clone and checkout your PR branch
+
git clone https://huggingface.co/datasets/tiny-agents/tiny-agents
+
git fetch origin refs/pr/<PR_NUMBER>:pr-<PR_NUMBER>
+
git checkout pr-<PR_NUMBER>
+
# Add your files and push
+
mkdir -p <your-username>/{project.config.projectName}
+
cp agent.json PROMPT.md <your-username>/{project.config.projectName}/
+
git add . && git commit -m "Add {project.config.projectName} agent"
+
git push origin HEAD:refs/pr/<PR_NUMBER>
+
+
+
+
+
3
+ Or use "Upload files" in the web UI to upload directly to your PR +
+ + {/* Test First */} +
+

+ Tip: Test your agent locally first: + npx @huggingface/tiny-agents run . +

+
+ + {/* After Merge */} +
+

After your PR is merged:

+ npx @huggingface/tiny-agents run <your-username>/{project.config.projectName} +
-
+ )} + + {/* Dependencies (hide for HuggingFace since there are none) */} + {project.config.sdkProvider !== 'huggingface' && ( +
+

Dependencies

+
+
+

Production Dependencies

+
+ {Object.entries(project.metadata.dependencies).map(([name, version]) => ( +
+ {name} + {version} +
+ ))} +
+
+
+

Development Dependencies

+
+ {Object.entries(project.metadata.devDependencies).map(([name, version]) => ( +
+ {name} + {version} +
+ ))} +
+
+
+
+ )}
)} diff --git a/agent-workshop-app/src/components/wizard/AgentBuilder.tsx b/agent-workshop-app/src/components/wizard/AgentBuilder.tsx index a6d1956e..53471740 100644 --- a/agent-workshop-app/src/components/wizard/AgentBuilder.tsx +++ b/agent-workshop-app/src/components/wizard/AgentBuilder.tsx @@ -1,14 +1,16 @@ 'use client' -import { useState, useCallback } from 'react' +import { useState, useCallback, useMemo, useEffect, useRef } from 'react' import { motion, AnimatePresence } from 'framer-motion' -import { - ArrowLeftIcon, +import { + ArrowLeftIcon, ArrowRightIcon, CheckIcon, XMarkIcon } from '@heroicons/react/24/outline' -import { AgentConfig, WizardStep } from '@/types/agent' +import { AgentConfig, WizardStep, SDKProvider } from '@/types/agent' +import { ProviderSelection } from './steps/ProviderSelection' +import { HuggingFaceQuickForm } from './steps/HuggingFaceQuickForm' import { DomainSelection } from './steps/DomainSelection' import { TemplateSelection } from './steps/TemplateSelection' import { SDKConfiguration } from './steps/SDKConfiguration' @@ -23,90 +25,159 @@ interface AgentBuilderProps { onBack: () => void } +// Step definitions for full flow (Claude/OpenAI) +const fullFlowSteps: WizardStep[] = [ + { + id: 'provider', + title: 'Choose Provider', + description: 'Select your AI provider', + component: ProviderSelection, + isComplete: (config) => !!config.sdkProvider, + validation: (config) => config.sdkProvider ? [] : ['Please select an AI provider'] + }, + { + id: 'domain', + title: 'Choose Domain', + description: 'Select your agent\'s area of expertise', + component: DomainSelection, + isComplete: (config) => !!config.domain, + validation: (config) => config.domain ? [] : ['Please select a domain'] + }, + { + id: 'template', + title: 'Select Template', + description: 'Pick a starting template for your agent', + component: TemplateSelection, + isComplete: (config) => !!config.templateId, + validation: (config) => config.templateId ? [] : ['Please select a template'] + }, + { + id: 'model', + title: 'Configure Model', + description: 'Choose your model and settings', + component: SDKConfiguration, + isComplete: (config) => !!config.model, + validation: (config) => { + const errors = [] + if (!config.model) errors.push('Please select a model') + return errors + } + }, + { + id: 'tools', + title: 'Configure Tools', + description: 'Select capabilities for your agent', + component: ToolConfiguration, + isComplete: (config) => !!(config.tools && config.tools.some(t => t.enabled)), + validation: (config) => { + if (!config.tools?.some(t => t.enabled)) { + return ['Please enable at least one tool'] + } + return [] + } + }, + { + id: 'mcp', + title: 'MCP Servers', + description: 'Connect to Model Context Protocol servers (optional)', + component: MCPConfiguration, + isComplete: () => true, + validation: () => [] + }, + { + id: 'project', + title: 'Project Settings', + description: 'Configure your project details', + component: ProjectSettings, + isComplete: (config) => !!(config.name && config.projectName && config.author), + validation: (config) => { + const errors = [] + if (!config.name) errors.push('Agent name is required') + if (!config.projectName) errors.push('Project name is required') + if (!config.author) errors.push('Author name is required') + return errors + } + }, + { + id: 'preview', + title: 'Preview & Generate', + description: 'Review your configuration and generate the project', + component: PreviewAndGenerate, + isComplete: () => true, + validation: () => [] + } +] + +// Step definitions for lightweight flow (HuggingFace) +const lightweightFlowSteps: WizardStep[] = [ + { + id: 'provider', + title: 'Choose Provider', + description: 'Select your AI provider', + component: ProviderSelection, + isComplete: (config) => !!config.sdkProvider, + validation: (config) => config.sdkProvider ? [] : ['Please select an AI provider'] + }, + { + id: 'config', + title: 'Configure Agent', + description: 'Set up your tiny-agent in one simple form', + component: HuggingFaceQuickForm, + isComplete: (config) => !!(config.name?.trim() && config.model), + validation: (config) => { + const errors = [] + if (!config.name?.trim()) errors.push('Agent name is required') + if (!config.model) errors.push('Please select a model') + return errors + } + }, + { + id: 'preview', + title: 'Preview & Download', + description: 'Review and download your agent files', + component: PreviewAndGenerate, + isComplete: () => true, + validation: () => [] + } +] + +// Get steps based on provider +function getStepsForProvider(provider?: SDKProvider): WizardStep[] { + if (provider === 'huggingface') { + return lightweightFlowSteps + } + return fullFlowSteps +} + export function AgentBuilder({ onBack }: AgentBuilderProps) { const { config, updateConfig, resetConfig } = useAgentStore() const [currentStep, setCurrentStep] = useState(0) + const previousProviderRef = useRef(config.sdkProvider) - const steps: WizardStep[] = [ - { - id: 'domain', - title: 'Choose Domain', - description: 'Select your agent\'s area of expertise', - component: DomainSelection, - isComplete: (config) => !!config.domain, - validation: (config) => config.domain ? [] : ['Please select a domain'] - }, - { - id: 'template', - title: 'Select Template', - description: 'Pick a starting template for your agent', - component: TemplateSelection, - isComplete: (config) => !!config.templateId, - validation: (config) => config.templateId ? [] : ['Please select a template'] - }, - { - id: 'sdk', - title: 'Configure AI Provider', - description: 'Choose your AI provider and model', - component: SDKConfiguration, - isComplete: (config) => !!config.sdkProvider, - validation: (config) => { - const errors = [] - if (!config.sdkProvider) errors.push('Please select an AI provider') - return errors - } - }, - { - id: 'tools', - title: 'Configure Tools', - description: 'Select capabilities for your agent', - component: ToolConfiguration, - isComplete: (config) => !!(config.tools && config.tools.some(t => t.enabled)), - validation: (config) => { - if (!config.tools?.some(t => t.enabled)) { - return ['Please enable at least one tool'] - } - return [] - } - }, - { - id: 'mcp', - title: 'MCP Servers', - description: 'Connect to Model Context Protocol servers (optional)', - component: MCPConfiguration, - isComplete: () => true, // Optional step, always complete - validation: () => [] // No validation required - }, - { - id: 'project', - title: 'Project Settings', - description: 'Configure your project details', - component: ProjectSettings, - isComplete: (config) => !!(config.name && config.projectName && config.author), - validation: (config) => { - const errors = [] - if (!config.name) errors.push('Agent name is required') - if (!config.projectName) errors.push('Project name is required') - if (!config.author) errors.push('Author name is required') - return errors + // Get current steps based on provider + const steps = useMemo(() => getStepsForProvider(config.sdkProvider), [config.sdkProvider]) + + // Handle provider switching - reset step if needed + useEffect(() => { + const previousProvider = previousProviderRef.current + const currentProvider = config.sdkProvider + + if (previousProvider !== currentProvider && currentProvider !== undefined) { + // Provider changed, reset to step 1 (after provider selection) + if (currentStep > 0) { + setCurrentStep(1) } - }, - { - id: 'preview', - title: 'Preview & Generate', - description: 'Review your configuration and generate the project', - component: PreviewAndGenerate, - isComplete: () => true, - validation: () => [] } - ] + + previousProviderRef.current = currentProvider + }, [config.sdkProvider, currentStep]) const currentStepData = steps[currentStep] const CurrentStepComponent = currentStepData.component const handleNext = useCallback(() => { const errors = currentStepData.validation?.(config) || [] - + if (errors.length > 0) { toast.error(errors[0]) return @@ -115,7 +186,7 @@ export function AgentBuilder({ onBack }: AgentBuilderProps) { if (currentStep < steps.length - 1) { setCurrentStep(currentStep + 1) } - }, [currentStep, currentStepData, config]) + }, [currentStep, currentStepData, config, steps.length]) const handlePrevious = useCallback(() => { if (currentStep > 0) { @@ -155,7 +226,7 @@ export function AgentBuilder({ onBack }: AgentBuilderProps) {

- +
+ {config.sdkProvider === 'huggingface' && ( + + Quick Setup + + )} {config.name && (
{config.name} @@ -179,21 +255,21 @@ export function AgentBuilder({ onBack }: AgentBuilderProps) {

Progress

- +
{steps.map((step, index) => { const isCompleted = step.isComplete(config) const isCurrent = index === currentStep const isAccessible = index <= currentStep - + return (
) -} \ No newline at end of file +} diff --git a/agent-workshop-app/src/components/wizard/steps/HuggingFaceQuickForm.tsx b/agent-workshop-app/src/components/wizard/steps/HuggingFaceQuickForm.tsx new file mode 100644 index 00000000..fc4516bd --- /dev/null +++ b/agent-workshop-app/src/components/wizard/steps/HuggingFaceQuickForm.tsx @@ -0,0 +1,389 @@ +'use client' + +import { useState, useEffect } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { + CheckIcon, + ChevronDownIcon, + ChevronUpIcon, + PlusIcon, + TrashIcon, + InformationCircleIcon, + ServerIcon, +} from '@heroicons/react/24/outline' +import { AgentConfig, MCPServer, MCPServerTemplate } from '@/types/agent' +import { MCP_SERVER_TEMPLATES } from '@/data/mcp-templates' + +interface HuggingFaceQuickFormProps { + config: Partial + updateConfig: (updates: Partial) => void + onNext: () => void +} + +const HUGGINGFACE_MODELS = [ + { + id: 'Qwen/Qwen3-235B-A22B-Instruct-2507', + name: 'Qwen 3 235B Instruct', + description: 'Most capable open-source model with excellent tool support', + contextWindow: '128K' + }, + { + id: 'Qwen/Qwen3-32B', + name: 'Qwen 3 32B', + description: 'Fast and capable with good tool support', + contextWindow: '128K' + }, + { + id: 'meta-llama/Llama-3.3-70B-Instruct', + name: 'Llama 3.3 70B', + description: 'Popular open-source model from Meta', + contextWindow: '128K' + }, + { + id: 'deepseek-ai/DeepSeek-R1', + name: 'DeepSeek R1', + description: 'Specialized in reasoning tasks', + contextWindow: '64K' + }, + { + id: 'deepseek-ai/DeepSeek-V3-0324', + name: 'DeepSeek V3', + description: 'Powerful general-purpose model', + contextWindow: '64K' + } +] + +// Popular MCP servers for quick-add (verified working packages) +const POPULAR_MCP_SERVERS = [ + 'playwright', + 'github', + 'filesystem', + 'memory', + 'sequential-thinking', + 'brave-search' +] + +export function HuggingFaceQuickForm({ config, updateConfig, onNext }: HuggingFaceQuickFormProps) { + const [showMCPSection, setShowMCPSection] = useState(false) + const [showAllTemplates, setShowAllTemplates] = useState(false) + + // Auto-apply defaults on mount + useEffect(() => { + const defaults: Partial = {} + + if (!config.domain) defaults.domain = 'development' + if (!config.version) defaults.version = '1.0.0' + if (!config.license) defaults.license = 'MIT' + if (!config.permissions) defaults.permissions = 'balanced' + if (!config.tools) defaults.tools = [] + if (!config.model) defaults.model = HUGGINGFACE_MODELS[0].id + + if (Object.keys(defaults).length > 0) { + updateConfig(defaults) + } + }, []) + + // Auto-generate projectName from name + useEffect(() => { + if (config.name) { + const projectName = config.name + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-|-$/g, '') + updateConfig({ + projectName, + packageName: projectName + }) + } + }, [config.name]) + + const mcpServers = config.mcpServers || [] + + const generateId = () => `mcp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` + + const addServerFromTemplate = (templateId: string) => { + const template = MCP_SERVER_TEMPLATES.find(t => t.id === templateId) + if (!template) return + + // Check if already added + if (mcpServers.some(s => s.name === template.id)) return + + const newServer: MCPServer = { + id: generateId(), + name: template.id, + description: template.description, + enabled: true, + ...template.defaultConfig, + } as MCPServer + + updateConfig({ + mcpServers: [...mcpServers, newServer] + }) + } + + const removeServer = (id: string) => { + updateConfig({ + mcpServers: mcpServers.filter(s => s.id !== id) + }) + } + + const popularTemplates = MCP_SERVER_TEMPLATES.filter(t => + POPULAR_MCP_SERVERS.includes(t.id) + ) + + const isValid = !!(config.name?.trim() && config.model) + + return ( +
+ {/* Header */} +
+
+ 🤗 + HuggingFace Tiny Agent +
+

+ Configure Your Agent +

+

+ Set up your lightweight agent in one simple form. No build step required! +

+
+ + {/* Agent Identity Section */} + +

Agent Identity

+ +
+
+ + updateConfig({ name: e.target.value })} + placeholder="My Awesome Agent" + className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" + /> + {config.name && ( +

+ Will be published as: {config.projectName} +

+ )} +
+ +
+ +