GC-301d · Module 3
Tool Mapping & Design Patterns
3 min read
Tool mapping is the art of translating your internal API into MCP tool boundaries that align with how Gemini thinks. The wrong approach: one MCP tool per API endpoint, creating a 1:1 mirror. The right approach: group related operations into tools that match natural language tasks. Instead of separate get_customer, get_customer_mrr, get_customer_activity tools, create a customer_lookup tool that accepts a fields parameter and returns a unified response. Gemini works better with fewer, richer tools than many narrow ones.
Three design patterns dominate production MCP servers. The Query pattern: read-only tools that return data — search, lookup, list, aggregate. These are safe and should be your first tools. The Action pattern: tools that mutate state — create, update, deploy, configure. These need confirmation semantics and clear success/failure responses. The Pipeline pattern: tools that chain multiple operations — "run tests, if they pass, deploy to staging" — handled as a single tool with intermediate progress reporting.
// Pattern 1: Query — rich, flexible data retrieval
server.tool("search_services", {
description: "Search running microservices by name, status, or region. Returns service name, status, instance count, and last deploy time.",
query: z.string().optional().describe("Name substring match"),
status: z.enum(["healthy", "degraded", "down"]).optional(),
region: z.string().optional().describe("AWS region like us-east-1"),
}, async (params) => { /* ... */ });
// Pattern 2: Action — mutation with confirmation
server.tool("deploy_service", {
description: "Deploy a service to the specified environment. Returns deployment ID and status URL. Use search_services first to verify the service exists.",
service: z.string().describe("Service name (exact match)"),
environment: z.enum(["staging", "production"]),
version: z.string().describe("Git tag or commit SHA"),
}, async (params) => {
// Validate before mutating
const svc = await getService(params.service);
if (!svc) return { content: [{ type: "text", text: `Error: Service '${params.service}' not found. Use search_services to find the correct name.` }], isError: true };
const result = await deploy(params);
return { content: [{ type: "text", text: `Deployed ${params.service} v${params.version} to ${params.environment}. Status: ${result.url}` }] };
});
// Pattern 3: Pipeline — chained operations
server.tool("validate_and_deploy", {
description: "Run validation checks, then deploy if all pass. Returns check results and deployment status. Fails fast if any check fails.",
service: z.string(),
environment: z.enum(["staging", "production"]),
}, async (params) => { /* run checks, deploy if green */ });