An MCP Server in Python: Building One From Scratch
What a minimal Python MCP server consists of, how tools and their schemas are declared, how to run and connect it, and how to test it before handing it to an agent.
All articles in the guide MCP-серверы · 11
A minimal MCP server takes an evening. Here is what it consists of and where the first run goes wrong.
Decisions before code
Three choices that shape the outcome more than the language or library.
Which tools to declare. Not a mirror of your API but operations phrased in terms of the agent’s tasks. Covered in wrapping an API.
How to describe them. The agent chooses by description. This is the real work.
Which permissions are needed. Start with reads. Write access is added separately and deliberately.
A minimal server
The official Python SDK handles the protocol, leaving you to declare tools. The shape is:
- Create a server object with a name.
- Declare each tool as a function: name, description, argument types, body.
- Start the server and let it listen on standard input.
A typical tool looks like an ordinary function with type annotations and a docstring. The SDK turns annotations into an argument schema and the docstring into the description the agent sees. The practical consequence: the docstring here is not a comment but working text that determines agent behaviour.
Things worth doing immediately:
- Annotate argument types explicitly. Without them the schema is vague and the agent passes the wrong things.
- Enumerate allowed values wherever the set is finite. A
statusparameter with no enumeration guarantees mistakes. - Return structure, not formatted prose. The agent can parse prose, but structure is more reliable and shorter.
- Return errors as explanatory text, not as an empty result.
Running and connecting
A local server is launched by the client as a child process and talks over standard streams. Three rules follow:
- Print nothing to stdout. The most common mistake: one debug line breaks the protocol and the client shows the server as failed. Logs go to stderr or a file.
- Test the launch by hand before connecting. The server should start and wait for input, not exit.
- Use the full path to the interpreter in the config when in doubt: the client launches the process in its own environment, where
PATHmay differ.
The connection procedure is in how to connect a server, and configuration and variables in setup.
Testing
Before granting the server real permissions, check three things.
The tools work when called directly. Call the functions as ordinary Python functions and confirm the behaviour. That separates logic errors from protocol errors.
The server exposes its tool list. After connecting, ask the agent what it has. An empty list on a live server is almost always an authentication failure.
The agent chooses correctly. Give it several typical requests and watch which tool fires. If it picks wrongly, fix the description rather than the prompt. This is the most informative test, and the one most often skipped.
A good habit: log calls with their arguments to a file. Half the problems turn out to be not in the code but in the agent passing something you did not expect.
What to add once the basics work
- Response size limits. Mandatory: the response enters context whole.
- Confirmation for dangerous operations. Not “a tool with a warning in its description” but a separate step.
- Authentication through environment variables, if the server reaches an external system.
- Timeout handling. An external call that hangs hangs the agent with it.
A worked example on a specific system: an MCP server for 1C. Security and permissions have their own article. The overview is in the MCP guide.
FAQ
Is writing your own MCP server hard?
No, a minimal server is a few dozen lines: declare a tool, describe its arguments, return a result. The real work is not the code but deciding which tools to declare and how to describe them so the agent chooses correctly.
What language should an MCP server be written in?
The one the thing it exposes is written in. Official SDKs exist for Python and TypeScript; Python is convenient when the server talks to a database or to systems that already have libraries.
Why does my server start but stay invisible to the client?
Usually because stray text reached standard output. Stdout belongs to the protocol, and any debug print breaks the exchange. Everything diagnostic must go to stderr or to a file.
- MCP Servers: What They Are and Why They ExistGuide
- An MCP Server for 1C: Connecting an Agent to an Accounting SystemWhat users of the 1C accounting platform want from an AI agent, the options for reaching the data, how to wrap HTTP services and OData, and where the permission and security lines sit.
- How to Connect an MCP Server to Claude Code and CursorWhere MCP configuration lives, how to connect a server step by step, how to confirm the agent really sees it, and what to do when the server never appears.
- Running a Local MCP ServerHow a local MCP server differs from a remote one, how it is launched, how to limit its file access, and how to debug it when the client shows no errors.
Done for you
I will connect your services and data to AI through MCP
A custom MCP server for your CRM, database or internal API, with access rules and logs.
from $1,500 · 1 to 2 weeks