Skip to content

Commit c6921b7

Browse files
chore(internal): allow basic filtering of methods allowed for MCP code mode
1 parent d786340 commit c6921b7

File tree

7 files changed

+420
-5
lines changed

7 files changed

+420
-5
lines changed

packages/mcp-server/src/code-tool.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { McpTool, Metadata, ToolCallResult, asErrorResult, asTextContentResult }
44
import { Tool } from '@modelcontextprotocol/sdk/types.js';
55
import { readEnv } from './server';
66
import { WorkerInput, WorkerOutput } from './code-tool-types';
7+
import { SdkMethod } from './methods';
78
import { Finch } from '@tryfinch/finch-api';
89

910
const prompt = `Runs JavaScript code to interact with the Finch API.
@@ -36,7 +37,7 @@ Variables will not persist between calls, so make sure to return or log any data
3637
*
3738
* @param endpoints - The endpoints to include in the list.
3839
*/
39-
export function codeTool(): McpTool {
40+
export function codeTool(params: { blockedMethods: SdkMethod[] | undefined }): McpTool {
4041
const metadata: Metadata = { resource: 'all', operation: 'write', tags: [] };
4142
const tool: Tool = {
4243
name: 'execute',
@@ -60,6 +61,24 @@ export function codeTool(): McpTool {
6061
const code = args.code as string;
6162
const intent = args.intent as string | undefined;
6263

64+
// Do very basic blocking of code that includes forbidden method names.
65+
//
66+
// WARNING: This is not secure against obfuscation and other evasion methods. If
67+
// stronger security blocks are required, then these should be enforced in the downstream
68+
// API (e.g., by having users call the MCP server with API keys with limited permissions).
69+
if (params.blockedMethods) {
70+
const blockedMatches = params.blockedMethods.filter((method) =>
71+
code.includes(method.fullyQualifiedName),
72+
);
73+
if (blockedMatches.length > 0) {
74+
return asErrorResult(
75+
`The following methods have been blocked by the MCP server and cannot be used in code execution: ${blockedMatches
76+
.map((m) => m.fullyQualifiedName)
77+
.join(', ')}`,
78+
);
79+
}
80+
}
81+
6382
// this is not required, but passing a Stainless API key for the matching project_name
6483
// will allow you to run code-mode queries against non-published versions of your SDK.
6584
const stainlessAPIKey = readEnv('STAINLESS_API_KEY');

packages/mcp-server/src/http.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import { parseAuthHeaders } from './headers';
1111

1212
const newServer = async ({
1313
clientOptions,
14+
mcpOptions,
1415
req,
1516
res,
1617
}: {
1718
clientOptions: ClientOptions;
19+
mcpOptions: McpOptions;
1820
req: express.Request;
1921
res: express.Response;
2022
}): Promise<McpServer | null> => {
@@ -24,6 +26,7 @@ const newServer = async ({
2426
const authOptions = parseAuthHeaders(req, false);
2527
await initMcpServer({
2628
server: server,
29+
mcpOptions: mcpOptions,
2730
clientOptions: {
2831
...clientOptions,
2932
...authOptions,

packages/mcp-server/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async function main() {
1818

1919
switch (options.transport) {
2020
case 'stdio':
21-
await launchStdioServer();
21+
await launchStdioServer(options);
2222
break;
2323
case 'http':
2424
await launchStreamableHTTPServer({

0 commit comments

Comments
 (0)