Who this is for and what you will be able to do
If you already use large language models and want them to call tools you wrote or read data you control, this guide is for you. By the end you will have an MCP development environment, a calculator server with an add tool and a greeting resource, a way to test it with MCP Inspector, and a sense of where setup usually goes wrong.[1]
What MCP is
The Model Context Protocol (MCP) is an open protocol that standardizes how applications provide context to LLMs. One way to picture it is as a USB-C port for AI applications: a single standard way to connect AI models to different data sources and tools.[1]
An MCP server typically includes server configuration (port, authentication and other settings), resources (data and context made available to LLMs), tools (functionality that models can invoke) and prompts (templates for generating or structuring text).[1] Tools and resources placed on a server can be used by any client, with or without an LLM.[1]
The benefit is reuse. Once tools and resources sit on a server, any client can call them, so you do not have to rewrite the same integration for every application. The exercise below follows that idea: build a minimal server first, then let a client call it.[1]
Prerequisites
- A development environment for C#, Java, Python, TypeScript or Rust.
- An editor or IDE such as Visual Studio Code, PyCharm or IntelliJ.
- A package manager that matches your language: NuGet, Maven/Gradle, pip, npm/yarn or Cargo.
- API keys, if your host application calls AI services.[1]
The exercise builds a calculator server in five steps: create the project, add dependencies, create the project files, write the server code, and test it. The TypeScript and Python paths come first, followed by Java, Rust and .NET.[1]
Step 1: set up a TypeScript project
Create a project directory and initialize an npm project:[1]
# Create project directory and initialize npm project
mkdir calculator-server
cd calculator-server
npm init -yInstall the MCP SDK and Zod, which the example uses for schema validation:[1]
# If not already installed, install TypeScript globally
npm install typescript -g
# Install the MCP SDK and Zod for schema validation
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescriptThe example package.json sets build to tsc and start to npm run build && node ./build/index.js, and pins @modelcontextprotocol/sdk at ^1.16.0 and zod at ^3.25.76.[1] The tsconfig.json sets outDir to ./build, rootDir to ./src, strict to true, target to ES2022, and both module and moduleResolution to Node16.[1]
The two files do different jobs: package.json manages dependencies and scripts, while tsconfig.json controls compilation. If the compile settings do not match, the usual symptom is an empty build directory or a missing entry file, so check both files against the example before going further.[1]
The server code starts with the SDK imports and the server object:[1]
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "Calculator MCP Server",
version: "1.0.0"
});The name and version passed to McpServer are the name and version the server reports. StdioServerTransport is the transport that connects the server to standard input and output, and the final server.connect(transport) call is what starts the server receiving messages.[1]
Next, add an add tool and a greeting resource. The tool takes two numbers, a and b. The resource is addressed through greeting://{name} and takes a name parameter.[1]
server.tool(
"add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
server.resource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
async (uri, { name }) => ({
contents: [{
uri: uri.href,
text: `Hello, ${name}!`
}]
})
);In the tool, server.tool takes the tool name add, an input definition in which a and b are declared as numbers with z.number(), and a handler. The handler returns an array whose element has type: "text", a plain text result. The resource returns uri and text: the URI identifies the resource and text holds its content.[1]
Declaring parameter types in the tool definition tells callers what to send, and the SDK can check incoming arguments against that definition.[1]
Step 2: the same server in Python
The Python version is shorter. Create a virtual environment and install mcp[cli]. The second line of the block below is the Windows activation command:[1]
# Create a virtual env and install dependencies
python -m venv venv
venv\Scripts\activate
pip install "mcp[cli]"In the server file, create a FastMCP server named Demo:[1]
# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")FastMCP is the Python SDK's server class, and the string Demo passed to it is the server's name. The tools and resources below are registered on this mcp object.[1]
Use the @mcp.tool() decorator to register the add function as a tool, and @mcp.resource("greeting://{name}") to register the greeting resource:[1]
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"The Python version has no separate registration step; the decorators do the registration. The type annotations on add (both parameters and the return value are int) and the docstring above the function, which states what the tool does, make up the tool's description. The name parameter of the greeting://{name} resource comes from the part of the URI inside the braces, and the function returns a greeting.[1]
Build, run and test
Both versions do the same thing: they register an addition tool and a greeting resource. The difference lies in the workflow. The TypeScript version compiles source with tsc into the build directory and runs the compiled build/index.js, with dependencies managed by npm. The Python version needs no compile step, installs its dependencies with pip, and starts with the mcp command.[1]
For TypeScript, compile with npm run build first. For Python, start the server with mcp run server.py. If you want the Inspector, use mcp dev server.py instead: it launches the Inspector and supplies the proxy session token it needs. With mcp run, you start the Inspector yourself and configure the connection.[1]
After you change code, recompile (TypeScript) or restart the server (Python) before testing, or you may be testing an old build.
Test with MCP Inspector
MCP Inspector is a visual testing tool. It discovers a server's tools, resources and prompts, lets you try tool execution with different parameters, and shows server info, schemas and configuration.[1][2] Its source is the modelcontextprotocol/inspector repository on GitHub.[2]
For the TypeScript build, start the Inspector with:[1]
npx @modelcontextprotocol/inspector node build/index.jsThe Inspector is a testing aid. It connects to the server, lists its tools and resources, and lets you call them by hand. It does not replace automated tests or check your business logic. Trying different numbers for the two arguments of add and checking the results is the simplest first round.
Java, Rust and .NET
This section only covers how to start in each language. The Java and Rust server code is longer, so after reading the TypeScript or Python path, the other languages will be easier to follow.
The Java example is generated with Spring Boot and depends on Spring AI. Its calculator service exposes add, subtract, multiply, divide, power, square root, modulus and absolute value as methods annotated with @Tool. Build and run it with:[1]
./mvnw clean install -DskipTests
java -jar target/calculator-server-0.0.1-SNAPSHOT.jarThe Java HTTP example uses the legacy HTTP+SSE transport, which targets an SDK compatible with MCP 2025-11-25. For new remote servers, use the 2026-07-28 Streamable HTTP transport instead.[1]
For Rust, add the dependencies with cargo add:[1]
cargo add rmcp --features server,transport-io
cargo add serde
cargo add tokio --features rt-multi-threadThen format the code with cargo fmt and run it with cargo run.[1]
For .NET, create a console project:[1]
dotnet new console -n McpCalculatorServer
cd McpCalculatorServerCommon setup problems
Most problems follow one order when you troubleshoot: first confirm the server process is actually running, then check the port, and finally look at dependencies and cross-origin settings.[1]
- Connection refused: check that the server is running and that the port is correct.[1]
- CORS errors: configure proper CORS headers for cross-origin requests.[1]
- Server not starting: check for port conflicts or missing dependencies.[1]
If several local services run at once, a port conflict is the most common cause. Dependencies must be installed in the environment of the current project; switch to a different terminal or virtual environment and the dependencies may not be found.[1]
Official SDKs and next steps
MCP has official SDKs for C# (maintained with Microsoft), Java (maintained with Spring AI), TypeScript, Python, Kotlin, Swift (maintained with Loopwork AI) and Rust.[1] This guide stops at a local server and a local test. Pick the SDK for a language you know, connect a tool that does something useful for you, then use the Inspector to check its inputs and outputs.
Adapted from lesson 3 of Microsoft's open-source course *MCP for Beginners*[1], released under the MIT License.
Sources
- 1Getting Started with MCPMicrosoft mcp-for-beginners (MIT)
- 2MCP InspectorModel Context Protocol
Cite this page: Your first MCP server, from zero to a running build. TellWorld, https://www.tellworld.net/practice/practice-mcp-first-server. Updated 2026-11-14