Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mcp-welcome-summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Show MCP server summary in the welcome panel and add configuration hints in the /mcp command output.
4 changes: 4 additions & 0 deletions apps/kimi-code/src/tui/components/chrome/welcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export class WelcomeComponent implements Component {
labelStyle('Version: ') + this.state.version,
];

if (this.state.mcpServersSummary) {
infoLines.push(labelStyle('MCP: ') + this.state.mcpServersSummary);
}

const contentLines: string[] = [...renderedHeaderLines, '', ...infoLines];

const lines: string[] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export function buildMcpStatusReportLines(options: McpStatusReportOptions): stri

lines.push('');
lines.push(` ${value(buildSummary(servers))}`);
lines.push(` ${muted('Configure with')} ${value('/mcp-config')}`);

return lines;
}
17 changes: 11 additions & 6 deletions apps/kimi-code/src/tui/controllers/session-event-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export class SessionEventHandler {
renderedSkillActivationIds: Set<string> = new Set();
renderedMcpServerStatusKeys: Map<string, string> = new Map();
mcpServerStatusSpinners: Map<string, MoonLoader> = new Map();
mcpServers: Map<string, McpServerStatusSnapshot> = new Map();

resetRuntimeState(): void {
this.backgroundAgentMetadata.clear();
Expand All @@ -112,6 +113,7 @@ export class SessionEventHandler {
this.subagentInfo.clear();
this.renderedSkillActivationIds.clear();
this.renderedMcpServerStatusKeys.clear();
this.mcpServers.clear();
this.stopAllMcpServerStatusSpinners();
}

Expand Down Expand Up @@ -155,19 +157,19 @@ export class SessionEventHandler {
this.renderMcpServerStatus(server);
}

this.mcpServers.clear();
for (const server of servers) {
this.mcpServers.set(server.name, server);
Comment on lines +160 to +162
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve live MCP statuses when snapshot resolves

When listMcpServers() is still in flight, a live mcp.server.status event can already call renderMcpServerStatus() and populate this.mcpServers with the current state. This block then unconditionally clears that map and rebuilds it from the older snapshot before publishing mcpServersSummary, so the welcome panel can regress to stale counts/statuses (for example, showing a failed server after a later connected event), even though the transcript path intentionally skips rendering stale visible rows. Merge only snapshot entries that have not received a newer event, or avoid clearing live state here.

Useful? React with 👍 / 👎.

}
const hidden: McpServerStatusSnapshot[] = [];
for (const server of servers) {
if (visibleNames.has(server.name)) continue;
if (this.renderedMcpServerStatusKeys.has(server.name)) continue;
this.renderedMcpServerStatusKeys.set(server.name, mcpServerStatusKey(server));
hidden.push(server);
}
if (hidden.length > 0) {
host.showStatus(
formatMcpStartupStatusSummary(hidden, visible.length),
host.state.theme.colors.textMuted,
);
}
const summary = formatMcpStartupStatusSummary(servers);
host.setAppState({ mcpServersSummary: summary || null });
}

handleEvent(event: Event, sendQueued: (item: QueuedMessage) => void): void {
Expand Down Expand Up @@ -582,6 +584,9 @@ export class SessionEventHandler {
const key = mcpServerStatusKey(server);
if (this.renderedMcpServerStatusKeys.get(server.name) === key) return;
this.renderedMcpServerStatusKeys.set(server.name, key);
this.mcpServers.set(server.name, server);
const summary = formatMcpStartupStatusSummary([...this.mcpServers.values()]);
this.host.setAppState({ mcpServersSummary: summary || null });

const colors = state.theme.colors;
switch (server.status) {
Expand Down
2 changes: 2 additions & 0 deletions apps/kimi-code/src/tui/kimi-tui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ function createInitialAppState(input: KimiTUIStartupInput): AppState {
availableModels: {},
availableProviders: {},
sessionTitle: null,
mcpServersSummary: null,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear MCP summary before rendering a new session

This summary is stored in shared AppState, but resetSessionRuntime/syncRuntimeState never clears it before clearTranscriptAndRedraw() renders the next session's welcome panel. After switching from a session with an MCP summary to another session, the old summary remains visible until this async snapshot finishes, and it can remain indefinitely if listMcpServers() fails, so the welcome panel can show MCP state from the previous session.

Useful? React with 👍 / 👎.

};
}

Expand Down Expand Up @@ -1081,6 +1082,7 @@ export class KimiTUI {
this.state.footer.setBackgroundCounts({ bashTasks: 0, agentTasks: 0 });
this.streamingUI.setTodoList([]);
this.streamingUI.setTurnId(undefined);
this.setAppState({ mcpServersSummary: null });
this.streamingUI.setStep(0);
this.streamingUI.resetLiveText();
this.updateQueueDisplay();
Expand Down
1 change: 1 addition & 0 deletions apps/kimi-code/src/tui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface AppState {
availableModels: Record<string, ModelAlias>;
availableProviders: Record<string, ProviderConfig>;
sessionTitle: string | null;
mcpServersSummary: string | null;
}

export interface ToolCallBlockData {
Expand Down
9 changes: 3 additions & 6 deletions apps/kimi-code/src/tui/utils/mcp-server-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,14 @@ export function selectMcpStartupStatusRows(
}

export function formatMcpStartupStatusSummary(
hidden: readonly McpServerStatusSnapshot[],
visibleCount: number,
servers: readonly McpServerStatusSnapshot[],
): string {
let failed = 0;
let needsAuth = 0;
let connecting = 0;
let connected = 0;
let disabled = 0;
for (const server of hidden) {
for (const server of servers) {
switch (server.status) {
case 'failed':
failed++;
Expand All @@ -63,9 +62,7 @@ export function formatMcpStartupStatusSummary(
if (connecting > 0) parts.push(`${connecting} connecting`);
if (connected > 0) parts.push(`${connected} connected`);
if (disabled > 0) parts.push(`${disabled} disabled`);
const detail = parts.join(', ');
if (visibleCount === 0) return `MCP servers: ${detail}`;
return `MCP servers: ${hidden.length} more (${detail})`;
return parts.join(', ');
}

export function mcpServerStatusKey(server: McpServerStatusSnapshot): string {
Expand Down
1 change: 1 addition & 0 deletions apps/kimi-code/test/tui/components/chrome/footer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const appState: AppState = {
notifications: { enabled: true, condition: 'unfocused' },
availableModels: {},
availableProviders: {},
mcpServersSummary: null,
};

describe('FooterComponent', () => {
Expand Down
1 change: 1 addition & 0 deletions apps/kimi-code/test/tui/components/chrome/welcome.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const appState: AppState = {
notifications: { enabled: true, condition: 'unfocused' },
availableModels: {},
availableProviders: {},
mcpServersSummary: null,
};

function truecolorCodes(text: string): Set<string> {
Expand Down
1 change: 1 addition & 0 deletions apps/kimi-code/test/tui/create-tui-state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function fakeInitialAppState(): AppState {
availableModels: {},
availableProviders: {},
sessionTitle: null,
mcpServersSummary: null,
};
}

Expand Down
Loading