fix(mcp): expose channel payloads in primary content

This commit is contained in:
Peter Steinberger
2026-05-04 04:25:57 +01:00
parent a4df85e55f
commit cf40284544
4 changed files with 63 additions and 2 deletions

View File

@@ -48,6 +48,7 @@ Docs: https://docs.openclaw.ai
- Control UI: point the Appearance tweakcn browse action and docs at the live tweakcn editor route instead of the removed `/themes` page. Fixes #77048.
- Control UI: render Dream Diary prose through the sanitized markdown pipeline, so diary bold/italic/header markdown no longer appears as literal source text. Fixes #62413.
- Control UI: render tool results whose output arrives as text-block arrays and give expanded tool output a scrollable block, so read/exec output remains visible in WebChat. Fixes #77054.
- MCP: include serialized conversation/message payloads in the primary text content for `conversations_list` and `messages_read`, while preserving `structuredContent` for capable clients. Fixes #77024.
- Diagnostics: keep webhook/message OTEL attributes and Prometheus delivery labels low-cardinality and omit raw chat/message IDs from spans, so progress-draft and message-tool modes do not leak high-cardinality messaging identifiers.
- Google Meet: stop advertising legacy `mode: "realtime"` to agents and config UIs, while keeping it as a hidden compatibility alias for `mode: "agent"`, so new joins use the STT -> OpenClaw agent -> TTS path instead of selecting the direct realtime voice fallback.
- Google Meet: add `chrome.audioBufferBytes` for generated command-pair SoX audio commands and lower the default buffer from SoX's 8192 bytes to 4096 bytes to reduce Chrome talk-back latency.

View File

@@ -176,6 +176,55 @@ describe("openclaw channel mcp server", () => {
);
});
test("serializes conversation and message payloads into MCP primary content", async () => {
const mcp = await connectMcpWithoutGateway({ claudeChannelMode: "off" });
try {
const gatewayRequest = vi.fn(async (method: string) => {
if (method === "sessions.list") {
return {
sessions: [
{
key: "agent:main:telegram:direct:123",
channel: "telegram",
deliveryContext: { to: "123" },
lastMessagePreview: "hello",
},
],
};
}
if (method === "sessions.get") {
return {
messages: [
{
id: "msg-1",
role: "assistant",
content: [{ type: "text", text: "full transcript text" }],
},
],
};
}
throw new Error(`unexpected gateway method ${method}`);
});
attachReadyGateway(mcp.bridge, gatewayRequest);
const conversations = (await mcp.client.callTool({
name: "conversations_list",
arguments: {},
})) as { content?: Array<{ type: string; text?: string }> };
expect(conversations.content?.[0]?.text).toContain('"sessionKey"');
expect(conversations.content?.[0]?.text).toContain('"lastMessagePreview": "hello"');
const messages = (await mcp.client.callTool({
name: "messages_read",
arguments: { session_key: "agent:main:telegram:direct:123" },
})) as { content?: Array<{ type: string; text?: string }> };
expect(messages.content?.[0]?.text).toContain('"id": "msg-1"');
expect(messages.content?.[0]?.text).toContain("full transcript text");
} finally {
await mcp.close();
}
});
test("emits Claude channel and permission notifications", async () => {
const sessionKey = "agent:main:main";
let mcp: Awaited<ReturnType<typeof connectMcpWithoutGateway>> | null = null;

View File

@@ -149,6 +149,16 @@ export function summarizeResult(
};
}
export function summarizeStructuredResult(
label: string,
count: number,
payload: unknown,
): { content: Array<{ type: "text"; text: string }> } {
return {
content: [{ type: "text", text: `${label}: ${count}\n\n${JSON.stringify(payload, null, 2)}` }],
};
}
function resolveConversationChannel(row: SessionRow): string | undefined {
return normalizeMessageChannel(
toText(row.deliveryContext?.channel) ??

View File

@@ -5,6 +5,7 @@ import {
extractAttachmentsFromMessage,
resolveMessageId,
summarizeResult,
summarizeStructuredResult,
toText,
} from "./channel-shared.js";
@@ -34,7 +35,7 @@ export function registerChannelMcpTools(server: McpServer, bridge: OpenClawChann
async (args) => {
const conversations = await bridge.listConversations(args);
return {
...summarizeResult("conversations", conversations.length),
...summarizeStructuredResult("conversations", conversations.length, { conversations }),
structuredContent: { conversations },
};
},
@@ -69,7 +70,7 @@ export function registerChannelMcpTools(server: McpServer, bridge: OpenClawChann
async ({ session_key, limit }) => {
const messages = await bridge.readMessages(session_key, limit ?? 20);
return {
...summarizeResult("messages", messages.length),
...summarizeStructuredResult("messages", messages.length, { messages }),
structuredContent: { messages },
};
},