parent
852ebb9ec0
commit
90e91cdd62
4 changed files with 128 additions and 4 deletions
|
|
@ -9,8 +9,13 @@ import {
|
|||
ResultSchema,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
SUPPORTED_PROTOCOL_VERSIONS,
|
||||
InitializeRequestSchema,
|
||||
ListResourcesRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from "../types.js";
|
||||
import { Transport } from "../shared/transport.js";
|
||||
import { Server } from "../server/index.js";
|
||||
import { InMemoryTransport } from "../inMemory.js";
|
||||
|
||||
test("should initialize with matching protocol version", async () => {
|
||||
const clientTransport: Transport = {
|
||||
|
|
@ -126,6 +131,61 @@ test("should reject unsupported protocol version", async () => {
|
|||
expect(clientTransport.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should respect server capabilities", async () => {
|
||||
const server = new Server({
|
||||
name: "test server",
|
||||
version: "1.0",
|
||||
});
|
||||
|
||||
server.setRequestHandler(InitializeRequestSchema, (request) => ({
|
||||
protocolVersion: LATEST_PROTOCOL_VERSION,
|
||||
capabilities: {
|
||||
resources: {},
|
||||
tools: {},
|
||||
},
|
||||
serverInfo: {
|
||||
name: "test",
|
||||
version: "1.0",
|
||||
},
|
||||
}));
|
||||
|
||||
server.setRequestHandler(ListResourcesRequestSchema, () => ({
|
||||
resources: [],
|
||||
}));
|
||||
|
||||
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
||||
tools: [],
|
||||
}));
|
||||
|
||||
const [clientTransport, serverTransport] =
|
||||
InMemoryTransport.createLinkedPair();
|
||||
|
||||
const client = new Client({
|
||||
name: "test client",
|
||||
version: "1.0",
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
client.connect(clientTransport),
|
||||
server.connect(serverTransport),
|
||||
]);
|
||||
|
||||
// Server supports resources and tools, but not prompts
|
||||
expect(client.getServerCapabilities()).toEqual({
|
||||
resources: {},
|
||||
tools: {},
|
||||
});
|
||||
|
||||
// These should work
|
||||
await expect(client.listResources()).resolves.not.toThrow();
|
||||
await expect(client.listTools()).resolves.not.toThrow();
|
||||
|
||||
// This should throw because prompts are not supported
|
||||
await expect(client.listPrompts()).rejects.toThrow(
|
||||
"Server does not support prompts",
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
Test that custom request/notification/result schemas can be used with the Client class.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -132,6 +132,17 @@ export class Client<
|
|||
return this._serverVersion;
|
||||
}
|
||||
|
||||
private assertCapability(
|
||||
capability: keyof ServerCapabilities,
|
||||
method: string,
|
||||
) {
|
||||
if (!this._serverCapabilities?.[capability]) {
|
||||
throw new Error(
|
||||
`Server does not support ${capability} (required for ${method})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async ping() {
|
||||
return this.request({ method: "ping" }, EmptyResultSchema);
|
||||
}
|
||||
|
|
@ -140,6 +151,7 @@ export class Client<
|
|||
params: CompleteRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("prompts", "completion/complete");
|
||||
return this.request(
|
||||
{ method: "completion/complete", params },
|
||||
CompleteResultSchema,
|
||||
|
|
@ -148,6 +160,7 @@ export class Client<
|
|||
}
|
||||
|
||||
async setLoggingLevel(level: LoggingLevel) {
|
||||
this.assertCapability("logging", "logging/setLevel");
|
||||
return this.request(
|
||||
{ method: "logging/setLevel", params: { level } },
|
||||
EmptyResultSchema,
|
||||
|
|
@ -158,6 +171,7 @@ export class Client<
|
|||
params: GetPromptRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("prompts", "prompts/get");
|
||||
return this.request(
|
||||
{ method: "prompts/get", params },
|
||||
GetPromptResultSchema,
|
||||
|
|
@ -169,6 +183,7 @@ export class Client<
|
|||
params?: ListPromptsRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("prompts", "prompts/list");
|
||||
return this.request(
|
||||
{ method: "prompts/list", params },
|
||||
ListPromptsResultSchema,
|
||||
|
|
@ -180,6 +195,7 @@ export class Client<
|
|||
params?: ListResourcesRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("resources", "resources/list");
|
||||
return this.request(
|
||||
{ method: "resources/list", params },
|
||||
ListResourcesResultSchema,
|
||||
|
|
@ -191,6 +207,7 @@ export class Client<
|
|||
params?: ListResourceTemplatesRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("resources", "resources/templates/list");
|
||||
return this.request(
|
||||
{ method: "resources/templates/list", params },
|
||||
ListResourceTemplatesResultSchema,
|
||||
|
|
@ -202,6 +219,7 @@ export class Client<
|
|||
params: ReadResourceRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("resources", "resources/read");
|
||||
return this.request(
|
||||
{ method: "resources/read", params },
|
||||
ReadResourceResultSchema,
|
||||
|
|
@ -210,6 +228,7 @@ export class Client<
|
|||
}
|
||||
|
||||
async subscribeResource(params: SubscribeRequest["params"]) {
|
||||
this.assertCapability("resources", "resources/subscribe");
|
||||
return this.request(
|
||||
{ method: "resources/subscribe", params },
|
||||
EmptyResultSchema,
|
||||
|
|
@ -217,6 +236,7 @@ export class Client<
|
|||
}
|
||||
|
||||
async unsubscribeResource(params: UnsubscribeRequest["params"]) {
|
||||
this.assertCapability("resources", "resources/unsubscribe");
|
||||
return this.request(
|
||||
{ method: "resources/unsubscribe", params },
|
||||
EmptyResultSchema,
|
||||
|
|
@ -230,6 +250,7 @@ export class Client<
|
|||
| typeof CompatibilityCallToolResultSchema = CallToolResultSchema,
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("tools", "tools/call");
|
||||
return this.request(
|
||||
{ method: "tools/call", params },
|
||||
resultSchema,
|
||||
|
|
@ -241,6 +262,7 @@ export class Client<
|
|||
params?: ListToolsRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("tools", "tools/list");
|
||||
return this.request(
|
||||
{ method: "tools/list", params },
|
||||
ListToolsResultSchema,
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import {
|
|||
ResultSchema,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
SUPPORTED_PROTOCOL_VERSIONS,
|
||||
InitializeRequestSchema,
|
||||
InitializeResultSchema,
|
||||
} from "../types.js";
|
||||
import { Transport } from "../shared/transport.js";
|
||||
import { InMemoryTransport } from "../inMemory.js";
|
||||
import { Client } from "../client/index.js";
|
||||
|
||||
test("should accept latest protocol version", async () => {
|
||||
let sendPromiseResolve: (value: unknown) => void;
|
||||
|
|
@ -165,6 +165,33 @@ test("should handle unsupported protocol version", async () => {
|
|||
await expect(sendPromise).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
test("should respect client capabilities", async () => {
|
||||
const server = new Server({
|
||||
name: "test server",
|
||||
version: "1.0",
|
||||
});
|
||||
|
||||
const client = new Client({
|
||||
name: "test client",
|
||||
version: "1.0",
|
||||
});
|
||||
|
||||
const [clientTransport, serverTransport] =
|
||||
InMemoryTransport.createLinkedPair();
|
||||
|
||||
await Promise.all([
|
||||
client.connect(clientTransport),
|
||||
server.connect(serverTransport),
|
||||
]);
|
||||
|
||||
expect(server.getClientCapabilities()).toEqual({});
|
||||
|
||||
// This should throw because roots are not supported by the client
|
||||
await expect(server.listRoots()).rejects.toThrow(
|
||||
"Client does not support roots",
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
Test that custom request/notification/result schemas can be used with the Server class.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import {
|
|||
ServerRequest,
|
||||
ServerResult,
|
||||
SetLevelRequestSchema,
|
||||
SUPPORTED_PROTOCOL_VERSIONS
|
||||
SUPPORTED_PROTOCOL_VERSIONS,
|
||||
} from "../types.js";
|
||||
|
||||
/**
|
||||
|
|
@ -93,7 +93,9 @@ export class Server<
|
|||
this._clientVersion = request.params.clientInfo;
|
||||
|
||||
return {
|
||||
protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION,
|
||||
protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion)
|
||||
? requestedVersion
|
||||
: LATEST_PROTOCOL_VERSION,
|
||||
capabilities: this.getCapabilities(),
|
||||
serverInfo: this._serverInfo,
|
||||
};
|
||||
|
|
@ -138,6 +140,17 @@ export class Server<
|
|||
};
|
||||
}
|
||||
|
||||
private assertCapability(
|
||||
capability: keyof ClientCapabilities,
|
||||
method: string,
|
||||
) {
|
||||
if (!this._clientCapabilities?.[capability]) {
|
||||
throw new Error(
|
||||
`Client does not support ${capability} (required for ${method})`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async ping() {
|
||||
return this.request({ method: "ping" }, EmptyResultSchema);
|
||||
}
|
||||
|
|
@ -146,6 +159,7 @@ export class Server<
|
|||
params: CreateMessageRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("sampling", "sampling/createMessage");
|
||||
return this.request(
|
||||
{ method: "sampling/createMessage", params },
|
||||
CreateMessageResultSchema,
|
||||
|
|
@ -157,6 +171,7 @@ export class Server<
|
|||
params?: ListRootsRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("roots", "roots/list");
|
||||
return this.request(
|
||||
{ method: "roots/list", params },
|
||||
ListRootsResultSchema,
|
||||
|
|
|
|||
Loading…
Reference in a new issue