# How to Filter Exposed MCP Tools for Token-Constrained Environments

> Filter exposed MCP tools for token-constrained environments by specifying allowed tools via CLI flag or environment variable. Reduce LLM token consumption effectively.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: how-to-guide
- Published: 2026-08-18

---

**Reduce LLM token consumption by exposing only specific MCP tools using the `--tools` CLI flag or `CRG_TOOLS` environment variable.**

The Code Review Graph (CRG) server provides a rich set of Model Context Protocol (MCP) tools for graph-based code analysis, but full tool exposure can overwhelm token-limited LLMs. This guide explains how to filter exposed MCP tools for token-constrained environments using the built-in `_apply_tool_filter` mechanism in CRG's FastMCP implementation.

## How the Tool Filter Works

CRG implements tool filtering in [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py) through the `_apply_tool_filter` helper, invoked during server startup. The mechanism prunes the MCP tool inventory before any client connection occurs.

### Filter Execution Flow

- **Step 1: Resolve allow‑list** — The filter reads `--tools` from CLI arguments or the `CRG_TOOLS` environment variable, normalizing the comma-separated list (whitespace stripped, empty entries removed). Source: [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py) lines 381‑386.

- **Step 2: Iterate registered tools** — CRG loops through every tool registered on the `FastMCP` instance (`mcp`).

- **Step 3: Prune non-matching tools** — Tools not in the allow‑list are removed via `mcp.remove_tool(name)`, eliminating their descriptions from the MCP tool inventory entirely.

- **Step 4: Preserve async semantics** — The underlying coroutine implementations remain intact; only the registration is dropped.

- **Step 5: Default to full exposure** — If no filter is provided, all tools remain available.

This approach is **token‑efficient by design**: shrinking the tool inventory reduces the initial payload size, directly lowering token consumption for constrained LLMs.

## Filtering Methods

### CLI Flag: `--tools`

Pass a comma-separated list of tool names to keep:

```bash
code-review-graph serve --tools query_graph_tool,semantic_search_nodes

```

Only `query_graph_tool` and `semantic_search_nodes` remain exposed; all others are pruned from the MCP inventory.

### Environment Variable: `CRG_TOOLS`

Ideal for containerized deployments or CI pipelines:

```bash
export CRG_TOOLS="embed_graph_tool,get_review_context_tool"
code-review-graph serve

```

The server reads `CRG_TOOLS` and applies identical filtering logic before accepting connections.

### Programmatic Filtering

For embedded server scenarios, invoke the filter directly:

```python
from code_review_graph.main import crg_main

# Retain minimal tool set before client connections

crg_main._apply_tool_filter("list_graph_stats,visualize")

```

This manipulates the `FastMCP` instance in-process, ensuring filtered tool exposure for all subsequent clients.

## Verifying Filtered Tool Exposure

After server startup, connected MCP clients receive only the pruned tool list:

```python
client = mcp_client.connect()   # platform-specific connection

print(client.list_tools())

# Output: ['query_graph_tool', 'semantic_search_nodes']

```

The filtered inventory persists for the server's lifetime; restart with different parameters to adjust exposure.

## Implementation Details

| Component | Location | Purpose |
|-----------|----------|---------|
| `_apply_tool_filter` | [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py) lines 381‑386 | Core filtering logic |
| CLI argument parsing | [`code_review_graph/cli.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/cli.py) | Bridges `--tools` to internal filter |
| Test coverage | [`tests/test_main.py`](https://github.com/tirth8205/code-review-graph/blob/main/tests/test_main.py) lines 418‑442 | Validates filter behavior across input methods |

The filter integrates into the `serve` command initialization, ensuring trimmed tool exposure from the first client handshake regardless of transport (stdio or HTTP).

## Test Coverage

The test suite confirms three critical scenarios in [`tests/test_main.py`](https://github.com/tirth8205/code-review-graph/blob/main/tests/test_main.py):

- **`test_no_filter_keeps_all_tools`** (lines 418‑425): Verifies full tool retention when `crg_main._apply_tool_filter(None)` is called.

- **`test_filter_via_argument`** (lines 426‑432): Confirms CLI-based filtering with `crg_main._apply_tool_filter("query_graph_tool")`.

- **`test_filter_via_env_var`** (lines 434‑442): Validates `CRG_TOOLS` environment variable behavior using `monkeypatch.setenv`.

## Summary

- **Use `--tools`** for ad-hoc filtering via command line
- **Set `CRG_TOOLS`** for environment-based configuration in containers
- **Call `_apply_tool_filter`** programmatically for embedded deployments
- **Target [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py)** for source-level understanding of the filter mechanism

## Frequently Asked Questions

### What happens if I specify a tool name that doesn't exist?

The filter silently ignores non-existent names. Only matching registered tools are retained; unknown names simply result in no tools being exposed if no valid names remain in the allow‑list.

### Can I change the filter without restarting the server?

No. The `_apply_tool_filter` runs once during server initialization. Changing exposed tools requires a server restart with new `--tools` or `CRG_TOOLS` values.

### Does filtering affect tool execution performance?

No. Performance characteristics remain unchanged—the filter only removes registration metadata, not the underlying tool implementations. Async semantics for long-running tools are preserved.

### How do I discover available tool names for filtering?

Check the source code in [`code_review_graph/main.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/main.py) where tools are registered via decorators like `@mcp.tool()`, or run the server without filtering and query `client.list_tools()` to see the full inventory.