Skip to content

Commit 4676c22

Browse files
chore(internal): support x-stainless-mcp-client-permissions headers in MCP servers
1 parent f9c3d86 commit 4676c22

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

packages/mcp-server/src/http.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,37 @@ const newServer = async ({
4040
}
4141
}
4242

43+
// Parse x-stainless-mcp-client-permissions header to override permission options
44+
//
45+
// Note: Permissions are best-effort and intended to prevent clients from doing unexpected things;
46+
// they're not a hard security boundary, so we allow arbitrary, client-driven overrides.
47+
//
48+
// See the Stainless MCP documentation for more details.
49+
let effectiveMcpOptions = mcpOptions;
50+
const clientPermissionsHeader = req.headers['x-stainless-mcp-client-permissions'];
51+
if (typeof clientPermissionsHeader === 'string') {
52+
try {
53+
const parsed = JSON.parse(clientPermissionsHeader);
54+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
55+
effectiveMcpOptions = {
56+
...mcpOptions,
57+
...(typeof parsed.allow_http_gets === 'boolean' && { codeAllowHttpGets: parsed.allow_http_gets }),
58+
...(Array.isArray(parsed.allowed_methods) && { codeAllowedMethods: parsed.allowed_methods }),
59+
...(Array.isArray(parsed.blocked_methods) && { codeBlockedMethods: parsed.blocked_methods }),
60+
};
61+
getLogger().info(
62+
{ clientPermissions: parsed },
63+
'Overriding code execution permissions from x-stainless-mcp-client-permissions header',
64+
);
65+
}
66+
} catch (error) {
67+
getLogger().warn({ error }, 'Failed to parse x-stainless-mcp-client-permissions header');
68+
}
69+
}
70+
4371
await initMcpServer({
4472
server: server,
45-
mcpOptions: mcpOptions,
73+
mcpOptions: effectiveMcpOptions,
4674
clientOptions: {
4775
...clientOptions,
4876
...authOptions,

0 commit comments

Comments
 (0)