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
23 changes: 4 additions & 19 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 45 additions & 4 deletions fresh-ui/components/ClientSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,30 @@ import { selectedClientId } from '../hooks/useConsoleState.ts';
import type { ClientInfo, SessionId } from '@shared/types/index.ts';
import { isClientList } from '@shared/types/index.ts';

/**
* Format a timestamp to a readable date/time string
*/
function formatTimestamp(timestamp: number): string {
const date = new Date(timestamp);
return date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
}

export default function ClientSelector() {
const detailsRef = useRef<HTMLDetailsElement>(null);
const dropdownContentRef = useRef<HTMLDivElement>(null);
const summaryRef = useRef<HTMLElement>(null);

// Extract clients from messages
// Extract clients from messages and sort by most recently seen
const clientListMessage = wsMessages.value.find(isClientList);
const clients = clientListMessage
? (clientListMessage.payload as { clients: ClientInfo[] }).clients
.sort((a, b) => b.lastSeen - a.lastSeen) // Sort by most recent first
: [];

// Auto-select first client if none selected and clients available
Expand Down Expand Up @@ -206,6 +221,18 @@ export default function ClientSelector() {
{client.sessionId.slice(0, 8)}...
</div>

{/* Connection Times */}
<div class='mt-2 text-xs opacity-60 space-y-0.5'>
<div>
<span class='opacity-70'>Connected:</span>{' '}
{formatTimestamp(client.connectedAt)}
</div>
<div>
<span class='opacity-70'>Last Seen:</span>{' '}
{formatTimestamp(client.lastSeen)}
</div>
</div>

{/* Last Meta Indicator */}
{metadata?.lastMeta && (
<div class='mt-1'>
Expand Down Expand Up @@ -246,9 +273,23 @@ export default function ClientSelector() {
)}

{/* Selection Info */}
{selectedClientId.value && (
<div class='text-xs opacity-60 mt-2'>
Selected: <span class='font-mono'>{selectedClientId.value}</span>
{selectedClient && (
<div class='mt-4 p-3 bg-base-200 rounded-lg'>
<div class='text-xs font-semibold opacity-70 mb-2'>Selected Client</div>
<div class='text-xs space-y-1'>
<div>
<span class='opacity-70'>Session ID:</span>{' '}
<span class='font-mono'>{selectedClient.sessionId}</span>
</div>
<div>
<span class='opacity-70'>Connected:</span>{' '}
{formatTimestamp(selectedClient.connectedAt)}
</div>
<div>
<span class='opacity-70'>Last Seen:</span>{' '}
{formatTimestamp(selectedClient.lastSeen)}
</div>
</div>
</div>
)}
</div>
Expand Down
5 changes: 3 additions & 2 deletions fresh-ui/components/ElicitationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default function ElicitationForm() {
type: 'object',
properties: {}, // Empty properties for approval mode
},
sessionId: selectedClientId.value || undefined, // Target specific client
options: { sessionId: selectedClientId.value || undefined }, // Target specific client
};

// Add schema if in form mode and schema provided
Expand All @@ -127,14 +127,15 @@ export default function ElicitationForm() {
}
}

//console.log('[ElicitationForm] Validating payload:', payload);
// Validate payload with Zod
const validation = validateElicitationPayload(payload);
if (!validation.success) {
alert(`Validation error: ${formatZodError(validation.error)}`);
return;
}

console.log('[ElicitationForm] Sending request:', payload);
console.log('[ElicitationForm] Sending request:', validation.data);
sendCommand({
type: 'request_elicitation',
payload: validation.data,
Expand Down
2 changes: 1 addition & 1 deletion fresh-ui/components/NotificationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function NotificationForm() {
level: notificationLevel.value,
logger: notificationLogger.value || undefined,
data: notificationMessage.value,
sessionId: selectedClientId.value || undefined, // Target specific client
options: { sessionId: selectedClientId.value || undefined }, // Target specific client
};

sendCommand({
Expand Down
2 changes: 1 addition & 1 deletion fresh-ui/components/SamplingForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function SamplingForm() {
: undefined,
temperature: temperature.value,
maxTokens: maxTokens.value,
sessionId: selectedClientId.value || undefined, // Target specific client
options: { sessionId: selectedClientId.value || undefined }, // Target specific client
};

console.log('[SamplingForm] Sending request:', payload);
Expand Down
4 changes: 2 additions & 2 deletions import_map.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"@shared/types/": "./shared/types/",
"@shared/version.ts": "./shared/version.ts",

"@beyondbetter/bb-mcp-server": "jsr:@beyondbetter/bb-mcp-server@^0.1.17",
"@beyondbetter/bb-mcp-server/": "jsr:@beyondbetter/bb-mcp-server@^0.1.17/",
"@beyondbetter/bb-mcp-server": "jsr:@beyondbetter/bb-mcp-server@^0.1.18",
"@beyondbetter/bb-mcp-server/": "jsr:@beyondbetter/bb-mcp-server@^0.1.18/",

"mcp": "npm:@modelcontextprotocol/sdk@1.18.2",
"mcp/client/": "npm:@modelcontextprotocol/sdk@1.18.2/client/",
Expand Down
18 changes: 0 additions & 18 deletions mcp-server/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions mcp-server/src/console/ConsoleManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export class ConsoleManager {
logger: payload.logger,
data: payload.data,
},
payload.sessionId,
payload.options,
);

this.broadcastMessage({
Expand Down Expand Up @@ -273,7 +273,7 @@ export class ConsoleManager {
): Promise<void> {
try {
this.logger.info('ConsoleManager: Requesting sampling from client', {
sessionId: payload.sessionId,
options: payload.options,
});

// Convert to library's CreateMessageRequest format
Expand All @@ -289,7 +289,7 @@ export class ConsoleManager {
maxTokens: payload.maxTokens,
temperature: payload.temperature,
stopSequences: payload.stopSequences,
}, payload.sessionId); // Pass sessionId for client targeting
}, payload.options); // Pass options for client targeting

this.broadcastMessage({
type: 'sampling_response',
Expand Down Expand Up @@ -321,24 +321,24 @@ export class ConsoleManager {
): Promise<void> {
try {
this.logger.info('ConsoleManager: Requesting elicitation from client', {
sessionId: payload.sessionId,
options: payload.options,
});
this.logger.debug('ConsoleManager: Elicitation request payload:', {
message: payload.message,
requestedSchema: payload.requestedSchema,
sessionId: payload.sessionId,
options: payload.options,
});

const response = await beyondMcpServer.elicitInput({
message: payload.message,
requestedSchema: payload.requestedSchema,
}, payload.sessionId);
}, payload.options);

this.logger.debug('ConsoleManager: Elicitation response received:', {
response: JSON.stringify(response, null, 2),
responseType: typeof response,
responseKeys: response ? Object.keys(response) : [],
}); // Pass sessionId for client targeting
}); // Pass options for client targeting

this.broadcastMessage({
type: 'elicitation_response',
Expand All @@ -355,7 +355,7 @@ export class ConsoleManager {
payloadSent: {
message: payload.message,
requestedSchema: payload.requestedSchema,
sessionId: payload.sessionId,
sessionId: payload.options.sessionId,
},
},
);
Expand Down Expand Up @@ -476,7 +476,7 @@ export class ConsoleManager {
): Promise<void> {
try {
const messages = await this.messageTracker.getMessages(
payload.sessionId || 'default',
payload.options.sessionId || 'default',
payload.limit || 100,
);

Expand Down
6 changes: 3 additions & 3 deletions shared/types/console.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export interface NotificationPayload {
/**
* Optional session ID for targeting specific client
*/
sessionId?: SessionId;
options: { sessionId?: SessionId; meta?: Record<string, unknown> };
}

/**
Expand Down Expand Up @@ -151,7 +151,7 @@ export interface SamplingPayload {
/**
* Optional session ID for targeting specific client
*/
sessionId?: SessionId;
options: { sessionId?: SessionId; meta?: Record<string, unknown> };
}

/**
Expand Down Expand Up @@ -204,7 +204,7 @@ export interface ElicitationPayload {
/**
* Optional session ID for targeting specific client
*/
sessionId?: SessionId;
options: { sessionId?: SessionId; meta?: Record<string, unknown> };
}

/**
Expand Down
9 changes: 8 additions & 1 deletion shared/types/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export const notificationPayloadSchema = z.object({
]),
logger: z.string().optional(),
data: z.unknown(),
sessionId: z.string().optional(),
options: z.object({
sessionId: z.string().optional(),
meta: z.record(z.unknown()).optional(),
}),
});

/**
Expand Down Expand Up @@ -125,6 +128,10 @@ const elicitationSchemaSchema = z.object({
export const elicitationPayloadSchema = z.object({
message: z.string().min(1, 'Message is required'),
requestedSchema: elicitationSchemaSchema,
options: z.object({
sessionId: z.string().optional(),
meta: z.record(z.unknown()).optional(),
}),
});

/**
Expand Down
8 changes: 4 additions & 4 deletions tests/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions tests/fixtures/console-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const notificationPayload: NotificationPayload = {
level: 'info',
logger: 'test',
data: 'Test notification message',
options: {},
};

/**
Expand Down Expand Up @@ -100,6 +101,7 @@ export const samplingPayload: SamplingPayload = {
],
maxTokens: 100,
temperature: 0.7,
options: {},
};

/**
Expand Down Expand Up @@ -153,6 +155,7 @@ export const elicitationPayload: ElicitationPayload = {
},
required: ['confirmed'],
},
options: {},
};

/**
Expand Down
Loading