Tests and assertions for client/server capabilities

Resolves #45.
This commit is contained in:
Justin Spahr-Summers 2024-11-11 22:30:50 +00:00
commit 90e91cdd62
No known key found for this signature in database
2 changed files with 128 additions and 4 deletions

View file

@ -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.
*/

View file

@ -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,