+ );
+}
diff --git a/apps/desktop/src/locales/en/messages.po b/apps/desktop/src/locales/en/messages.po
index 5ced097c29..ed38435eae 100644
--- a/apps/desktop/src/locales/en/messages.po
+++ b/apps/desktop/src/locales/en/messages.po
@@ -469,7 +469,7 @@ msgstr "Base URL"
msgid "Built-in Templates"
msgstr "Built-in Templates"
-#: src/routes/app.settings.tsx:58
+#: src/routes/app.settings.tsx:59
msgid "Calendar"
msgstr "Calendar"
@@ -795,8 +795,8 @@ msgstr "Enter the base URL for your custom LLM endpoint"
msgid "Extract action items"
msgstr "Extract action items"
-#: src/routes/app.settings.tsx:68
-#: src/routes/app.settings.tsx:103
+#: src/routes/app.settings.tsx:69
+#: src/routes/app.settings.tsx:106
msgid "Feedback"
msgstr "Feedback"
@@ -832,7 +832,7 @@ msgstr "Finish Onboarding"
msgid "Full name"
msgstr "Full name"
-#: src/routes/app.settings.tsx:52
+#: src/routes/app.settings.tsx:53
msgid "General"
msgstr "General"
@@ -900,12 +900,12 @@ msgstr "Important Q&As"
#~ msgid "Integration with other apps like Notion and Google Calendar"
#~ msgstr "Integration with other apps like Notion and Google Calendar"
-#: src/routes/app.settings.tsx:66
+#: src/routes/app.settings.tsx:67
#: src/components/settings/views/integrations.tsx:124
msgid "Integrations"
msgstr "Integrations"
-#: src/routes/app.settings.tsx:54
+#: src/routes/app.settings.tsx:55
msgid "Intelligence"
msgstr "Intelligence"
@@ -965,7 +965,7 @@ msgstr "Learn more about AI autonomy"
msgid "Learn more about templates"
msgstr "Learn more about templates"
-#: src/routes/app.settings.tsx:70
+#: src/routes/app.settings.tsx:71
msgid "License"
msgstr "License"
@@ -1026,6 +1026,10 @@ msgstr "Loading..."
msgid "Make it public"
msgstr "Make it public"
+#: src/routes/app.settings.tsx:73
+msgid "MCP"
+msgstr "MCP"
+
#: src/components/settings/views/billing.tsx:41
#~ msgid "Meeting note sharing via links"
#~ msgstr "Meeting note sharing via links"
@@ -1147,7 +1151,7 @@ msgstr "No upcoming events for this organization"
msgid "No upcoming events with this contact"
msgstr "No upcoming events with this contact"
-#: src/routes/app.settings.tsx:60
+#: src/routes/app.settings.tsx:61
msgid "Notifications"
msgstr "Notifications"
@@ -1424,7 +1428,7 @@ msgstr "Show notifications when you join a meeting."
msgid "Some downloads failed, but you can continue"
msgstr "Some downloads failed, but you can continue"
-#: src/routes/app.settings.tsx:64
+#: src/routes/app.settings.tsx:65
msgid "Sound"
msgstr "Sound"
@@ -1501,7 +1505,7 @@ msgstr "Teamspace"
msgid "Template"
msgstr "Template"
-#: src/routes/app.settings.tsx:62
+#: src/routes/app.settings.tsx:63
msgid "Templates"
msgstr "Templates"
@@ -1565,7 +1569,7 @@ msgstr "Toggle transcript panel"
#~ msgid "Transcribing"
#~ msgstr "Transcribing"
-#: src/routes/app.settings.tsx:56
+#: src/routes/app.settings.tsx:57
msgid "Transcription"
msgstr "Transcription"
diff --git a/apps/desktop/src/locales/ko/messages.po b/apps/desktop/src/locales/ko/messages.po
index 331ff92e74..74f6bb66ac 100644
--- a/apps/desktop/src/locales/ko/messages.po
+++ b/apps/desktop/src/locales/ko/messages.po
@@ -469,7 +469,7 @@ msgstr ""
msgid "Built-in Templates"
msgstr ""
-#: src/routes/app.settings.tsx:58
+#: src/routes/app.settings.tsx:59
msgid "Calendar"
msgstr ""
@@ -795,8 +795,8 @@ msgstr ""
msgid "Extract action items"
msgstr ""
-#: src/routes/app.settings.tsx:68
-#: src/routes/app.settings.tsx:103
+#: src/routes/app.settings.tsx:69
+#: src/routes/app.settings.tsx:106
msgid "Feedback"
msgstr ""
@@ -832,7 +832,7 @@ msgstr ""
msgid "Full name"
msgstr ""
-#: src/routes/app.settings.tsx:52
+#: src/routes/app.settings.tsx:53
msgid "General"
msgstr ""
@@ -900,12 +900,12 @@ msgstr ""
#~ msgid "Integration with other apps like Notion and Google Calendar"
#~ msgstr ""
-#: src/routes/app.settings.tsx:66
+#: src/routes/app.settings.tsx:67
#: src/components/settings/views/integrations.tsx:124
msgid "Integrations"
msgstr ""
-#: src/routes/app.settings.tsx:54
+#: src/routes/app.settings.tsx:55
msgid "Intelligence"
msgstr ""
@@ -965,7 +965,7 @@ msgstr ""
msgid "Learn more about templates"
msgstr ""
-#: src/routes/app.settings.tsx:70
+#: src/routes/app.settings.tsx:71
msgid "License"
msgstr ""
@@ -1026,6 +1026,10 @@ msgstr ""
msgid "Make it public"
msgstr ""
+#: src/routes/app.settings.tsx:73
+msgid "MCP"
+msgstr ""
+
#: src/components/settings/views/billing.tsx:41
#~ msgid "Meeting note sharing via links"
#~ msgstr ""
@@ -1147,7 +1151,7 @@ msgstr ""
msgid "No upcoming events with this contact"
msgstr ""
-#: src/routes/app.settings.tsx:60
+#: src/routes/app.settings.tsx:61
msgid "Notifications"
msgstr ""
@@ -1424,7 +1428,7 @@ msgstr ""
msgid "Some downloads failed, but you can continue"
msgstr ""
-#: src/routes/app.settings.tsx:64
+#: src/routes/app.settings.tsx:65
msgid "Sound"
msgstr ""
@@ -1501,7 +1505,7 @@ msgstr ""
msgid "Template"
msgstr ""
-#: src/routes/app.settings.tsx:62
+#: src/routes/app.settings.tsx:63
msgid "Templates"
msgstr ""
@@ -1565,7 +1569,7 @@ msgstr ""
#~ msgid "Transcribing"
#~ msgstr ""
-#: src/routes/app.settings.tsx:56
+#: src/routes/app.settings.tsx:57
msgid "Transcription"
msgstr ""
diff --git a/apps/desktop/src/routes/app.settings.tsx b/apps/desktop/src/routes/app.settings.tsx
index 4c225fef1a..b4001dbf7d 100644
--- a/apps/desktop/src/routes/app.settings.tsx
+++ b/apps/desktop/src/routes/app.settings.tsx
@@ -14,6 +14,7 @@ import {
Calendar,
General,
Integrations,
+ MCP,
Notifications,
Sound,
TemplatesView,
@@ -68,6 +69,8 @@ function Component() {
return t`Feedback`;
case "billing":
return t`License`;
+ case "mcp":
+ return t`MCP`;
default:
return tab;
}
@@ -135,6 +138,7 @@ function Component() {
{search.tab === "ai-llm" && }
{search.tab === "templates" && }
{search.tab === "integrations" && }
+ {search.tab === "mcp" && }
{search.tab === "billing" && }
diff --git a/apps/desktop/src/utils/broadcast.ts b/apps/desktop/src/utils/broadcast.ts
index 21c135eed4..69b557c29e 100644
--- a/apps/desktop/src/utils/broadcast.ts
+++ b/apps/desktop/src/utils/broadcast.ts
@@ -74,6 +74,12 @@ export function broadcastQueryClient(queryClient: QueryClient) {
queryKey: ["org", keys[1]],
});
}
+
+ if (keys[0] === "mcp-tools") {
+ queryClient.invalidateQueries({
+ queryKey: ["mcp-tools"],
+ });
+ }
});
};
diff --git a/crates/db-user/src/chat_messages_migration_1.sql b/crates/db-user/src/chat_messages_migration_1.sql
new file mode 100644
index 0000000000..7dfe274d02
--- /dev/null
+++ b/crates/db-user/src/chat_messages_migration_1.sql
@@ -0,0 +1,4 @@
+ALTER TABLE
+ chat_messages
+ADD
+ COLUMN type TEXT DEFAULT 'text-delta';
diff --git a/crates/db-user/src/chat_messages_ops.rs b/crates/db-user/src/chat_messages_ops.rs
index a82c3ab509..560eda9a68 100644
--- a/crates/db-user/src/chat_messages_ops.rs
+++ b/crates/db-user/src/chat_messages_ops.rs
@@ -14,8 +14,9 @@ impl UserDatabase {
group_id,
created_at,
role,
- content
- ) VALUES (?, ?, ?, ?, ?)
+ content,
+ type
+ ) VALUES (?, ?, ?, ?, ?, ?)
RETURNING *",
vec![
message.id,
@@ -23,6 +24,7 @@ impl UserDatabase {
message.created_at.to_rfc3339(),
message.role.to_string(),
message.content,
+ message.r#type.to_string(),
],
)
.await?;
diff --git a/crates/db-user/src/chat_messages_types.rs b/crates/db-user/src/chat_messages_types.rs
index 68b0177c96..1665fda4ea 100644
--- a/crates/db-user/src/chat_messages_types.rs
+++ b/crates/db-user/src/chat_messages_types.rs
@@ -8,6 +8,24 @@ user_common_derives! {
}
}
+user_common_derives! {
+ #[derive(strum::EnumString, strum::Display)]
+ pub enum ChatMessageType {
+ #[serde(rename = "text-delta")]
+ #[strum(serialize = "text-delta")]
+ TextDelta,
+ #[serde(rename = "tool-start")]
+ #[strum(serialize = "tool-start")]
+ ToolStart,
+ #[serde(rename = "tool-result")]
+ #[strum(serialize = "tool-result")]
+ ToolResult,
+ #[serde(rename = "tool-error")]
+ #[strum(serialize = "tool-error")]
+ ToolError,
+ }
+}
+
user_common_derives! {
pub struct ChatMessage {
pub id: String,
@@ -15,5 +33,6 @@ user_common_derives! {
pub created_at: chrono::DateTime,
pub role: ChatMessageRole,
pub content: String,
+ pub r#type: ChatMessageType,
}
}
diff --git a/crates/db-user/src/init.rs b/crates/db-user/src/init.rs
index 458b9a82ed..b6d8a1f2a7 100644
--- a/crates/db-user/src/init.rs
+++ b/crates/db-user/src/init.rs
@@ -1,8 +1,8 @@
use crate::{Config, ConfigAI, ConfigGeneral, ConfigNotification};
use super::{
- Calendar, ChatGroup, ChatMessage, ChatMessageRole, Event, Human, Organization, Platform,
- Session, Tag, UserDatabase,
+ Calendar, ChatGroup, ChatMessage, ChatMessageRole, ChatMessageType, Event, Human, Organization,
+ Platform, Session, Tag, UserDatabase,
};
const ONBOARDING_RAW_HTML: &str = include_str!("../assets/onboarding-raw.html");
@@ -794,6 +794,7 @@ pub async fn seed(db: &UserDatabase, user_id: impl Into) -> Result<(), c
role: ChatMessageRole::User,
content: "Hello, how are you?".to_string(),
created_at: now,
+ r#type: ChatMessageType::TextDelta,
};
let _ = db.upsert_chat_message(chat_message_1).await?;
diff --git a/crates/db-user/src/lib.rs b/crates/db-user/src/lib.rs
index 55507b9edb..abb73c2056 100644
--- a/crates/db-user/src/lib.rs
+++ b/crates/db-user/src/lib.rs
@@ -129,7 +129,7 @@ impl std::ops::Deref for UserDatabase {
}
// Append only. Do not reorder.
-const MIGRATIONS: [&str; 22] = [
+const MIGRATIONS: [&str; 23] = [
include_str!("./calendars_migration.sql"),
include_str!("./configs_migration.sql"),
include_str!("./events_migration.sql"),
@@ -152,6 +152,7 @@ const MIGRATIONS: [&str; 22] = [
include_str!("./events_migration_1.sql"),
include_str!("./session_participants_migration_1.sql"),
include_str!("./events_migration_2.sql"),
+ include_str!("./chat_messages_migration_1.sql"),
];
pub async fn migrate(db: &UserDatabase) -> Result<(), crate::Error> {
diff --git a/crates/template/assets/ai_chat_system.jinja b/crates/template/assets/ai_chat_system.jinja
index db68957d67..e865274836 100644
--- a/crates/template/assets/ai_chat_system.jinja
+++ b/crates/template/assets/ai_chat_system.jinja
@@ -144,10 +144,17 @@ Your response would mostly be either of the two formats:
"
-{% if modelId == "gpt-4.1" or modelId == "openai/gpt-4.1" or modelId == "anthropic/claude-4-sonnet" or modelId == "openai/gpt-4o" or modelId == "gpt-4o"%}
+{% if modelId == "gpt-4.1" or modelId == "openai/gpt-4.1" or modelId == "anthropic/claude-sonnet-4" or modelId == "openai/gpt-4o" or modelId == "gpt-4o" or (apiBase and "pro.hyprnote.com" in apiBase)%}
[Tool Calling]
Here are available tools that you can call to get more information.
-{important} not all models have access to all tools. If you can't call a tool, tell user that your model doesn't have access, so try switching to a different model like gpt-4.1 or claude 4 sonnet.
+
+- custom MCP tools: user will be able to request you to call MCP tools. Often times, they are related to other producitvity tools like Slack, Notion, Google workspace, etc.
+ Below is the list of tools that you have access to.
+
+{% for tool in mcpTools %}
+
+- {{ tool.name }}: {{ tool.description }}. Input schema is {{ tool.inputSchema }}.
+ {% endfor %}
- search_sessions_multi_keywords: Search for sessions (meeting notes) with multiple keywords. The keywords should be the most important things that the user is talking about. This could be either topics, people, or company names.
when you return the response for a request that used search_sessions_multi_keywords, you should smartly combined the information from the tool call results, instead of naively returning all raw results (oftentime thses could be very long).
diff --git a/crates/template/assets/enhance.system.jinja b/crates/template/assets/enhance.system.jinja
index 4fa1aa3698..add87dab0c 100644
--- a/crates/template/assets/enhance.system.jinja
+++ b/crates/template/assets/enhance.system.jinja
@@ -35,6 +35,7 @@ The sections and their order in your output must exactly match those defined in
Sections in the template should be the only existing sections in the enhanced note, as markdown 1 headers h1 (#).
Each section's content must strictly adhere to the section description provided by the user. Extract and organize relevant information from the raw notes and transcript to fulfill each section's requirements.
CRITICAL: Only include information that exists in the source material. If the transcript or raw notes do not contain information relevant to a specific section, explicitly say so. Do not generate, infer, or create content that is not directly supported by the source material.
+CRITICAL: Do not include any of your thought process or comments. Just return the end result of the enhanced note, do not explain or justify your results (regardless of whether template is given or not)
---Template Structure---
{{ templateInfo }}
@@ -81,7 +82,7 @@ You will be given multiple inputs from the user. Below are useful information th
- Disclaimer: Raw notes and the transcript may contain errors made by human and STT, respectively. So it is important you make the best out of every material to create the best enhanced meeting note.
- Do not include meeting note title, attendee lists nor explanatory notes about the output structure. Just print a markdown document.
-- Go straight to the actual note part, there is no need to include any of your thought process or comments. Just return the meeting note.
+- Go straight to the actual note part, never include any of your thought process or comments. Just return the meeting note, do not explain or justify your results (regardless of whether template is given or not)
- It is super important to acknowledge what the user found to be important, and raw notes show a glimpse of the important information as well as moments during the meeting. Naturally integrate raw note entries into relevant sections instead of forcefully converting them into headers.
- Preserve essential details; avoid excessive abstraction. Ensure content remains concrete and specific.
- Pay close attention to emphasized text in raw notes. Users highlight information using four styles: bold(**text**), italic(_text_), underline(text), strikethrough(~~text~~).
diff --git a/packages/utils/src/ai.ts b/packages/utils/src/ai.ts
index dd1e854de4..85ecdfd0b9 100644
--- a/packages/utils/src/ai.ts
+++ b/packages/utils/src/ai.ts
@@ -5,7 +5,17 @@ import { getLicenseKey } from "tauri-plugin-keygen-api";
import { commands as connectorCommands } from "@hypr/plugin-connector";
import { fetch as customFetch } from "@hypr/utils";
-export { generateObject, generateText, type Provider, smoothStream, stepCountIs, streamText, tool } from "ai";
+export {
+ dynamicTool,
+ experimental_createMCPClient,
+ generateObject,
+ generateText,
+ type Provider,
+ smoothStream,
+ stepCountIs,
+ streamText,
+ tool,
+} from "ai";
export const localProviderName = "hypr-llm-local";
export const remoteProviderName = "hypr-llm-remote";
diff --git a/plugins/db/js/bindings.gen.ts b/plugins/db/js/bindings.gen.ts
index 0dc0e540c0..e38f4ee566 100644
--- a/plugins/db/js/bindings.gen.ts
+++ b/plugins/db/js/bindings.gen.ts
@@ -162,8 +162,9 @@ async deleteTag(tagId: string) : Promise {
export type Calendar = { id: string; tracking_id: string; user_id: string; platform: Platform; name: string; selected: boolean; source: string | null }
export type ChatGroup = { id: string; user_id: string; name: string | null; created_at: string; session_id: string }
-export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string }
+export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string; type: ChatMessageType }
export type ChatMessageRole = "User" | "Assistant"
+export type ChatMessageType = "text-delta" | "tool-start" | "tool-result" | "tool-error"
export type Config = { id: string; user_id: string; general: ConfigGeneral; notification: ConfigNotification; ai: ConfigAI }
export type ConfigAI = { api_base: string | null; api_key: string | null; ai_specificity: number | null; redemption_time_ms: number | null }
export type ConfigGeneral = { autostart: boolean; display_language: string; spoken_languages?: string[]; jargons?: string[]; telemetry_consent: boolean; save_recordings: boolean | null; selected_template_id: string | null; summary_language?: string }