-
Notifications
You must be signed in to change notification settings - Fork 536
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revert "feat: support extending McpServer with authorization" #272
Conversation
@jspahrsummers Should we put this advice to put in the header of |
Sure, we can do, although this is really more of a general OO principles thing. |
Agreed, but there is a choice available to the implementor, who may not always choose composition. In the case they do want to use inheritance, some guidance in this class's docs could head off efforts such as @wangshijun's in the future. |
Agree with the "composition over inheritance" principal, 👍 I took a look at the codebase for the latest version and noticed that there's no easy way to support following common use case:
It would be great if the McpServer could provide the authorization context to each tool callback at runtime. This way, each tool would know who's making the request and could perform a permission check before diving into the actual business logic. Here's an example of how I imagine it working: import { ZodRawShape } from 'zod';
import { McpServer, ToolCallback } from './server/mcp.js';
import { RequestHandlerExtra } from './shared/protocol.js';
import { JSONRPCRequest, ServerNotification, ServerRequest } from './types.js';
const mcpServer = new McpServer({
name: 'test server with auth',
version: '1.0',
});
type SessionUser = {
role: string;
[key: string]: unknown;
};
type AccessPolicy = {
allow?: {
roles?: string[];
};
deny?: {
roles?: string[];
};
};
type AuthorizeContext = {
user?: SessionUser;
ip?: string;
ua?: string;
};
function checkPermission(request: JSONRPCRequest, authorizeContext: AuthorizeContext, policy?: AccessPolicy): boolean {
// do permission check base on request, authorizeContext and policy
return true;
}
function wrapToolCallback(cb: ToolCallback<ZodRawShape>, policy?: AccessPolicy) {
return (async (
request: JSONRPCRequest,
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
authorizeContext: AuthorizeContext
) => {
const hasPermission = await checkPermission(request, authorizeContext, policy);
if (!hasPermission) {
throw new Error('Unauthorized');
}
return cb(request, extra);
}) as ToolCallback<ZodRawShape>;
}
mcpServer.tool(
'protected-tool',
wrapToolCallback(
() => ({
content: [
{
type: 'text',
text: 'Protected tool response',
},
],
}),
{
allow: {
roles: ['admin'],
},
}
)
); @jspahrsummers @cliffhall Do you have any thoughts or suggestions on the use case mentioned above? 😊 |
You might be able to make use of #166 once merged, which I think would be a clean way to support something like this. |
Reverts #249, as I don't think the core changes of that PR are a good idea:
McpServer
, and it will definitely cause problems (now or in the future) to override the way it installs tool call handlers, etc. Two possible alternatives here:McpServer.tool
, orTransport.user
ofunknown
type—which isn't set anywhere in the SDK—isn't useful, and is actively confusing because it provides no semantics or type information. Since object types are open-ended in JS, the correct approach would be to just save an additional property, and use a TS declaration in your application to refer to that property. Something like this:If there are ways in which these suggestions prevent something from being possible, let's discuss, but I suspect there's no additional SDK support required here.