Claude Sonnet and many other LLMs ship with context windows in the 200k–1M token range, but that space fills up faster than you'd think when working with Model Context Protocol (MCP) servers. The official Azure DevOps MCP server is a perfect example of this problem: it exposes 70 tools covering builds, releases, repositories, wikis, test plans, and work items - consuming 55,125 tokens just to describe available functionality. The token count was measured against the Azure DevOps MCP server in late 2025; specific numbers will drift as Microsoft adds or removes tools, but the ratio of "tools you don't use" to "tools you actually call" stays roughly the same.
On a recent project planning session, I wanted to automate the creation of features, user stories, and tasks using this MCP server, and quickly realized I was burning more than a quarter of a 200k context window on tools I'd never use for this task (still meaningful even on a 1M window — that's room for the equivalent of a mid-sized codebase you're not planning to touch). I discovered this by running the /context command in Claude Code before even starting a conversation. It shows a visual breakdown of context usage, and the graph revealed just how much the Azure DevOps MCP server was consuming before I'd even added my codebase or documentation. This left less room for the actual work.
This guide shows you how I built a quick MCP proxy server using Node that filters the Azure DevOps server down to only the tools you need, cutting context consumption by up to 90% while maintaining full functionality.
The Context Window Problem
LLMs have finite context windows, and every token counts. Here's what the unfiltered Azure DevOps MCP server costs:
Total Tools: 70
Total Context Tokens: 55,125
Breakdown by Category:
├─ Core (3 tools): 2,098 tokens
├─ Work Iterations (3 tools): 2,259 tokens
├─ Work Items (19 tools): 14,377 tokens
├─ Build Pipeline (9 tools): 7,957 tokens
├─ Release Pipeline (2 tools): 2,400 tokens
├─ Repositories/PRs (18 tools): 13,767 tokens
├─ Wiki (5 tools): 3,589 tokens
├─ Test Plans (6 tools): 4,367 tokens
├─ Search (3 tools): 2,412 tokens
└─ Advanced Security (2 tools): 1,899 tokensBy filtering to just the core project tools, work item tracking, and search functionality, you can reduce this to approximately 25 tools consuming roughly 19,000 tokens - a 66% savings in context usage.
Architecture Overview
The proxy server architecture is straightforward:
Claude Code
↓
MCP Proxy Server (filters tools)
↓
Azure DevOps MCP Server (full tool set)
↓
Azure DevOps APIThe proxy acts as a transparent filter:
- Connects to the upstream Azure DevOps MCP server
- Retrieves the full tool list
- Filters to only desired tools (e.g., core**, wit*_, search__)
- Presents filtered tools to Claude
- Proxies tool execution requests through to upstream server
Implementation: Building the Proxy Server
Project Setup
Create a new Node.js project with the MCP SDK:
mkdir azure-devops-wit-proxy
cd azure-devops-wit-proxy
npm init -y
npm install @modelcontextprotocol/sdkUpdate package.json to enable ES modules:
{
"name": "azure-devops-wit-proxy",
"version": "1.0.0",
"type": "module",
"dependencies": {
"@modelcontextprotocol/sdk": "^1"
}
}Core Proxy Implementation
Create ado-wit-proxy.mjs:
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
import {
ListToolsRequestSchema,
CallToolRequestSchema,
} from '@modelcontextprotocol/sdk/types.js'
// Configuration
const AZURE_DEVOPS_ORG = 'YOUR_ORG' // Replace with your organization name
const TOOL_PREFIX_FILTER = ['core_', 'wit_', 'search_']
// Create proxy server
const server = new Server(
{ name: 'azure-devops-wit-proxy', version: '1.0.0' },
{ capabilities: { tools: {} } },
)
// Connect to actual Azure DevOps MCP server
const client = new Client(
{ name: 'proxy-client', version: '1.0.0' },
{ capabilities: {} },
)
let filteredTools = []
// List tools - filter to only WIT tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: filteredTools }
})
// Call tool - proxy through to actual server
server.setRequestHandler(CallToolRequestSchema, async (request) => {
return await client.callTool(request.params)
})
async function main() {
// Connect to the real Azure DevOps server
const isWindows = process.platform === 'win32'
let command, args
if (isWindows) {
command = 'cmd.exe'
args = ['/c', 'npx', '-y', '@azure-devops/mcp', AZURE_DEVOPS_ORG]
} else {
command = 'npx'
args = ['-y', '@azure-devops/mcp', AZURE_DEVOPS_ORG]
}
const transport = new StdioClientTransport({
command: command,
args: args,
env: process.env,
})
await client.connect(transport)
// Get all tools and filter
const result = await client.listTools()
filteredTools = result.tools.filter((tool) =>
TOOL_PREFIX_FILTER.some((prefix) => tool.name.startsWith(prefix)),
)
console.error(
`Filtered to ${filteredTools.length} tools of ${result.tools.length} total tools.`,
)
// Start proxy server
const serverTransport = new StdioServerTransport()
await server.connect(serverTransport)
}
main().catch(console.error)Configuration for Claude Code
Update your .claude.json to use the proxy:
{
"mcpServers": {
"azure-devops-wit": {
"command": "node",
"args": ["C:/path/to/azure-devops-wit-proxy/ado-wit-proxy.mjs"]
}
}
}The proxy automatically forwards environment variables to the upstream server, so authentication works seamlessly.
Advanced Filtering Patterns
Multiple Filter Categories
You can extend the proxy to support multiple tool categories:
const TOOL_FILTERS = {
wit: ['core_', 'wit_', 'search_'],
repos: ['core_', 'repo_'],
builds: ['core_', 'build_'],
}
// Get filter from environment variable or command line arg
const filterType = process.env.MCP_FILTER_TYPE || 'wit'
const TOOL_PREFIX_FILTER = TOOL_FILTERS[filterType]
filteredTools = result.tools.filter((tool) =>
TOOL_PREFIX_FILTER.some((prefix) => tool.name.startsWith(prefix)),
)Then configure which categories to enable:
{
"mcpServers": {
"azure-devops": {
"command": "node",
"args": ["C:/path/to/ado-wit-proxy.mjs"],
"env": {
"MCP_FILTER_TYPE": "wit"
}
}
}
}Tool Allowlist Configuration
For fine-grained control, implement an allowlist:
const ALLOWED_TOOLS = [
'core_get_projects',
'core_get_teams',
'wit_my_work_items',
'wit_get_work_item',
'wit_update_work_item',
'wit_create_work_item',
'search_work_items',
]
filteredTools = result.tools.filter((tool) => ALLOWED_TOOLS.includes(tool.name))This reduces to just 7 essential tools, further minimizing context usage.
Measuring the Impact
Let's compare context usage before and after implementing the proxy:
Before (Full Azure DevOps MCP Server)
Available Tools: 70
Context Tokens Used: 55,125 (~28% of a 200k window, ~5.5% of a 1M window)After (Filtered Core + WIT + Search Tools)
Available Tools: 25
Context Tokens Used: 18,987 (~9.5% of a 200k window, ~1.9% of a 1M window)
Tokens Reclaimed: 36,138 — measurable on any context size, but most
impactful on smaller windows where you'd otherwise be paying overhead
on every turn.Real-World Use Cases
Scenario 1: Sprint Planning Focus
A team using Azure DevOps primarily for sprint planning only needs:
const SPRINT_TOOLS = [
'core_list_projects',
'core_list_project_teams',
'work_list_team_iterations',
'wit_my_work_items',
'wit_get_work_items_for_iteration',
'wit_update_work_item',
'wit_create_work_item',
]Context Savings: 50,263 tokens (91% reduction) - Uses only 4,862 tokens
Scenario 2: Code Review Workflow
A team focused on pull request reviews needs:
const CODE_REVIEW_TOOLS = [
'core_list_projects',
'repo_list_repos_by_project',
'repo_list_pull_requests_by_repo',
'repo_get_pull_request_by_id',
'repo_list_pull_request_threads',
'repo_list_pull_request_thread_comments',
'repo_create_pull_request_thread',
'repo_reply_to_comment',
'repo_resolve_comment',
]Context Savings: 48,282 tokens (88% reduction) - Uses only 6,843 tokens
Scenario 3: Build Pipeline Management
A DevOps engineer managing builds needs:
const BUILD_TOOLS = [
'core_list_projects',
'pipelines_get_build_definitions',
'pipelines_get_builds',
'pipelines_get_build_status',
'pipelines_get_build_log',
'pipelines_run_pipeline',
'pipelines_update_build_stage',
]Context Savings: 49,722 tokens (90% reduction) - Uses only 5,403 tokens
Troubleshooting Common Issues
Proxy Doesn't Start
Symptom: No output when running the proxy script
Solutions:
- Verify Node.js version (requires v20+, same as the Azure DevOps MCP server)
- Check package.json has "type": "module"
- Ensure MCP SDK is installed correctly
- Try running with node --trace-warnings ado-wit-proxy.mjs
Tools Not Filtering
Symptom: Claude still sees all 70 tools
Solutions:
- Verify filter prefix matches tool names exactly
- Check console error output for filter results
- Add debug logging: console.error('Filtering:', tool.name)
Authentication Failures
Symptom: Tools execute but return authorization errors
Solutions:
- Verify you have authenticated using Azure CLI
- Ensure organization name is correct
Reclaim Your Context Window
MCP tool bloat is a real problem when working with comprehensive servers like Azure DevOps.
By implementing a simple proxy like the one in this guide, you can reclaim thousands of tokens of context window space for what matters: your code, documentation, and productive conversations with your LLM of choice.
The pattern is universal - apply it to any MCP server that exposes more tools than you need, and optimize your AI development workflow for maximum efficiency.
