Add MCP server

This commit is contained in:
Terrence
2025-05-22 19:19:36 +08:00
parent f142c5469c
commit 5da66773d5
23 changed files with 845 additions and 41 deletions

View File

@@ -182,17 +182,7 @@ bool MqttProtocol::OpenAudioChannel() {
session_id_ = "";
xEventGroupClearBits(event_group_handle_, MQTT_PROTOCOL_SERVER_HELLO_EVENT);
// 发送 hello 消息申请 UDP 通道
std::string message = "{";
message += "\"type\":\"hello\",";
message += "\"version\": 3,";
message += "\"transport\":\"udp\",";
#if CONFIG_USE_SERVER_AEC
message += "\"features\":{\"aec\":true},";
#endif
message += "\"audio_params\":{";
message += "\"format\":\"opus\", \"sample_rate\":16000, \"channels\":1, \"frame_duration\":" + std::to_string(OPUS_FRAME_DURATION_MS);
message += "}}";
auto message = GetHelloMessage();
if (!SendText(message)) {
return false;
}
@@ -262,6 +252,33 @@ bool MqttProtocol::OpenAudioChannel() {
return true;
}
std::string MqttProtocol::GetHelloMessage() {
// 发送 hello 消息申请 UDP 通道
cJSON* root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "hello");
cJSON_AddNumberToObject(root, "version", 3);
cJSON_AddStringToObject(root, "transport", "udp");
cJSON* features = cJSON_CreateObject();
#if CONFIG_USE_SERVER_AEC
cJSON_AddBoolToObject(features, "aec", true);
#endif
#if CONFIG_IOT_PROTOCOL_MCP
cJSON_AddBoolToObject(features, "mcp", true);
#endif
cJSON_AddItemToObject(root, "features", features);
cJSON* audio_params = cJSON_CreateObject();
cJSON_AddStringToObject(audio_params, "format", "opus");
cJSON_AddNumberToObject(audio_params, "sample_rate", 16000);
cJSON_AddNumberToObject(audio_params, "channels", 1);
cJSON_AddNumberToObject(audio_params, "frame_duration", OPUS_FRAME_DURATION_MS);
cJSON_AddItemToObject(root, "audio_params", audio_params);
auto json_str = cJSON_PrintUnformatted(root);
std::string message(json_str);
cJSON_free(json_str);
cJSON_Delete(root);
return message;
}
void MqttProtocol::ParseServerHello(const cJSON* root) {
auto transport = cJSON_GetObjectItem(root, "transport");
if (transport == nullptr || strcmp(transport->valuestring, "udp") != 0) {

View File

@@ -51,6 +51,7 @@ private:
std::string DecodeHexString(const std::string& hex_string);
bool SendText(const std::string& text) override;
std::string GetHelloMessage();
};

View File

@@ -115,6 +115,11 @@ void Protocol::SendIotStates(const std::string& states) {
SendText(message);
}
void Protocol::SendMcpMessage(const std::string& payload) {
std::string message = "{\"session_id\":\"" + session_id_ + "\",\"type\":\"mcp\",\"payload\":" + payload + "}";
SendText(message);
}
bool Protocol::IsTimeout() const {
const int kTimeoutSeconds = 120;
auto now = std::chrono::steady_clock::now();

View File

@@ -71,6 +71,7 @@ public:
virtual void SendAbortSpeaking(AbortReason reason);
virtual void SendIotDescriptors(const std::string& descriptors);
virtual void SendIotStates(const std::string& states);
virtual void SendMcpMessage(const std::string& message);
protected:
std::function<void(const cJSON* root)> on_incoming_json_;

View File

@@ -180,22 +180,12 @@ bool WebsocketProtocol::OpenAudioChannel() {
ESP_LOGI(TAG, "Connecting to websocket server: %s with version: %d", url.c_str(), version_);
if (!websocket_->Connect(url.c_str())) {
ESP_LOGE(TAG, "Failed to connect to websocket server");
SetError(Lang::Strings::SERVER_NOT_FOUND);
SetError(Lang::Strings::SERVER_NOT_CONNECTED);
return false;
}
// Send hello message to describe the client
// keys: message type, version, audio_params (format, sample_rate, channels)
std::string message = "{";
message += "\"type\":\"hello\",";
message += "\"version\": " + std::to_string(version_) + ",";
#if CONFIG_USE_SERVER_AEC
message += "\"features\":{\"aec\":true},";
#endif
message += "\"transport\":\"websocket\",";
message += "\"audio_params\":{";
message += "\"format\":\"opus\", \"sample_rate\":16000, \"channels\":1, \"frame_duration\":" + std::to_string(OPUS_FRAME_DURATION_MS);
message += "}}";
auto message = GetHelloMessage();
if (!SendText(message)) {
return false;
}
@@ -215,6 +205,33 @@ bool WebsocketProtocol::OpenAudioChannel() {
return true;
}
std::string WebsocketProtocol::GetHelloMessage() {
// keys: message type, version, audio_params (format, sample_rate, channels)
cJSON* root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", "hello");
cJSON_AddNumberToObject(root, "version", version_);
cJSON* features = cJSON_CreateObject();
#if CONFIG_USE_SERVER_AEC
cJSON_AddBoolToObject(features, "aec", true);
#endif
#if CONFIG_IOT_PROTOCOL_MCP
cJSON_AddBoolToObject(features, "mcp", true);
#endif
cJSON_AddItemToObject(root, "features", features);
cJSON_AddStringToObject(root, "transport", "websocket");
cJSON* audio_params = cJSON_CreateObject();
cJSON_AddStringToObject(audio_params, "format", "opus");
cJSON_AddNumberToObject(audio_params, "sample_rate", 16000);
cJSON_AddNumberToObject(audio_params, "channels", 1);
cJSON_AddNumberToObject(audio_params, "frame_duration", OPUS_FRAME_DURATION_MS);
cJSON_AddItemToObject(root, "audio_params", audio_params);
auto json_str = cJSON_PrintUnformatted(root);
std::string message(json_str);
cJSON_free(json_str);
cJSON_Delete(root);
return message;
}
void WebsocketProtocol::ParseServerHello(const cJSON* root) {
auto transport = cJSON_GetObjectItem(root, "transport");
if (transport == nullptr || strcmp(transport->valuestring, "websocket") != 0) {

View File

@@ -28,6 +28,7 @@ private:
void ParseServerHello(const cJSON* root);
bool SendText(const std::string& text) override;
std::string GetHelloMessage();
};
#endif