-
Notifications
You must be signed in to change notification settings - Fork 5
AT-360 Added Diagram Chat Functionality To Mermaid/sdk So MCP Server Can Directly Access Mermaid AI #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
AT-360 Added Diagram Chat Functionality To Mermaid/sdk So MCP Server Can Directly Access Mermaid AI #43
Changes from all commits
94c459b
7578448
7d76908
a13e21c
908ecab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -10,6 +10,8 @@ import { | |||||
| import type { | ||||||
| AuthState, | ||||||
| AuthorizationData, | ||||||
| DiagramChatRequest, | ||||||
| DiagramChatResponse, | ||||||
| Document, | ||||||
| InitParams, | ||||||
| MCDocument, | ||||||
|
|
@@ -24,6 +26,63 @@ import { URLS } from './urls.js'; | |||||
| const defaultBaseURL = 'https://www.mermaid.ai'; // "http://127.0.0.1:5174" | ||||||
| const authorizationURLTimeout = 60_000; | ||||||
|
|
||||||
| /** | ||||||
| * Parses text tokens from a Vercel AI SDK data-stream response body. | ||||||
| * | ||||||
| * The stream format uses line prefixes: | ||||||
| * `0:"text_chunk"\n` – text token (JSON-encoded string) | ||||||
| * `2:[{"documentChatThreadID":"thread-abc-123"}]\n` – data payload (JSON-encoded array) | ||||||
| * `e:{...}\n` – step finish | ||||||
| * `d:{...}\n` – stream done | ||||||
| */ | ||||||
| function parseVercelAIStreamText(rawBody: string): string { | ||||||
| return rawBody | ||||||
| .split('\n') | ||||||
| .filter((line) => line.startsWith('0:')) | ||||||
| .map((line) => { | ||||||
| try { | ||||||
| const value = JSON.parse(line.slice(2)); | ||||||
| return typeof value === 'string' ? value : ''; | ||||||
| } catch { | ||||||
| return ''; | ||||||
| } | ||||||
| }) | ||||||
| .join(''); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Extracts data payloads from a Vercel AI SDK data-stream response body. | ||||||
| * Returns the first `documentChatThreadID` found in the stream, if any. | ||||||
| */ | ||||||
| function parseVercelAIStreamData(rawBody: string): { documentChatThreadID?: string } { | ||||||
| let documentChatThreadID: string | undefined; | ||||||
|
|
||||||
| for (const line of rawBody.split('\n')) { | ||||||
| if (!line.startsWith('2:')) { | ||||||
| continue; | ||||||
| } | ||||||
| try { | ||||||
| const items: unknown[] = JSON.parse(line.slice(2)); | ||||||
| for (const item of items) { | ||||||
| if (item && typeof item === 'object' && 'documentChatThreadID' in item) { | ||||||
| const value = (item as Record<string, unknown>).documentChatThreadID; | ||||||
| if (typeof value === 'string') { | ||||||
| documentChatThreadID = value; | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } catch { | ||||||
| // ignore malformed lines | ||||||
| } | ||||||
| if (documentChatThreadID) { | ||||||
| break; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return { documentChatThreadID }; | ||||||
| } | ||||||
|
|
||||||
| export class MermaidChart { | ||||||
| private clientID: string; | ||||||
| #baseURL!: string; | ||||||
|
|
@@ -319,4 +378,79 @@ export class MermaidChart { | |||||
| throw error; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Chat with Mermaid AI about a diagram. | ||||||
| * | ||||||
| * Sends a single user message to the Mermaid AI chat endpoint. The backend | ||||||
| * automatically fetches the full conversation history from the database | ||||||
| * (when `documentChatThreadID` is provided), so callers never need to track | ||||||
| * or resend previous messages. | ||||||
| * | ||||||
| * @param request - The chat request containing the user message and diagram context | ||||||
| * @returns The AI response text and the chat thread ID | ||||||
| * @throws {@link AICreditsLimitExceededError} if AI credits limit is exceeded (HTTP 402) | ||||||
| */ | ||||||
| public async diagramChat(request: DiagramChatRequest): Promise<DiagramChatResponse> { | ||||||
| const { message, documentID, code = '', documentChatThreadID } = request; | ||||||
|
|
||||||
| // Send only the current user message. The backend will prepend the stored | ||||||
| // conversation history when autoFetchHistory is true (see AIChatRequestData). | ||||||
| const messages = [ | ||||||
| { | ||||||
| id: uuid(), | ||||||
| role: 'user' as const, | ||||||
| content: message, | ||||||
| experimental_attachments: [] as [], | ||||||
| }, | ||||||
| ]; | ||||||
|
|
||||||
| const requestBody = { | ||||||
| messages, | ||||||
| code, | ||||||
| documentID, | ||||||
| documentChatThreadID, | ||||||
| // parentID null: the backend already handles finding the correct parent | ||||||
| parentID: null, | ||||||
| // Tell the backend to fetch DB history and prepend it before calling the AI. | ||||||
| autoFetchHistory: true, | ||||||
| }; | ||||||
|
|
||||||
| try { | ||||||
| // responseType: 'text' buffers the full stream body as a plain string so we | ||||||
| // can parse the Vercel AI SDK data-stream format after the request completes. | ||||||
| const response = await this.axios.post<string>(URLS.rest.openai.chat, requestBody, { | ||||||
| responseType: 'text', | ||||||
| timeout: 120_000, | ||||||
|
||||||
| timeout: 120_000, | |
| timeout: Math.max(this.requestTimeout ?? 0, 120_000), |
Prashant-7718 marked this conversation as resolved.
Show resolved
Hide resolved
Prashant-7718 marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Mar 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 402->AICreditsLimitExceededError mapping logic is now duplicated in both repairDiagram() and diagramChat(). To reduce divergence risk (e.g., message formatting changes), consider extracting a small shared helper (or a private method) to detect the 402 Axios shape and build the error consistently.
Uh oh!
There was an error while loading. Please reload this page.