SEP-1319: Decouple Request Payloads, Remove passthrough iteration, Typecheck fixes (#1086)

This commit is contained in:
Konstantin Konstantinov 2025-11-07 18:50:20 +02:00 • committed by GitHub
commit f59996ae66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 2840 additions and 951 deletions

View file

@ -10,18 +10,15 @@ import { BaseMetadata } from '../types.js';
* For other objects: title → name
* This implements the spec requirement: "if no title is provided, name should be used for display purposes"
*/
export function getDisplayName(metadata: BaseMetadata): string {
export function getDisplayName(metadata: BaseMetadata & { annotations?: { title?: string } }): string {
// First check for title (not undefined and not empty string)
if (metadata.title !== undefined && metadata.title !== '') {
return metadata.title;
}
// Then check for annotations.title (only present in Tool objects)
if ('annotations' in metadata) {
const metadataWithAnnotations = metadata as BaseMetadata & { annotations?: { title?: string } };
if (metadataWithAnnotations.annotations?.title) {
return metadataWithAnnotations.annotations.title;
}
if (metadata.annotations?.title) {
return metadata.annotations.title;
}
// Finally fall back to name

View file

@ -666,11 +666,13 @@ describe('mergeCapabilities', () => {
const additional: ClientCapabilities = {
experimental: {
feature: true
feature: {
featureFlag: true
}
},
elicitation: {},
roots: {
newProp: true
listChanged: true
}
};
@ -679,11 +681,12 @@ describe('mergeCapabilities', () => {
sampling: {},
elicitation: {},
roots: {
listChanged: true,
newProp: true
listChanged: true
},
experimental: {
feature: true
feature: {
featureFlag: true
}
}
});
});
@ -701,7 +704,7 @@ describe('mergeCapabilities', () => {
subscribe: true
},
prompts: {
newProp: true
listChanged: true
}
};
@ -709,8 +712,7 @@ describe('mergeCapabilities', () => {
expect(merged).toEqual({
logging: {},
prompts: {
listChanged: true,
newProp: true
listChanged: true
},
resources: {
subscribe: true

View file

@ -693,16 +693,24 @@ export abstract class Protocol<SendRequestT extends Request, SendNotificationT e
}
}
export function mergeCapabilities<T extends ServerCapabilities | ClientCapabilities>(base: T, additional: T): T {
return Object.entries(additional).reduce(
(acc, [key, value]) => {
if (value && typeof value === 'object') {
acc[key] = acc[key] ? { ...acc[key], ...value } : value;
} else {
acc[key] = value;
}
return acc;
},
{ ...base }
);
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
export function mergeCapabilities(base: ServerCapabilities, additional: Partial<ServerCapabilities>): ServerCapabilities;
export function mergeCapabilities(base: ClientCapabilities, additional: Partial<ClientCapabilities>): ClientCapabilities;
export function mergeCapabilities<T extends ServerCapabilities | ClientCapabilities>(base: T, additional: Partial<T>): T {
const result: T = { ...base };
for (const key in additional) {
const k = key as keyof T;
const addValue = additional[k];
if (addValue === undefined) continue;
const baseValue = result[k];
if (isPlainObject(baseValue) && isPlainObject(addValue)) {
result[k] = { ...(baseValue as Record<string, unknown>), ...(addValue as Record<string, unknown>) } as T[typeof k];
} else {
result[k] = addValue as T[typeof k];
}
}
return result;
}

View file

@ -71,7 +71,7 @@ export interface Transport {
*
* The requestInfo can be used to get the original request information (headers, etc.)
*/
onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;
onmessage?: <T extends JSONRPCMessage>(message: T, extra?: MessageExtraInfo) => void;
/**
* The session ID generated for this connection.