I'm experimenting with building a codemode MCP server and have a few tools I was handrolling OpenAPI -> MCP for. I was going to try the openApiMcpServer as a plug-and-play solution for quickly experimenting with code mode, but for a variety of reasons we want to expose only a subset of routes to the agent. We can obviously just prune the json itself before running, which is fine for stable interfaces, but becomes troublesome for interfaces that somewhat regularly change.
I discussed this with Claude, who believed the change wouldn't be so complex. I've shared the relevant snippets below (I'm not well versed enough in TypeScript to fully understand what is being suggested).
Claude:
Very small change. The filtering needs to happen in two places:
- Spec pruning (line 380) — strip paths/operations from the spec before
resolveRefs so the search tool never sees them
- Request guard (line 478) — reject paths at runtime in the
execute tool so the agent can't call a blocked route even if it guesses the path
The interface I'd propose is a predicate function — more flexible than allowlist/blocklist arrays and idiomatic for this codebase:
export interface OpenApiMcpServerOptions {
spec: Record<string, unknown>;
executor: Executor;
request: (options: RequestOptions) => Promise<unknown>;
/**
* Filter which operations are exposed. Return false to exclude.
* Applied before $ref resolution — excluded operations never enter agent context.
* Also enforced at request time as a safety net.
*/
filterOperation?: (method: string, path: string, operation: { operationId?: string; tags?: string[] }) => boolean;
// ... existing fields
}
The implementation is ~15 lines — prune spec.paths before resolving, and wrap requestFn to check the path/method at runtime. Something like:
// Before resolveRefs:
if (options.filterOperation) {
const pruned = structuredClone(options.spec);
for (const [path, pathItem] of Object.entries(pruned.paths ?? {})) {
for (const method of ["get","post","put","patch","delete"]) {
const op = pathItem[method];
if (op && !options.filterOperation(method, path, op)) delete pathItem[method];
}
if (!Object.keys(pathItem).some(k => ["get","post","put","patch","delete"].includes(k))) {
delete pruned.paths[path];
}
}
// use pruned instead of options.spec
}
// In execute tool, wrap requestFn:
const guardedRequest = options.filterOperation
? (opts: RequestOptions) => {
// find matching path in spec, check filter — reject if blocked
}
: requestFn;
Would this be something worth adding? Thanks in advance.
I'm experimenting with building a codemode MCP server and have a few tools I was handrolling OpenAPI -> MCP for. I was going to try the
openApiMcpServeras a plug-and-play solution for quickly experimenting with code mode, but for a variety of reasons we want to expose only a subset of routes to the agent. We can obviously just prune the json itself before running, which is fine for stable interfaces, but becomes troublesome for interfaces that somewhat regularly change.I discussed this with Claude, who believed the change wouldn't be so complex. I've shared the relevant snippets below (I'm not well versed enough in TypeScript to fully understand what is being suggested).
Claude:
Very small change. The filtering needs to happen in two places:
resolveRefsso thesearchtool never sees themexecutetool so the agent can't call a blocked route even if it guesses the pathThe interface I'd propose is a predicate function — more flexible than allowlist/blocklist arrays and idiomatic for this codebase:
The implementation is ~15 lines — prune
spec.pathsbefore resolving, and wraprequestFnto check the path/method at runtime. Something like:Would this be something worth adding? Thanks in advance.