mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-05-30 02:49:12 -04:00
feat: Add qodeassist mcp server
This commit is contained in:
@@ -155,6 +155,7 @@ add_qtc_plugin(QodeAssist
|
|||||||
tools/ReadFileTool.hpp tools/ReadFileTool.cpp
|
tools/ReadFileTool.hpp tools/ReadFileTool.cpp
|
||||||
tools/FileSearchUtils.hpp tools/FileSearchUtils.cpp
|
tools/FileSearchUtils.hpp tools/FileSearchUtils.cpp
|
||||||
tools/TodoTool.hpp tools/TodoTool.cpp
|
tools/TodoTool.hpp tools/TodoTool.cpp
|
||||||
|
mcp/McpServerManager.hpp mcp/McpServerManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
get_target_property(QtCreatorCorePath QtCreator::Core LOCATION)
|
get_target_property(QtCreatorCorePath QtCreator::Core LOCATION)
|
||||||
|
|||||||
122
mcp/McpServerManager.cpp
Normal file
122
mcp/McpServerManager.cpp
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (C) 2024-2026 Petr Mironychev
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "McpServerManager.hpp"
|
||||||
|
|
||||||
|
#include <LLMQore/BaseTool.hpp>
|
||||||
|
#include <LLMQore/McpHttpServerTransport.hpp>
|
||||||
|
#include <LLMQore/McpServer.hpp>
|
||||||
|
#include <LLMQore/McpTypes.hpp>
|
||||||
|
#include <LLMQore/Version.hpp>
|
||||||
|
|
||||||
|
#include <QHostAddress>
|
||||||
|
|
||||||
|
#include <logger/Logger.hpp>
|
||||||
|
#include <settings/McpSettings.hpp>
|
||||||
|
|
||||||
|
#include "tools/BuildProjectTool.hpp"
|
||||||
|
#include "tools/CreateNewFileTool.hpp"
|
||||||
|
#include "tools/EditFileTool.hpp"
|
||||||
|
#include "tools/ExecuteTerminalCommandTool.hpp"
|
||||||
|
#include "tools/FindFileTool.hpp"
|
||||||
|
#include "tools/GetIssuesListTool.hpp"
|
||||||
|
#include "tools/ListProjectFilesTool.hpp"
|
||||||
|
#include "tools/ProjectSearchTool.hpp"
|
||||||
|
#include "tools/ReadFileTool.hpp"
|
||||||
|
#include "tools/TodoTool.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Mcp {
|
||||||
|
|
||||||
|
McpServerManager::McpServerManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
McpServerManager::~McpServerManager()
|
||||||
|
{
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpServerManager::init()
|
||||||
|
{
|
||||||
|
auto &s = Settings::mcpSettings();
|
||||||
|
|
||||||
|
auto onChanged = [this] { reconfigure(); };
|
||||||
|
connect(&s.enableMcpServer, &Utils::BaseAspect::changed, this, onChanged);
|
||||||
|
connect(&s.mcpServerPort, &Utils::BaseAspect::changed, this, onChanged);
|
||||||
|
|
||||||
|
reconfigure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpServerManager::reconfigure()
|
||||||
|
{
|
||||||
|
auto &s = Settings::mcpSettings();
|
||||||
|
const bool wantRunning = s.enableMcpServer();
|
||||||
|
const quint16 wantPort = static_cast<quint16>(s.mcpServerPort());
|
||||||
|
|
||||||
|
if (wantRunning && m_server && m_runningPort == wantPort)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stopServer();
|
||||||
|
|
||||||
|
if (wantRunning) {
|
||||||
|
startServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpServerManager::startServer()
|
||||||
|
{
|
||||||
|
auto &s = Settings::mcpSettings();
|
||||||
|
|
||||||
|
::LLMQore::Mcp::HttpServerConfig cfg;
|
||||||
|
cfg.address = QHostAddress::LocalHost;
|
||||||
|
cfg.port = static_cast<quint16>(s.mcpServerPort());
|
||||||
|
cfg.path = QStringLiteral("/mcp");
|
||||||
|
|
||||||
|
m_transport = new ::LLMQore::Mcp::McpHttpServerTransport(cfg, this);
|
||||||
|
|
||||||
|
::LLMQore::Mcp::McpServerConfig scfg;
|
||||||
|
scfg.serverInfo = {"QodeAssist",
|
||||||
|
QStringLiteral(LLMQORE_VERSION_STRING)};
|
||||||
|
scfg.instructions = tr("QodeAssist MCP server exposing Qt Creator project tools.");
|
||||||
|
|
||||||
|
m_server = new ::LLMQore::Mcp::McpServer(m_transport.data(), scfg, this);
|
||||||
|
|
||||||
|
m_server->addTool(new Tools::ListProjectFilesTool(m_server));
|
||||||
|
m_server->addTool(new Tools::FindFileTool(m_server));
|
||||||
|
m_server->addTool(new Tools::ReadFileTool(m_server));
|
||||||
|
m_server->addTool(new Tools::ProjectSearchTool(m_server));
|
||||||
|
m_server->addTool(new Tools::CreateNewFileTool(m_server));
|
||||||
|
m_server->addTool(new Tools::EditFileTool(m_server));
|
||||||
|
m_server->addTool(new Tools::BuildProjectTool(m_server));
|
||||||
|
m_server->addTool(new Tools::GetIssuesListTool(m_server));
|
||||||
|
m_server->addTool(new Tools::ExecuteTerminalCommandTool(m_server));
|
||||||
|
m_server->addTool(new Tools::TodoTool(m_server));
|
||||||
|
|
||||||
|
m_server->start();
|
||||||
|
|
||||||
|
if (!m_transport->isOpen()) {
|
||||||
|
LOG_MESSAGE(QString("QodeAssist MCP server failed to start on port %1").arg(cfg.port));
|
||||||
|
stopServer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_runningPort = m_transport->serverPort();
|
||||||
|
LOG_MESSAGE(QString("QodeAssist MCP server listening on http://127.0.0.1:%1/mcp")
|
||||||
|
.arg(m_runningPort));
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpServerManager::stopServer()
|
||||||
|
{
|
||||||
|
if (m_server) {
|
||||||
|
m_server->stop();
|
||||||
|
m_server->deleteLater();
|
||||||
|
m_server.clear();
|
||||||
|
}
|
||||||
|
if (m_transport) {
|
||||||
|
m_transport->deleteLater();
|
||||||
|
m_transport.clear();
|
||||||
|
}
|
||||||
|
m_runningPort = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Mcp
|
||||||
35
mcp/McpServerManager.hpp
Normal file
35
mcp/McpServerManager.hpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (C) 2024-2026 Petr Mironychev
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
|
||||||
|
namespace LLMQore::Mcp {
|
||||||
|
class McpServer;
|
||||||
|
class McpHttpServerTransport;
|
||||||
|
} // namespace LLMQore::Mcp
|
||||||
|
|
||||||
|
namespace QodeAssist::Mcp {
|
||||||
|
|
||||||
|
class McpServerManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit McpServerManager(QObject *parent = nullptr);
|
||||||
|
~McpServerManager() override;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reconfigure();
|
||||||
|
void startServer();
|
||||||
|
void stopServer();
|
||||||
|
|
||||||
|
QPointer<::LLMQore::Mcp::McpServer> m_server;
|
||||||
|
QPointer<::LLMQore::Mcp::McpHttpServerTransport> m_transport;
|
||||||
|
quint16 m_runningPort = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Mcp
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "pluginllmcore/PromptProviderFim.hpp"
|
#include "pluginllmcore/PromptProviderFim.hpp"
|
||||||
#include "pluginllmcore/ProvidersManager.hpp"
|
#include "pluginllmcore/ProvidersManager.hpp"
|
||||||
#include "logger/RequestPerformanceLogger.hpp"
|
#include "logger/RequestPerformanceLogger.hpp"
|
||||||
|
#include "mcp/McpServerManager.hpp"
|
||||||
#include "providers/Providers.hpp"
|
#include "providers/Providers.hpp"
|
||||||
#include "settings/ChatAssistantSettings.hpp"
|
#include "settings/ChatAssistantSettings.hpp"
|
||||||
#include "settings/GeneralSettings.hpp"
|
#include "settings/GeneralSettings.hpp"
|
||||||
@@ -163,6 +164,9 @@ public:
|
|||||||
Settings::setupProjectPanel();
|
Settings::setupProjectPanel();
|
||||||
ConfigurationManager::instance().init();
|
ConfigurationManager::instance().init();
|
||||||
|
|
||||||
|
m_mcpServerManager = new Mcp::McpServerManager(this);
|
||||||
|
m_mcpServerManager->init();
|
||||||
|
|
||||||
if (Settings::generalSettings().enableCheckUpdate()) {
|
if (Settings::generalSettings().enableCheckUpdate()) {
|
||||||
QTimer::singleShot(3000, this, &QodeAssistPlugin::checkForUpdates);
|
QTimer::singleShot(3000, this, &QodeAssistPlugin::checkForUpdates);
|
||||||
}
|
}
|
||||||
@@ -298,6 +302,7 @@ private:
|
|||||||
UpdateStatusWidget *m_statusWidget{nullptr};
|
UpdateStatusWidget *m_statusWidget{nullptr};
|
||||||
QString m_lastRefactorInstructions;
|
QString m_lastRefactorInstructions;
|
||||||
QScopedPointer<Chat::ChatView> m_chatView;
|
QScopedPointer<Chat::ChatView> m_chatView;
|
||||||
|
QPointer<Mcp::McpServerManager> m_mcpServerManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist::Internal
|
} // namespace QodeAssist::Internal
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
|
|
||||||
#include <utils/aspects.h>
|
#include <utils/aspects.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <QPushButton>
|
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
class ButtonAspect : public Utils::BaseAspect
|
class ButtonAspect : public Utils::BaseAspect
|
||||||
{
|
{
|
||||||
@@ -21,17 +22,25 @@ public:
|
|||||||
{
|
{
|
||||||
auto button = new QPushButton(m_buttonText);
|
auto button = new QPushButton(m_buttonText);
|
||||||
button->setVisible(m_visible);
|
button->setVisible(m_visible);
|
||||||
|
button->setAutoDefault(false);
|
||||||
|
button->setDefault(false);
|
||||||
|
button->setFocusPolicy(Qt::TabFocus);
|
||||||
|
|
||||||
|
QTimer::singleShot(0, button, [button] {
|
||||||
|
button->setAutoDefault(false);
|
||||||
|
button->setDefault(false);
|
||||||
|
});
|
||||||
|
|
||||||
if (!m_icon.isNull()) {
|
if (!m_icon.isNull()) {
|
||||||
button->setIcon(m_icon);
|
button->setIcon(m_icon);
|
||||||
button->setText(""); // Clear text if icon is set
|
button->setText("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_isCompact) {
|
if (m_isCompact) {
|
||||||
button->setMaximumWidth(30);
|
button->setMaximumWidth(30);
|
||||||
button->setToolTip(m_tooltip.isEmpty() ? m_buttonText : m_tooltip);
|
button->setToolTip(m_tooltip.isEmpty() ? m_buttonText : m_tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);
|
connect(button, &QPushButton::clicked, this, &ButtonAspect::clicked);
|
||||||
connect(this, &ButtonAspect::visibleChanged, button, &QPushButton::setVisible);
|
connect(this, &ButtonAspect::visibleChanged, button, &QPushButton::setVisible);
|
||||||
parent.addItem(button);
|
parent.addItem(button);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ add_library(QodeAssistSettings STATIC
|
|||||||
ChatAssistantSettings.hpp ChatAssistantSettings.cpp
|
ChatAssistantSettings.hpp ChatAssistantSettings.cpp
|
||||||
QuickRefactorSettings.hpp QuickRefactorSettings.cpp
|
QuickRefactorSettings.hpp QuickRefactorSettings.cpp
|
||||||
ToolsSettings.hpp ToolsSettings.cpp
|
ToolsSettings.hpp ToolsSettings.cpp
|
||||||
|
McpSettings.hpp McpSettings.cpp
|
||||||
SettingsDialog.hpp SettingsDialog.cpp
|
SettingsDialog.hpp SettingsDialog.cpp
|
||||||
ProjectSettings.hpp ProjectSettings.cpp
|
ProjectSettings.hpp ProjectSettings.cpp
|
||||||
ProjectSettingsPanel.hpp ProjectSettingsPanel.cpp
|
ProjectSettingsPanel.hpp ProjectSettingsPanel.cpp
|
||||||
|
|||||||
296
settings/McpSettings.cpp
Normal file
296
settings/McpSettings.cpp
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
// Copyright (C) 2024-2026 Petr Mironychev
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "McpSettings.hpp"
|
||||||
|
|
||||||
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTabWidget>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "SettingsConstants.hpp"
|
||||||
|
#include "SettingsTr.hpp"
|
||||||
|
#include "SettingsUtils.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
McpSettings &mcpSettings()
|
||||||
|
{
|
||||||
|
static McpSettings settings;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
McpSettings::McpSettings()
|
||||||
|
{
|
||||||
|
setAutoApply(false);
|
||||||
|
|
||||||
|
setDisplayName(Tr::tr("MCP"));
|
||||||
|
|
||||||
|
enableMcpServer.setSettingsKey(Constants::MCP_ENABLE_SERVER);
|
||||||
|
enableMcpServer.setLabelText(Tr::tr("Enable MCP server"));
|
||||||
|
enableMcpServer.setToolTip(
|
||||||
|
Tr::tr("Expose QodeAssist tools to external MCP clients over HTTP. "
|
||||||
|
"Which tools are visible is controlled on the client side."));
|
||||||
|
enableMcpServer.setDefaultValue(false);
|
||||||
|
|
||||||
|
mcpServerPort.setSettingsKey(Constants::MCP_SERVER_PORT);
|
||||||
|
mcpServerPort.setLabelText(Tr::tr("Server port"));
|
||||||
|
mcpServerPort.setToolTip(
|
||||||
|
Tr::tr("TCP port the MCP server listens on (localhost only). "
|
||||||
|
"Requires restart of the server after change."));
|
||||||
|
mcpServerPort.setRange(1, 65535);
|
||||||
|
mcpServerPort.setDefaultValue(3456);
|
||||||
|
|
||||||
|
resetToDefaults.m_buttonText = Tr::tr("Reset Page to Defaults");
|
||||||
|
showConnectionInstructions.m_buttonText = Tr::tr("How to connect...");
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
setupConnections();
|
||||||
|
|
||||||
|
setLayouter([this]() {
|
||||||
|
using namespace Layouting;
|
||||||
|
|
||||||
|
return Column{
|
||||||
|
Row{Stretch{1}, resetToDefaults},
|
||||||
|
Space{8},
|
||||||
|
Group{
|
||||||
|
title(Tr::tr("Server")),
|
||||||
|
Column{
|
||||||
|
enableMcpServer,
|
||||||
|
mcpServerPort,
|
||||||
|
Row{Stretch{1}, showConnectionInstructions}}},
|
||||||
|
Stretch{1}};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpSettings::setupConnections()
|
||||||
|
{
|
||||||
|
connect(
|
||||||
|
&resetToDefaults,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&McpSettings::resetSettingsToDefaults);
|
||||||
|
|
||||||
|
connect(
|
||||||
|
&showConnectionInstructions,
|
||||||
|
&ButtonAspect::clicked,
|
||||||
|
this,
|
||||||
|
&McpSettings::showConnectionInstructionsDialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpSettings::resetSettingsToDefaults()
|
||||||
|
{
|
||||||
|
QMessageBox::StandardButton reply;
|
||||||
|
reply = QMessageBox::question(
|
||||||
|
Core::ICore::dialogParent(),
|
||||||
|
Tr::tr("Reset Settings"),
|
||||||
|
Tr::tr("Are you sure you want to reset all settings to default values?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (reply == QMessageBox::Yes) {
|
||||||
|
resetAspect(enableMcpServer);
|
||||||
|
resetAspect(mcpServerPort);
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void McpSettings::showConnectionInstructionsDialog()
|
||||||
|
{
|
||||||
|
const quint16 port = static_cast<quint16>(mcpServerPort.volatileValue());
|
||||||
|
const QString serverUrl = QString("http://127.0.0.1:%1/mcp").arg(port);
|
||||||
|
|
||||||
|
const QString bridgeUrl = QStringLiteral("https://github.com/Palm1r/llmqore/releases");
|
||||||
|
|
||||||
|
const QString directJson = QString(R"({
|
||||||
|
"mcpServers": {
|
||||||
|
"qodeassist": {
|
||||||
|
"type": "sse",
|
||||||
|
"url": "%1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})").arg(serverUrl);
|
||||||
|
|
||||||
|
const QString claudeCodeCmd
|
||||||
|
= QString("claude mcp add --transport sse qodeassist %1").arg(serverUrl);
|
||||||
|
|
||||||
|
const QString vscodeJson = QString(R"({
|
||||||
|
"servers": {
|
||||||
|
"qodeassist": {
|
||||||
|
"type": "sse",
|
||||||
|
"url": "%1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})").arg(serverUrl);
|
||||||
|
|
||||||
|
const QString bridgeJson = QString(R"({
|
||||||
|
"port": 8808,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"mcpServers": {
|
||||||
|
"qodeassist": {
|
||||||
|
"type": "sse",
|
||||||
|
"url": "%1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})").arg(serverUrl);
|
||||||
|
|
||||||
|
const QString claudeDesktopJson = QStringLiteral(R"({
|
||||||
|
"mcpServers": {
|
||||||
|
"qodeassist": {
|
||||||
|
"command": "<path-to>/mcp-bridge",
|
||||||
|
"args": ["--stdio", "<path-to>/mcp-bridge.json"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})");
|
||||||
|
|
||||||
|
auto *dialog = new QDialog(Core::ICore::dialogParent());
|
||||||
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
dialog->setWindowTitle(Tr::tr("Connect to QodeAssist MCP"));
|
||||||
|
dialog->resize(640, 480);
|
||||||
|
|
||||||
|
auto *intro = new QLabel(
|
||||||
|
Tr::tr("Server URL: <code>%1</code>. If your MCP client speaks HTTP/SSE "
|
||||||
|
"natively, use the <b>Direct</b> tab. If it only speaks stdio "
|
||||||
|
"(e.g. Claude Desktop), use the <b>Bridge</b> tab.")
|
||||||
|
.arg(serverUrl),
|
||||||
|
dialog);
|
||||||
|
intro->setWordWrap(true);
|
||||||
|
intro->setTextFormat(Qt::RichText);
|
||||||
|
|
||||||
|
auto makeCodeBlock = [](QWidget *parent, const QString &text) -> QWidget * {
|
||||||
|
auto *container = new QWidget(parent);
|
||||||
|
auto *layout = new QVBoxLayout(container);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
auto *edit = new QPlainTextEdit(text, container);
|
||||||
|
edit->setReadOnly(true);
|
||||||
|
edit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
|
||||||
|
edit->setLineWrapMode(QPlainTextEdit::NoWrap);
|
||||||
|
edit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
|
edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||||
|
|
||||||
|
const int lines = text.count(QLatin1Char('\n')) + 1;
|
||||||
|
const QFontMetrics fm(edit->font());
|
||||||
|
const int contentHeight = lines * fm.lineSpacing();
|
||||||
|
const int frame = 2 * edit->frameWidth();
|
||||||
|
edit->setFixedHeight(contentHeight + frame + 8);
|
||||||
|
|
||||||
|
auto *copyBtn = new QPushButton(Tr::tr("Copy"), container);
|
||||||
|
copyBtn->setAutoDefault(false);
|
||||||
|
copyBtn->setDefault(false);
|
||||||
|
QObject::connect(copyBtn, &QPushButton::clicked, container, [edit] {
|
||||||
|
QApplication::clipboard()->setText(edit->toPlainText());
|
||||||
|
});
|
||||||
|
|
||||||
|
auto *btnRow = new QHBoxLayout();
|
||||||
|
btnRow->addStretch(1);
|
||||||
|
btnRow->addWidget(copyBtn);
|
||||||
|
|
||||||
|
layout->addWidget(edit);
|
||||||
|
layout->addLayout(btnRow);
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto *tabs = new QTabWidget(dialog);
|
||||||
|
|
||||||
|
// Direct tab
|
||||||
|
{
|
||||||
|
auto *tab = new QWidget(tabs);
|
||||||
|
auto *layout = new QVBoxLayout(tab);
|
||||||
|
|
||||||
|
auto *hint = new QLabel(Tr::tr("<b>Claude Code</b> (CLI): run once —"), tab);
|
||||||
|
hint->setTextFormat(Qt::RichText);
|
||||||
|
layout->addWidget(hint);
|
||||||
|
layout->addWidget(makeCodeBlock(tab, claudeCodeCmd));
|
||||||
|
|
||||||
|
auto *vscodeHint = new QLabel(
|
||||||
|
Tr::tr("<b>VS Code</b>: save as <code>.vscode/mcp.json</code> in the workspace:"),
|
||||||
|
tab);
|
||||||
|
vscodeHint->setTextFormat(Qt::RichText);
|
||||||
|
vscodeHint->setWordWrap(true);
|
||||||
|
layout->addWidget(vscodeHint);
|
||||||
|
layout->addWidget(makeCodeBlock(tab, vscodeJson));
|
||||||
|
|
||||||
|
auto *jsonHint = new QLabel(
|
||||||
|
Tr::tr("Any other client that reads an <code>mcpServers</code> JSON block:"),
|
||||||
|
tab);
|
||||||
|
jsonHint->setTextFormat(Qt::RichText);
|
||||||
|
jsonHint->setWordWrap(true);
|
||||||
|
layout->addWidget(jsonHint);
|
||||||
|
layout->addWidget(makeCodeBlock(tab, directJson));
|
||||||
|
layout->addStretch(1);
|
||||||
|
|
||||||
|
tabs->addTab(tab, Tr::tr("Direct (HTTP/SSE)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bridge tab
|
||||||
|
{
|
||||||
|
auto *tab = new QWidget(tabs);
|
||||||
|
auto *layout = new QVBoxLayout(tab);
|
||||||
|
|
||||||
|
auto *step1 = new QLabel(
|
||||||
|
Tr::tr("<b>1.</b> Download <code>mcp-bridge</code> for your OS from "
|
||||||
|
"<a href=\"%1\">%1</a>.")
|
||||||
|
.arg(bridgeUrl),
|
||||||
|
tab);
|
||||||
|
step1->setTextFormat(Qt::RichText);
|
||||||
|
step1->setWordWrap(true);
|
||||||
|
step1->setOpenExternalLinks(true);
|
||||||
|
layout->addWidget(step1);
|
||||||
|
|
||||||
|
auto *step2 = new QLabel(
|
||||||
|
Tr::tr("<b>2.</b> Save the following as <code>mcp-bridge.json</code>:"), tab);
|
||||||
|
step2->setTextFormat(Qt::RichText);
|
||||||
|
layout->addWidget(step2);
|
||||||
|
layout->addWidget(makeCodeBlock(tab, bridgeJson));
|
||||||
|
|
||||||
|
auto *step3 = new QLabel(
|
||||||
|
Tr::tr("<b>3.</b> Point the stdio-only client at the bridge. "
|
||||||
|
"Example for <code>claude_desktop_config.json</code>:"),
|
||||||
|
tab);
|
||||||
|
step3->setTextFormat(Qt::RichText);
|
||||||
|
step3->setWordWrap(true);
|
||||||
|
layout->addWidget(step3);
|
||||||
|
layout->addWidget(makeCodeBlock(tab, claudeDesktopJson));
|
||||||
|
layout->addStretch(1);
|
||||||
|
|
||||||
|
tabs->addTab(tab, Tr::tr("Bridge (stdio)"));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *buttons = new QDialogButtonBox(QDialogButtonBox::Close, dialog);
|
||||||
|
QObject::connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::close);
|
||||||
|
|
||||||
|
auto *layout = new QVBoxLayout(dialog);
|
||||||
|
layout->addWidget(intro);
|
||||||
|
layout->addWidget(tabs, 1);
|
||||||
|
layout->addWidget(buttons);
|
||||||
|
|
||||||
|
dialog->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
class McpSettingsPage : public Core::IOptionsPage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McpSettingsPage()
|
||||||
|
{
|
||||||
|
setId(Constants::QODE_ASSIST_MCP_SETTINGS_PAGE_ID);
|
||||||
|
setDisplayName(Tr::tr("MCP"));
|
||||||
|
setCategory(Constants::QODE_ASSIST_GENERAL_OPTIONS_CATEGORY);
|
||||||
|
setSettingsProvider([] { return &mcpSettings(); });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const McpSettingsPage mcpSettingsPage;
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
32
settings/McpSettings.hpp
Normal file
32
settings/McpSettings.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2024-2026 Petr Mironychev
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/aspects.h>
|
||||||
|
|
||||||
|
#include "ButtonAspect.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Settings {
|
||||||
|
|
||||||
|
class McpSettings : public Utils::AspectContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McpSettings();
|
||||||
|
|
||||||
|
ButtonAspect resetToDefaults{this};
|
||||||
|
|
||||||
|
Utils::BoolAspect enableMcpServer{this};
|
||||||
|
Utils::IntegerAspect mcpServerPort{this};
|
||||||
|
|
||||||
|
ButtonAspect showConnectionInstructions{this};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupConnections();
|
||||||
|
void resetSettingsToDefaults();
|
||||||
|
void showConnectionInstructionsDialog();
|
||||||
|
};
|
||||||
|
|
||||||
|
McpSettings &mcpSettings();
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Settings
|
||||||
@@ -100,6 +100,10 @@ const char CA_ALLOWED_TERMINAL_COMMANDS_MACOS[] = "QodeAssist.caAllowedTerminalC
|
|||||||
const char CA_ALLOWED_TERMINAL_COMMANDS_WINDOWS[] = "QodeAssist.caAllowedTerminalCommandsWindows";
|
const char CA_ALLOWED_TERMINAL_COMMANDS_WINDOWS[] = "QodeAssist.caAllowedTerminalCommandsWindows";
|
||||||
const char CA_TERMINAL_COMMAND_TIMEOUT[] = "QodeAssist.caTerminalCommandTimeout";
|
const char CA_TERMINAL_COMMAND_TIMEOUT[] = "QodeAssist.caTerminalCommandTimeout";
|
||||||
|
|
||||||
|
// MCP server settings
|
||||||
|
const char MCP_ENABLE_SERVER[] = "QodeAssist.mcpEnableServer";
|
||||||
|
const char MCP_SERVER_PORT[] = "QodeAssist.mcpServerPort";
|
||||||
|
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
||||||
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
|
const char QODE_ASSIST_GENERAL_SETTINGS_PAGE_ID[] = "QodeAssist.1GeneralSettingsPageId";
|
||||||
const char QODE_ASSIST_CODE_COMPLETION_SETTINGS_PAGE_ID[]
|
const char QODE_ASSIST_CODE_COMPLETION_SETTINGS_PAGE_ID[]
|
||||||
@@ -109,6 +113,7 @@ const char QODE_ASSIST_CHAT_ASSISTANT_SETTINGS_PAGE_ID[]
|
|||||||
const char QODE_ASSIST_QUICK_REFACTOR_SETTINGS_PAGE_ID[]
|
const char QODE_ASSIST_QUICK_REFACTOR_SETTINGS_PAGE_ID[]
|
||||||
= "QodeAssist.4QuickRefactorSettingsPageId";
|
= "QodeAssist.4QuickRefactorSettingsPageId";
|
||||||
const char QODE_ASSIST_TOOLS_SETTINGS_PAGE_ID[] = "QodeAssist.5ToolsSettingsPageId";
|
const char QODE_ASSIST_TOOLS_SETTINGS_PAGE_ID[] = "QodeAssist.5ToolsSettingsPageId";
|
||||||
|
const char QODE_ASSIST_MCP_SETTINGS_PAGE_ID[] = "QodeAssist.6McpSettingsPageId";
|
||||||
|
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
|
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "QodeAssist";
|
const char QODE_ASSIST_GENERAL_OPTIONS_DISPLAY_CATEGORY[] = "QodeAssist";
|
||||||
|
|||||||
Reference in New Issue
Block a user