2026-06-19 · Architecture
Our MCP server is 160 lines
We put our whole CRM behind MCP last month — every table, document, dashboard, the chat, the calendar, the agents' own memory. 73 tools. I'd blocked out a week for it. It took an afternoon, and the server came out to 160 lines.
I keep going back to that number, so I want to write it down before it stops looking strange to me.
Here's the part that does the work. Not pseudocode — the file:
const mcpTools = AGENT_TOOLS
.map(openaiToMcpTool)
.filter(t => t && !SKIP_TOOLS.has(t.name));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const result = await executeTool(name, args || {}, 1, context);
return { content: [{ type: 'text', text: stringify(result) }] };
});
List the tools, run the tool. The schema converter is nine lines — it copies a name, a description and a JSON schema from one envelope into another:
function openaiToMcpTool(toolDef) {
const fn = toolDef.function;
if (!fn?.name) return null;
return {
name: fn.name,
description: fn.description || '',
inputSchema: fn.parameters || { type: 'object', properties: {} },
};
}
There's no MCP mode. No bridge, no translation layer. The tools were already defined as function schemas, because that's how our own agents have always called them. When a human adds a row in the UI it runs add_table_row. When an agent adds a row it runs add_table_row — the same executeTool, no second code path, no endpoint written specially for the bot. So when MCP came along there was nothing to build. We wrote a nine-line adapter and pointed it at the executor we'd been using for years.
A detail, for honesty in both directions. We skip about fifteen tools on purpose — the agents' private scaffolding (planning, orchestration) and the file tools, since a client like Claude Code has its own. And this isn't npx-and-go: the bridge boots the real backend and wants a Postgres behind it. It's a server to a running CRM, not a toy. The trade is that you get all 73 tools over real, typed, related state.
I don't think 160 lines is impressive by itself. It's small because the expensive decision was made years earlier, somewhere that has nothing to do with MCP. That's the part I find interesting — and it's easier to see in the repo than to explain in a post.
It's open source, MIT. The bridge and all 73 tool definitions are there.