feat: more work on mcp server integration
This commit is contained in:
@@ -1674,7 +1674,7 @@ const Dashboard: React.FC = () => {
|
||||
<span className="timeline-bar-count">{entry.count}</span>
|
||||
</div>
|
||||
<div className="timeline-bar-label">
|
||||
<span className="timeline-bar-label-month">{monthFormatter.format(new Date(entry.year, entry.month, 1))}</span>
|
||||
<span className="timeline-bar-label-month">{monthFormatter.format(new Date(entry.year, entry.month - 1, 1))}</span>
|
||||
<span className="timeline-bar-label-year">{entry.year}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import './SettingsView.css';
|
||||
|
||||
// Export category IDs for sidebar navigation
|
||||
export type SettingsCategory = 'project' | 'editor' | 'content' | 'ai' | 'technology' | 'publishing' | 'data';
|
||||
export type SettingsCategory = 'project' | 'editor' | 'content' | 'ai' | 'technology' | 'publishing' | 'data' | 'mcp';
|
||||
|
||||
// Scroll to a settings section by category ID
|
||||
export const scrollToSettingsSection = (category: SettingsCategory) => {
|
||||
@@ -122,6 +122,62 @@ const SettingSection: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
/** Small component that shows the MCP server port or "Not running". */
|
||||
const MCPStatusBadge: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const [port, setPort] = React.useState<number | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.electronAPI?.mcp?.getPort().then(setPort).catch(() => setPort(null));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<span className={`badge ${port ? 'badge-success' : 'badge-secondary'}`}>
|
||||
{port ? t('settings.mcp.portRunning', { port: String(port) }) : t('settings.mcp.portStopped')}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
/** Button to add bDS MCP server to an agent's config. Shows "Configured" if already present. */
|
||||
const MCPAgentButton: React.FC<{ agentId: string; agentLabel: string }> = ({ agentId, agentLabel }) => {
|
||||
const { t } = useI18n();
|
||||
const [configured, setConfigured] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
window.electronAPI?.mcp?.isConfigured(agentId).then(setConfigured).catch(() => setConfigured(false));
|
||||
}, [agentId]);
|
||||
|
||||
if (configured) {
|
||||
return <span className="badge badge-success">{t('settings.mcp.alreadyConfigured')}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
className="secondary"
|
||||
disabled={loading}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await window.electronAPI?.mcp?.addToAgentConfig(agentId);
|
||||
if (result?.success) {
|
||||
showToast.success(t('settings.toast.mcpConfigSuccess', { agent: agentLabel }));
|
||||
setConfigured(true);
|
||||
} else {
|
||||
showToast.error(t('settings.toast.mcpConfigFailed', { agent: agentLabel, error: result?.error ?? 'Unknown error' }));
|
||||
}
|
||||
} catch {
|
||||
showToast.error(t('settings.toast.mcpConfigFailed', { agent: agentLabel, error: 'Unexpected error' }));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('settings.mcp.addToAgent', { agent: agentLabel })}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsView: React.FC = () => {
|
||||
const { t } = useI18n();
|
||||
const {
|
||||
@@ -417,6 +473,7 @@ export const SettingsView: React.FC = () => {
|
||||
const technologyKeywords = ['technology', 'python', 'runtime', 'worker', 'webworker', 'main thread', 'execution'];
|
||||
const publishingKeywords = ['publishing', 'ssh', 'deploy', 'server', 'host', 'upload', 'scp', 'rsync'];
|
||||
const dataKeywords = ['data', 'database', 'rebuild', 'maintenance', 'posts', 'media', 'scripts', 'links', 'folder', 'filesystem'];
|
||||
const mcpKeywords = ['mcp', 'server', 'agent', 'claude', 'copilot', 'gemini', 'opencode', 'model context protocol', 'coding', 'configuration'];
|
||||
|
||||
const renderProjectSettings = () => (
|
||||
<SettingSection
|
||||
@@ -1197,6 +1254,43 @@ export const SettingsView: React.FC = () => {
|
||||
</SettingSection>
|
||||
);
|
||||
|
||||
const renderMCPSettings = () => {
|
||||
const agents = [
|
||||
{ id: 'claude-code', label: 'Claude Code' },
|
||||
{ id: 'github-copilot', label: 'GitHub Copilot' },
|
||||
{ id: 'gemini-cli', label: 'Gemini CLI' },
|
||||
{ id: 'opencode', label: 'OpenCode' },
|
||||
];
|
||||
|
||||
return (
|
||||
<SettingSection
|
||||
id="settings-section-mcp"
|
||||
title={t('settings.mcp.title')}
|
||||
description={t('settings.mcp.description')}
|
||||
hidden={!sectionHasMatches(mcpKeywords)}
|
||||
>
|
||||
<SettingRow
|
||||
id="mcp-status"
|
||||
label={t('settings.mcp.statusLabel')}
|
||||
description={t('settings.mcp.statusDescription')}
|
||||
>
|
||||
<MCPStatusBadge />
|
||||
</SettingRow>
|
||||
|
||||
{agents.map((agent) => (
|
||||
<SettingRow
|
||||
key={agent.id}
|
||||
id={`mcp-agent-${agent.id}`}
|
||||
label={t('settings.mcp.addToAgent', { agent: agent.label })}
|
||||
description=""
|
||||
>
|
||||
<MCPAgentButton agentId={agent.id} agentLabel={agent.label} />
|
||||
</SettingRow>
|
||||
))}
|
||||
</SettingSection>
|
||||
);
|
||||
};
|
||||
|
||||
const renderDataSettings = () => (
|
||||
<>
|
||||
<SettingSection
|
||||
@@ -1392,7 +1486,8 @@ export const SettingsView: React.FC = () => {
|
||||
sectionHasMatches(aiKeywords) ||
|
||||
sectionHasMatches(technologyKeywords) ||
|
||||
sectionHasMatches(publishingKeywords) ||
|
||||
sectionHasMatches(dataKeywords);
|
||||
sectionHasMatches(dataKeywords) ||
|
||||
sectionHasMatches(mcpKeywords);
|
||||
|
||||
return (
|
||||
<div className="settings-view">
|
||||
@@ -1429,6 +1524,7 @@ export const SettingsView: React.FC = () => {
|
||||
{renderTechnologySettings()}
|
||||
{renderPublishingSettings()}
|
||||
{renderDataSettings()}
|
||||
{renderMCPSettings()}
|
||||
</>
|
||||
) : (
|
||||
<div className="settings-no-results">
|
||||
|
||||
@@ -167,7 +167,7 @@ const CalendarView: React.FC<CalendarViewProps> = ({ onDateSelect, selectedYear,
|
||||
onDateSelect(year, month);
|
||||
}}
|
||||
>
|
||||
<span className="month-label">{MONTH_NAMES[month]}</span>
|
||||
<span className="month-label">{MONTH_NAMES[month - 1]}</span>
|
||||
<span className="month-count">{count}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -374,7 +374,7 @@ const MediaCalendarView: React.FC<MediaCalendarViewProps> = ({ onDateSelect, sel
|
||||
onDateSelect(year, month);
|
||||
}}
|
||||
>
|
||||
<span className="month-label">{MONTH_NAMES[month]}</span>
|
||||
<span className="month-label">{MONTH_NAMES[month - 1]}</span>
|
||||
<span className="month-count">{count}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -1261,7 +1261,7 @@ const SettingsNav: React.FC = () => {
|
||||
const { tabs, activeTabId, openTab } = useAppStore();
|
||||
const [activeSection, setActiveSection] = useState<SettingsCategory | null>(() => {
|
||||
const persisted = getPersistedSidebarSection('settings');
|
||||
if (persisted === 'project' || persisted === 'editor' || persisted === 'content' || persisted === 'ai' || persisted === 'technology' || persisted === 'publishing' || persisted === 'data') {
|
||||
if (persisted === 'project' || persisted === 'editor' || persisted === 'content' || persisted === 'ai' || persisted === 'technology' || persisted === 'publishing' || persisted === 'data' || persisted === 'mcp') {
|
||||
return persisted;
|
||||
}
|
||||
return null;
|
||||
@@ -1343,6 +1343,13 @@ const SettingsNav: React.FC = () => {
|
||||
<span className="settings-nav-entry-icon">🗄️</span>
|
||||
<span>{t('sidebar.nav.data')}</span>
|
||||
</button>
|
||||
<button
|
||||
className={`settings-nav-entry ${activeSection === 'mcp' ? 'active' : ''}`}
|
||||
onClick={() => handleNavClick('mcp')}
|
||||
>
|
||||
<span className="settings-nav-entry-icon">🔌</span>
|
||||
<span>{t('sidebar.nav.mcp')}</span>
|
||||
</button>
|
||||
<button
|
||||
className={`settings-nav-entry ${isStyleTabActive ? 'active' : ''}`}
|
||||
onClick={handleStyleClick}
|
||||
|
||||
@@ -975,5 +975,21 @@
|
||||
"importAnalysis.macroUses": "{count} Verwendungen",
|
||||
"importAnalysis.usedIn": "Verwendet in: {items}{more}",
|
||||
"importAnalysis.moreSuffix": ", +{count} weitere",
|
||||
"importAnalysis.noParameters": "(keine Parameter)"
|
||||
"importAnalysis.noParameters": "(keine Parameter)",
|
||||
|
||||
"sidebar.nav.mcp": "MCP-Server",
|
||||
|
||||
"settings.mcp.title": "MCP-Server",
|
||||
"settings.mcp.description": "Konfigurieren Sie den Model Context Protocol Server, der KI-Programmieragenten die Interaktion mit Ihrem Blog ermöglicht.",
|
||||
"settings.mcp.statusLabel": "Serverstatus",
|
||||
"settings.mcp.statusDescription": "Aktueller Status des MCP-Servers.",
|
||||
"settings.mcp.portRunning": "Läuft auf Port {port}",
|
||||
"settings.mcp.portStopped": "Nicht gestartet",
|
||||
"settings.mcp.agentsTitle": "Agenten-Konfiguration",
|
||||
"settings.mcp.agentsDescription": "Fügen Sie den bDS MCP-Server zur Konfiguration Ihres Programmieragenten hinzu. Vorhandene Einstellungen bleiben erhalten.",
|
||||
"settings.mcp.addToAgent": "Zu {agent} hinzufügen",
|
||||
"settings.mcp.alreadyConfigured": "Konfiguriert",
|
||||
"settings.toast.mcpConfigSuccess": "bDS MCP-Server zur {agent}-Konfiguration hinzugefügt",
|
||||
"settings.toast.mcpConfigFailed": "Konfiguration von {agent} fehlgeschlagen: {error}",
|
||||
"settings.toast.mcpConfigPath": "Konfiguration geschrieben nach {path}"
|
||||
}
|
||||
|
||||
@@ -975,5 +975,21 @@
|
||||
"importAnalysis.macroUses": "{count} uses",
|
||||
"importAnalysis.usedIn": "Used in: {items}{more}",
|
||||
"importAnalysis.moreSuffix": ", +{count} more",
|
||||
"importAnalysis.noParameters": "(no parameters)"
|
||||
"importAnalysis.noParameters": "(no parameters)",
|
||||
|
||||
"sidebar.nav.mcp": "MCP Server",
|
||||
|
||||
"settings.mcp.title": "MCP Server",
|
||||
"settings.mcp.description": "Configure the Model Context Protocol server that allows AI coding agents to interact with your blog.",
|
||||
"settings.mcp.statusLabel": "Server Status",
|
||||
"settings.mcp.statusDescription": "Current status of the MCP server.",
|
||||
"settings.mcp.portRunning": "Running on port {port}",
|
||||
"settings.mcp.portStopped": "Not running",
|
||||
"settings.mcp.agentsTitle": "Agent Configuration",
|
||||
"settings.mcp.agentsDescription": "Add the bDS MCP server to your coding agent's configuration. Existing settings are preserved.",
|
||||
"settings.mcp.addToAgent": "Add to {agent}",
|
||||
"settings.mcp.alreadyConfigured": "Configured",
|
||||
"settings.toast.mcpConfigSuccess": "bDS MCP server added to {agent} configuration",
|
||||
"settings.toast.mcpConfigFailed": "Failed to configure {agent}: {error}",
|
||||
"settings.toast.mcpConfigPath": "Config written to {path}"
|
||||
}
|
||||
|
||||
@@ -975,5 +975,21 @@
|
||||
"importAnalysis.macroUses": "{count} usos",
|
||||
"importAnalysis.usedIn": "Usado en: {items}{more}",
|
||||
"importAnalysis.moreSuffix": ", +{count} más",
|
||||
"importAnalysis.noParameters": "(sin parámetros)"
|
||||
"importAnalysis.noParameters": "(sin parámetros)",
|
||||
|
||||
"sidebar.nav.mcp": "Servidor MCP",
|
||||
|
||||
"settings.mcp.title": "Servidor MCP",
|
||||
"settings.mcp.description": "Configure el servidor Model Context Protocol que permite a los agentes de programación IA interactuar con su blog.",
|
||||
"settings.mcp.statusLabel": "Estado del servidor",
|
||||
"settings.mcp.statusDescription": "Estado actual del servidor MCP.",
|
||||
"settings.mcp.portRunning": "Ejecutándose en el puerto {port}",
|
||||
"settings.mcp.portStopped": "No está en ejecución",
|
||||
"settings.mcp.agentsTitle": "Configuración de agentes",
|
||||
"settings.mcp.agentsDescription": "Añada el servidor MCP de bDS a la configuración de su agente de programación. Las configuraciones existentes se conservan.",
|
||||
"settings.mcp.addToAgent": "Añadir a {agent}",
|
||||
"settings.mcp.alreadyConfigured": "Configurado",
|
||||
"settings.toast.mcpConfigSuccess": "Servidor MCP de bDS añadido a la configuración de {agent}",
|
||||
"settings.toast.mcpConfigFailed": "Error al configurar {agent}: {error}",
|
||||
"settings.toast.mcpConfigPath": "Configuración escrita en {path}"
|
||||
}
|
||||
|
||||
@@ -973,5 +973,21 @@
|
||||
"importAnalysis.macroUses": "{count} utilisations",
|
||||
"importAnalysis.usedIn": "Utilisé dans : {items}{more}",
|
||||
"importAnalysis.moreSuffix": ", +{count} de plus",
|
||||
"importAnalysis.noParameters": "(aucun paramètre)"
|
||||
"importAnalysis.noParameters": "(aucun paramètre)",
|
||||
|
||||
"sidebar.nav.mcp": "Serveur MCP",
|
||||
|
||||
"settings.mcp.title": "Serveur MCP",
|
||||
"settings.mcp.description": "Configurez le serveur Model Context Protocol qui permet aux agents de programmation IA d'interagir avec votre blog.",
|
||||
"settings.mcp.statusLabel": "État du serveur",
|
||||
"settings.mcp.statusDescription": "État actuel du serveur MCP.",
|
||||
"settings.mcp.portRunning": "En cours d'exécution sur le port {port}",
|
||||
"settings.mcp.portStopped": "Non démarré",
|
||||
"settings.mcp.agentsTitle": "Configuration des agents",
|
||||
"settings.mcp.agentsDescription": "Ajoutez le serveur MCP bDS à la configuration de votre agent de programmation. Les paramètres existants sont préservés.",
|
||||
"settings.mcp.addToAgent": "Ajouter à {agent}",
|
||||
"settings.mcp.alreadyConfigured": "Configuré",
|
||||
"settings.toast.mcpConfigSuccess": "Serveur MCP bDS ajouté à la configuration de {agent}",
|
||||
"settings.toast.mcpConfigFailed": "Échec de la configuration de {agent}: {error}",
|
||||
"settings.toast.mcpConfigPath": "Configuration écrite dans {path}"
|
||||
}
|
||||
|
||||
@@ -973,5 +973,21 @@
|
||||
"importAnalysis.macroUses": "{count} utilizzi",
|
||||
"importAnalysis.usedIn": "Usato in: {items}{more}",
|
||||
"importAnalysis.moreSuffix": ", +{count} altri",
|
||||
"importAnalysis.noParameters": "(nessun parametro)"
|
||||
"importAnalysis.noParameters": "(nessun parametro)",
|
||||
|
||||
"sidebar.nav.mcp": "Server MCP",
|
||||
|
||||
"settings.mcp.title": "Server MCP",
|
||||
"settings.mcp.description": "Configura il server Model Context Protocol che permette agli agenti di programmazione IA di interagire con il tuo blog.",
|
||||
"settings.mcp.statusLabel": "Stato del server",
|
||||
"settings.mcp.statusDescription": "Stato attuale del server MCP.",
|
||||
"settings.mcp.portRunning": "In esecuzione sulla porta {port}",
|
||||
"settings.mcp.portStopped": "Non in esecuzione",
|
||||
"settings.mcp.agentsTitle": "Configurazione agenti",
|
||||
"settings.mcp.agentsDescription": "Aggiungi il server MCP bDS alla configurazione del tuo agente di programmazione. Le impostazioni esistenti vengono preservate.",
|
||||
"settings.mcp.addToAgent": "Aggiungi a {agent}",
|
||||
"settings.mcp.alreadyConfigured": "Configurato",
|
||||
"settings.toast.mcpConfigSuccess": "Server MCP bDS aggiunto alla configurazione di {agent}",
|
||||
"settings.toast.mcpConfigFailed": "Configurazione di {agent} non riuscita: {error}",
|
||||
"settings.toast.mcpConfigPath": "Configurazione scritta in {path}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user