Must I write the server myself, or are there ready-made ones?
Many common services (cloud drives, project management, messaging) already have official or community MCP servers you can connect directly without writing your own. You should write your own when connecting to a system that's internal to your company with no off-the-shelf option - your own database, internal API, private tools.
The principle: for standardized external services, prefer ready-made ones - faster and battle-tested; only when your need is specific enough that nothing existing fits is it worth building your own. Don't reinvent the wheel for a problem a ready-made option already solves.
Between stdio and SSE/HTTP transports, which should I pick?
Simple split: if the server runs on the same machine as Claude (e.g. your local desktop app), use stdio; if the server is elsewhere and connects over the network (e.g. deployed in the cloud for team use), use SSE/HTTP.
The deciding factor is "who uses it and from where." For solo local dev and testing, stdio is simplest - no networking or auth to handle. To serve a whole team or let a remote Claude reach it, you need HTTP, and then don't forget authentication, because once it's online anyone who can reach it could call your server, making permissions and identity verification even more important.
Beyond making tools read-only, what else matters for permissions?
Read-only is the first layer but not enough. A few more to consider: one, "data scope" - even read-only, limit what's queryable, e.g. only one department's data, not the whole database wide open. Two, "input validation" - don't assume the parameters Claude brings are clean; check and filter them like any external input.
Three, "audit logging" - record who called which tool when and what they queried, so you can investigate if something goes wrong. Stack these layers and the core idea is the same: your server should assume requests may be wrong or malicious rather than defaulting to trusting them.
Advanced: how should I write tool descriptions so Claude calls them correctly?
This is the invisible key to whether your server is usable, and it's often underrated. Claude relies on your tool descriptions to judge "when to use it and what parameters to pass." Write them vaguely and it guesses; guess wrong and you get those "called with odd parameters" problems.
Three keys to good tool descriptions: one, state the tool's purpose and fitting situations clearly so Claude knows when to choose it; two, annotate each parameter - what it is, what format, required or optional - so it doesn't fill blindly; three, if a parameter has constraints (date format, value range), write them right in the description.
A practical principle: treat the tool description as API documentation written for someone who can't see your internal system. They can only understand the tool through your words - the clearer you write, the more accurately Claude uses it.
When you want Claude to do more than chat - to actually look up data inside your company, like a customer database, an internal API, or private documents - what you need is an MCP server. It's the translator and gatekeeper between Claude and your internal tools: Claude says, in a standard way, "I want to use a tool," and your server checks permissions, actually queries, and hands the result back.
This is for people who already code. The goal is to make clear the skeleton of a custom MCP server, where permissions belong, and how to debug when things break. It won't pin to line-by-line code in one language; it explains what each piece is responsible for.
The chain is: Claude to your MCP server to your internal system, then back the same way. Claude never touches your database directly; it only tells your server "please use the query_customer tool with these parameters." Your server, on receiving that, first validates whether the request should be allowed, queries the internal system only if it passes, then formats the data into something Claude can read and returns it.
The benefit of this design: your sensitive credentials (database passwords, API keys) live only in your server and are never handed to Claude. Claude always accesses through the tools you define and can't reach the underlying layer.
First, tool definitions: you tell Claude which tools exist, what parameters each takes, and what they return. This is the "menu" you open to Claude - it can't order anything not on it. Second, the execution logic: the code that actually queries the database or calls the API when Claude picks a tool. Third, the transport: stdio for local, SSE/HTTP for remote, which determines how Claude talks to your server.
This is the easiest place to mess up. Never control permissions by "telling Claude please don't delete data" - that's a request, not a restriction. The real permission check must live in your server code: before executing any action, your code decides whether the operation is allowed. Make tools read-only, limit the data range queryable, reject dangerous operations outright. Put the gatekeeper in the server rather than hoping the model behaves.
Three common MCP debugging issues: one, "Claude can't see the tool," usually a tool definition not registered correctly or the transport not connecting; two, "the tool is called but with odd parameters," usually because your tool description wasn't clear enough and Claude guessed wrong; three, "Claude misreads what comes back," usually a messy return format. In practice, log every request and response on your server side first - nine out of ten problems are located just by reading the log.
If your team has piles of internal data scattered across systems and you want it queryable in natural language, a custom MCP server is the standard way to connect those tools to AI safely. The one guiding principle: design the server as a gatekeeper that distrusts external requests. Launch with read-only, low-risk tools first, confirm permissions and logging work, then gradually open more capability. Safety and control first, full features second - get the order wrong and the cost of an incident far exceeds the time you saved.