Building Your Own Search MCP Server with SerpStack as the Backend
A practical, step-by-step guide to building a search MCP server that calls SerpStack's API on the backend.
Why build your own instead of using a published one
Plenty of search MCP servers already exist (see our landscape roundup), but building your own is straightforward and gives you full control over the backend, the result shaping, and exactly what gets logged or cached. If SerpStack is already your search API of choice — see our SerpStack provider page — wrapping it in a small MCP server is a good way to make it available as a tool across Claude Desktop, Claude Code, and any other MCP client without maintaining a separate integration per client.
1. Server skeleton
Using an MCP SDK (TypeScript or Python both work fine here), a search MCP server needs three things: a server instance, a declared search tool with an input schema, and a handler that runs when the tool is called. In outline, using the TypeScript SDK's shape:
const server = new McpServer({ name: "serpstack-search", version: "1.0.0" });
server.registerTool("search", {
description: "Search Google via SerpStack and return structured results.",
inputSchema: {
query: z.string().describe("The search query"),
num: z.number().int().min(1).max(20).optional().default(5),
},
}, async ({ query, num }) => {
const results = await searchSerpStack(query, num);
return { content: [{ type: "text", text: JSON.stringify(results) }] };
});
2. Calling SerpStack server-side
The handler calls SerpStack's REST endpoint with your access key, passing the query and any parameters (result count, locale, etc.) the tool schema exposed:
async function searchSerpStack(query, num) {
const url = new URL("https://api.serpstack.com/search");
url.searchParams.set("access_key", process.env.SERPSTACK_KEY);
url.searchParams.set("query", query);
url.searchParams.set("num", String(num));
const resp = await fetch(url);
if (!resp.ok) throw new Error(`SerpStack request failed: ${resp.status}`);
const data = await resp.json();
return shapeResults(data);
}
3. Shaping and truncating results for the model
SerpStack's raw response includes more than a model needs. Map it down to the small set of fields worth spending context tokens on — title, URL, snippet — and cap the count to whatever the caller requested:
function shapeResults(data) {
const organic = (data.organic_results || []).map((r) => ({
title: r.title,
url: r.url,
snippet: r.snippet,
}));
return { results: organic };
}
Truncate long snippets to a fixed character budget, and consider dropping results with no snippet at all rather than passing the model an empty field it has to reason around.
4. Error handling
Return failures as structured tool output rather than letting the MCP call throw uncaught — a rate-limit response, a timeout, or a zero-results query should each come back as something the model can read and act on ("no results found, consider rephrasing the query") rather than an opaque protocol-level error. See designing a good MCP tool schema for search for more on shaping error responses.
5. Auth and key management
Keep the SerpStack access key server-side, read from an environment variable, never passed through from the MCP client or embedded in the tool schema. If the server runs locally (stdio transport), the key lives in the environment where the server process is launched, configured through whichever client is starting it. If you deploy it remotely instead, keep the key in your hosting platform's secret store rather than in source.
6. Deploying it
For local use, the server can just run over stdio from an npx-style command referenced in Claude Desktop's or Claude Code's MCP configuration — see connecting a search MCP server to Claude Desktop and Claude Code for that wiring. For shared or remote use, deploy it as a small always-on service exposing an MCP-compatible HTTP/SSE endpoint, behind your own auth, so multiple clients or teammates can point at the same server without each holding a copy of the SerpStack key. Either way, pair it with the caching and rate-limiting patterns in caching, rate-limiting, and cost control for MCP search servers so a chatty agent loop doesn't burn through your SerpStack quota unexpectedly.
Ready to get a key and try it? Sign up for SerpStack — its clean JSON response is a straightforward fit for the result-shaping step above.