Handle capabilities checks at the request() level, make non-strict by default
This commit is contained in:
parent
8e369b7171
commit
dc45f5d148
2 changed files with 254 additions and 83 deletions
|
|
@ -46,10 +46,12 @@ test("should accept latest protocol version", async () => {
|
|||
version: "1.0",
|
||||
},
|
||||
{
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
capabilities: {
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -105,10 +107,12 @@ test("should accept supported older protocol version", async () => {
|
|||
version: "1.0",
|
||||
},
|
||||
{
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
capabilities: {
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -163,10 +167,12 @@ test("should handle unsupported protocol version", async () => {
|
|||
version: "1.0",
|
||||
},
|
||||
{
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
capabilities: {
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -197,19 +203,25 @@ test("should respect client capabilities", async () => {
|
|||
version: "1.0",
|
||||
},
|
||||
{
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
capabilities: {
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
},
|
||||
enforceStrictCapabilities: true,
|
||||
},
|
||||
);
|
||||
|
||||
const client = new Client(
|
||||
{
|
||||
name: "test client",
|
||||
version: "1.0",
|
||||
},
|
||||
{
|
||||
sampling: {},
|
||||
capabilities: {
|
||||
sampling: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -245,9 +257,7 @@ test("should respect client capabilities", async () => {
|
|||
).resolves.not.toThrow();
|
||||
|
||||
// This should still throw because roots are not supported by the client
|
||||
await expect(server.listRoots()).rejects.toThrow(
|
||||
"Client does not support roots",
|
||||
);
|
||||
await expect(server.listRoots()).rejects.toThrow(/^Client does not support/);
|
||||
});
|
||||
|
||||
/*
|
||||
|
|
@ -301,10 +311,12 @@ test("should typecheck", () => {
|
|||
version: "1.0.0",
|
||||
},
|
||||
{
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
capabilities: {
|
||||
prompts: {},
|
||||
resources: {},
|
||||
tools: {},
|
||||
logging: {},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
import { ProgressCallback, Protocol } from "../shared/protocol.js";
|
||||
import {
|
||||
ProgressCallback,
|
||||
Protocol,
|
||||
ProtocolOptions,
|
||||
} from "../shared/protocol.js";
|
||||
import {
|
||||
ClientCapabilities,
|
||||
CreateMessageRequest,
|
||||
|
|
@ -24,6 +28,13 @@ import {
|
|||
SUPPORTED_PROTOCOL_VERSIONS,
|
||||
} from "../types.js";
|
||||
|
||||
export type ServerOptions = ProtocolOptions & {
|
||||
/**
|
||||
* Capabilities to advertise as being supported by this server.
|
||||
*/
|
||||
capabilities: ClientCapabilities;
|
||||
};
|
||||
|
||||
/**
|
||||
* An MCP server on top of a pluggable transport.
|
||||
*
|
||||
|
|
@ -60,6 +71,7 @@ export class Server<
|
|||
> {
|
||||
private _clientCapabilities?: ClientCapabilities;
|
||||
private _clientVersion?: Implementation;
|
||||
private _capabilities: ServerCapabilities;
|
||||
|
||||
/**
|
||||
* Callback for when initialization has fully completed (i.e., the client has sent an `initialized` notification).
|
||||
|
|
@ -71,9 +83,10 @@ export class Server<
|
|||
*/
|
||||
constructor(
|
||||
private _serverInfo: Implementation,
|
||||
private _capabilities: ServerCapabilities,
|
||||
options: ServerOptions,
|
||||
) {
|
||||
super();
|
||||
super(options);
|
||||
this._capabilities = options.capabilities;
|
||||
|
||||
this.setRequestHandler(InitializeRequestSchema, (request) =>
|
||||
this._oninitialize(request),
|
||||
|
|
@ -83,6 +96,30 @@ export class Server<
|
|||
);
|
||||
}
|
||||
|
||||
protected assertCapabilityForMethod(method: RequestT["method"]): void {
|
||||
switch (method as ServerRequest["method"]) {
|
||||
case "sampling/createMessage":
|
||||
if (!this._clientCapabilities?.sampling) {
|
||||
throw new Error(
|
||||
`Client does not support sampling (required for ${method})`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "roots/list":
|
||||
if (!this._clientCapabilities?.roots) {
|
||||
throw new Error(
|
||||
`Client does not support listing roots (required for ${method})`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case "ping":
|
||||
// No specific capability required for ping
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async _oninitialize(
|
||||
request: InitializeRequest,
|
||||
): Promise<InitializeResult> {
|
||||
|
|
@ -137,7 +174,6 @@ export class Server<
|
|||
params: CreateMessageRequest["params"],
|
||||
onprogress?: ProgressCallback,
|
||||
) {
|
||||
this.assertCapability("sampling", "sampling/createMessage");
|
||||
return this.request(
|
||||
{ method: "sampling/createMessage", params },
|
||||
CreateMessageResultSchema,
|
||||
|
|
@ -149,7 +185,6 @@ 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