Compare commits

...

5 Commits

Author SHA1 Message Date
Cline Evaluation a67319f1d1 Adding thinking Slider for Gemini models 2025-06-11 01:06:53 +05:30
Cline Evaluation 7a3d66cb71 first commit 2025-06-10 11:11:01 +05:30
Cline Evaluation a913be6c34 Adding real time client 2025-06-10 02:53:51 +05:30
Cline Evaluation c4fe966032 Adding real time client 2025-06-10 02:45:58 +05:30
Cline Evaluation bd14dad9d9 Adding real time client 2025-06-10 02:29:14 +05:30
8 changed files with 243 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Supporting Notifications MCP with Cline
+38 -2
View File
@@ -128,7 +128,25 @@
"type": "npm",
"script": "watch:esbuild",
"group": "build",
"problemMatcher": "$esbuild-watch",
"problemMatcher": {
"pattern": [
{
"regexp": "^✘ \\[ERROR\\] (.*)$",
"message": 1
},
{
"regexp": "^\\s+(.*):(\\d+):(\\d+):$",
"file": 1,
"line": 2,
"column": 3
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "^\\[watch\\] build started$",
"endsPattern": "^\\[watch\\] build finished$"
}
},
"isBackground": true,
"label": "npm: watch:esbuild",
"dependsOn": ["npm: protos"],
@@ -146,7 +164,25 @@
"type": "npm",
"script": "watch:esbuild:test",
"group": "build",
"problemMatcher": "$esbuild-watch",
"problemMatcher": {
"pattern": [
{
"regexp": "^✘ \\[ERROR\\] (.*)$",
"message": 1
},
{
"regexp": "^\\s+(.*):(\\d+):(\\d+):$",
"file": 1,
"line": 2,
"column": 3
}
],
"background": {
"activeOnStart": true,
"beginsPattern": "^\\[watch\\] build started$",
"endsPattern": "^\\[watch\\] build finished$"
}
},
"isBackground": true,
"label": "npm: watch:esbuild:test",
"dependsOn": ["npm: protos"],
+8 -7
View File
@@ -65,13 +65,14 @@ enum ClineSay {
BROWSER_ACTION_RESULT = 16;
MCP_SERVER_REQUEST_STARTED = 17;
MCP_SERVER_RESPONSE = 18;
USE_MCP_SERVER_SAY = 19;
DIFF_ERROR = 20;
DELETED_API_REQS = 21;
CLINEIGNORE_ERROR = 22;
CHECKPOINT_CREATED = 23;
LOAD_MCP_DOCUMENTATION = 24;
INFO = 25;
MCP_NOTIFICATION = 19;
USE_MCP_SERVER_SAY = 20;
DIFF_ERROR = 21;
DELETED_API_REQS = 22;
CLINEIGNORE_ERROR = 23;
CHECKPOINT_CREATED = 24;
LOAD_MCP_DOCUMENTATION = 25;
INFO = 26;
}
// Enum for ClineSayTool tool types
+22
View File
@@ -234,6 +234,12 @@ export class Task {
this.chatSettings = chatSettings
this.enableCheckpoints = enableCheckpointsSetting
// Set up MCP notification callback for real-time notifications
this.mcpHub.setNotificationCallback(async (serverName: string, level: string, message: string) => {
// Display notification in chat immediately
await this.say("mcp_notification", `[${serverName}] ${message}`)
})
// Initialize taskId first
if (historyItem) {
this.taskId = historyItem.id
@@ -1209,6 +1215,9 @@ export class Task {
this.clineIgnoreController.dispose()
this.fileContextTracker.dispose()
await this.diffViewProvider.revertChanges() // need to await for when we want to make sure directories/files are reverted before re-starting the task from a checkpoint
// Clear the notification callback when task is aborted
this.mcpHub.clearNotificationCallback()
}
// Checkpoints
@@ -3340,8 +3349,21 @@ export class Task {
// now execute the tool
await this.say("mcp_server_request_started") // same as browser_action_result
// Check for any pending notifications before the tool call
const notificationsBefore = this.mcpHub.getPendingNotifications()
for (const notification of notificationsBefore) {
await this.say("mcp_notification", `[${notification.serverName}] ${notification.message}`)
}
const toolResult = await this.mcpHub.callTool(server_name, tool_name, parsedArguments)
// Check for any pending notifications after the tool call
const notificationsAfter = this.mcpHub.getPendingNotifications()
for (const notification of notificationsAfter) {
await this.say("mcp_notification", `[${notification.serverName}] ${notification.message}`)
}
// TODO: add progress indicator
const toolResultImages =
+137
View File
@@ -52,6 +52,17 @@ export class McpHub {
connections: McpConnection[] = []
isConnecting: boolean = false
// Store notifications for display in chat
private pendingNotifications: Array<{
serverName: string
level: string
message: string
timestamp: number
}> = []
// Callback for sending notifications to active task
private notificationCallback?: (serverName: string, level: string, message: string) => void
constructor(
getMcpServersPath: () => Promise<string>,
getSettingsDirectoryPath: () => Promise<string>,
@@ -319,6 +330,100 @@ export class McpHub {
connection.server.status = "connected"
connection.server.error = ""
// Register notification handler for real-time messages
console.log(`[MCP Debug] Setting up notification handlers for server: ${name}`)
console.log(`[MCP Debug] Client instance:`, connection.client)
console.log(`[MCP Debug] Transport type:`, config.type)
// Try to set notification handler using the client's method
try {
// Import the notification schema from MCP SDK
const { z } = await import("zod")
// Define the notification schema for notifications/message
const NotificationMessageSchema = z.object({
method: z.literal("notifications/message"),
params: z
.object({
level: z.enum(["debug", "info", "warning", "error"]).optional(),
logger: z.string().optional(),
data: z.string().optional(),
message: z.string().optional(),
})
.optional(),
})
// Set the notification handler
connection.client.setNotificationHandler(NotificationMessageSchema as any, async (notification: any) => {
console.log(`[MCP Notification] ${name}:`, JSON.stringify(notification, null, 2))
const params = notification.params || {}
const level = params.level || "info"
const data = params.data || params.message || ""
const logger = params.logger || ""
console.log(`[MCP Message Notification] ${name}: level=${level}, data=${data}, logger=${logger}`)
// Format the message
const message = logger ? `[${logger}] ${data}` : data
// Send notification directly to active task if callback is set
if (this.notificationCallback) {
console.log(`[MCP Debug] Sending notification to active task: ${message}`)
this.notificationCallback(name, level, message)
} else {
// Fallback: store for later retrieval
console.log(`[MCP Debug] No active task, storing notification: ${message}`)
this.pendingNotifications.push({
serverName: name,
level,
message,
timestamp: Date.now(),
})
}
// Also show as VS Code notification for now (can be removed later if desired)
switch (level) {
case "error":
vscode.window.showErrorMessage(`MCP ${name}: ${message}`)
break
case "warning":
vscode.window.showWarningMessage(`MCP ${name}: ${message}`)
break
default:
vscode.window.showInformationMessage(`MCP ${name}: ${message}`)
}
// Forward to webview if available
if (this.postMessageToWebview) {
await this.postMessageToWebview({
type: "mcpNotification",
serverName: name,
notification: {
level,
data,
logger,
timestamp: Date.now(),
},
} as any)
}
})
console.log(`[MCP Debug] Successfully set notifications/message handler for ${name}`)
// Also set a fallback handler for any other notification types
connection.client.fallbackNotificationHandler = async (notification: any) => {
console.log(`[MCP Fallback Notification] ${name}:`, JSON.stringify(notification, null, 2))
// Show in VS Code for visibility
vscode.window.showInformationMessage(
`MCP ${name}: ${notification.method || "unknown"} - ${JSON.stringify(notification.params || {})}`,
)
}
console.log(`[MCP Debug] Successfully set fallback notification handler for ${name}`)
} catch (error) {
console.error(`[MCP Debug] Error setting notification handlers for ${name}:`, error)
}
// Initial fetch of tools and resources
connection.server.tools = await this.fetchToolsList(name)
connection.server.resources = await this.fetchResourcesList(name)
@@ -943,6 +1048,38 @@ export class McpHub {
}
}
/**
* Get and clear pending notifications
* @returns Array of pending notifications
*/
getPendingNotifications(): Array<{
serverName: string
level: string
message: string
timestamp: number
}> {
const notifications = [...this.pendingNotifications]
this.pendingNotifications = []
return notifications
}
/**
* Set the notification callback for real-time notifications
* @param callback Function to call when notifications arrive
*/
setNotificationCallback(callback: (serverName: string, level: string, message: string) => void): void {
this.notificationCallback = callback
console.log("[MCP Debug] Notification callback set")
}
/**
* Clear the notification callback
*/
clearNotificationCallback(): void {
this.notificationCallback = undefined
console.log("[MCP Debug] Notification callback cleared")
}
async dispose(): Promise<void> {
this.removeAllFileWatchers()
for (const connection of this.connections) {
+1
View File
@@ -164,6 +164,7 @@ export type ClineSay =
| "browser_action_result"
| "mcp_server_request_started"
| "mcp_server_response"
| "mcp_notification"
| "use_mcp_server"
| "diff_error"
| "deleted_api_reqs"
@@ -89,6 +89,7 @@ function convertClineSayToProtoEnum(say: AppClineSay | undefined): ClineSay | un
browser_action_result: ClineSay.BROWSER_ACTION_RESULT,
mcp_server_request_started: ClineSay.MCP_SERVER_REQUEST_STARTED,
mcp_server_response: ClineSay.MCP_SERVER_RESPONSE,
mcp_notification: ClineSay.MCP_NOTIFICATION,
use_mcp_server: ClineSay.USE_MCP_SERVER_SAY,
diff_error: ClineSay.DIFF_ERROR,
deleted_api_reqs: ClineSay.DELETED_API_REQS,
@@ -132,6 +133,7 @@ function convertProtoEnumToClineSay(say: ClineSay): AppClineSay | undefined {
[ClineSay.BROWSER_ACTION_RESULT]: "browser_action_result",
[ClineSay.MCP_SERVER_REQUEST_STARTED]: "mcp_server_request_started",
[ClineSay.MCP_SERVER_RESPONSE]: "mcp_server_response",
[ClineSay.MCP_NOTIFICATION]: "mcp_notification",
[ClineSay.USE_MCP_SERVER_SAY]: "use_mcp_server",
[ClineSay.DIFF_ERROR]: "diff_error",
[ClineSay.DELETED_API_REQS]: "deleted_api_reqs",
@@ -1054,6 +1054,36 @@ export const ChatRowContent = ({
return null // we should never see this message type
case "mcp_server_response":
return <McpResponseDisplay responseText={message.text || ""} />
case "mcp_notification":
return (
<div
style={{
display: "flex",
alignItems: "flex-start",
gap: "8px",
padding: "8px 12px",
backgroundColor: "var(--vscode-textBlockQuote-background)",
borderRadius: "4px",
fontSize: "13px",
color: "var(--vscode-foreground)",
opacity: 0.9,
marginBottom: "8px",
}}>
<i
className="codicon codicon-bell"
style={{
marginTop: "2px",
fontSize: "14px",
color: "var(--vscode-notificationsInfoIcon-foreground)",
flexShrink: 0,
}}
/>
<div style={{ flex: 1, wordBreak: "break-word" }}>
<span style={{ fontWeight: 500 }}>MCP Notification: </span>
<span className="ph-no-capture">{message.text}</span>
</div>
</div>
)
case "text":
return (
<WithCopyButton ref={contentRef} onMouseUp={handleMouseUp} textToCopy={message.text}>