
Services
One MCP server example with a tool, a resource, and a prompt. On mcp 2.3.0, unit_price(250, 3) returned 750, and a call missing quantity never reached the function.

An MCP server example worth copying registers three things in one file: a tool the model calls, a resource the host reads, and a prompt the host fills in. The notes echo already lives in how to build an MCP server. This page is the other three, tested on the Python SDK.
By Tayyab Javed, AI agent developer
Install mcp[cli]==2.3.0 on Python 3.11 or newer. Decorate one function with @mcp.tool(), one with @mcp.resource(), and one with @mcp.prompt(). I connected an in-process client to the server below. unit_price(250, 3) returned 750. A call missing quantity came back as an error before the function ran.
Tools are functions. Resources are data, read by URI. Prompts are reusable message templates. A server that only echoes a string teaches the tool and hides the other two. The file below is the set I actually imported. More of this cluster is on the MCP servers hub.
pip install "mcp[cli]==2.3.0"
from mcp.server import MCPServer
mcp = MCPServer("examples")
@mcp.tool()
def unit_price(cents: int, quantity: int) -> str:
"""Return the line total in cents."""
return str(cents * quantity)
@mcp.resource("policy://returns")
def returns_policy() -> str:
"""The return rule the model should quote."""
return "Returns are accepted within 30 days. Refunds go to the original payment method."
@mcp.prompt()
def explain_charge(sku: str) -> str:
"""Ask the model to explain one charge in plain language."""
return f"Explain the charge for {sku} in two sentences. Do not offer a refund."
if __name__ == "__main__":
mcp.run()
You do not write a JSON Schema. cents: int is the schema. The docstring is the description. On mcp 2.3.0, list_tools named the tool unit_price and required both integers. Calling it with {"cents": 250, "quantity": 3} returned the text 750. Reading policy://returns returned the 30-day sentence as text/plain. explain_charge with sku set to SKU-9 returned one user message: "Explain the charge for SKU-9 in two sentences. Do not offer a refund."
mcp.run() speaks stdio. A host such as Claude Desktop or Cursor starts the process and owns its lifetime. Log to stderr if you add logs. stdout is the protocol. I did not leave a process running. I connected mcp.Client to the server object in the same Python process, which is the SDK's in-process path, and called the three primitives from there.
A call with quantity missing did not reach unit_price. The client received is_error true and the text Error executing tool unit_price, naming quantity as a missing field. That is the check you want before a model is in the loop. Wiring a host, and the Inspector command, is the build guide linked above. When the tool should stay inside one program instead of a server, read MCP vs function calling.
Copy the three registrations. Do not copy a toy tool onto a refund, a file write, or a database update and call it done. This example only multiplies two integers and returns text. A write needs a person, a timeout, and a credential the model cannot see. The reference servers that already do those jobs are on the MCP server list.
I check the three registrations before I care which host will start the process. A tool is the only thing the model should call. A resource holds a rule the model should quote, not invent. A prompt is the template, and the constraint belongs in that template. The mistake is one function that tries to be all three, or a toy multiplier later pointed at a refund or a file write. My default is one of each, returning text, with the credential kept where the model cannot see it. I call them from an in-process client first, including a call that drops a required argument. I wire a host only after that error comes back before the function runs.
The file on this page is one tool, one resource, and one prompt on mcp 2.3.0. unit_price(250, 3) returned 750. The notes server, which is a single echo tool in Python and TypeScript, is a separate post.
Not this one. Stdio has no network, so there is nothing to authenticate. Put bearer-token checks on Streamable HTTP before anyone else can call the process.
Free 30-minute scoping call. We will pick the tool, the resource, and where a write has to stop for a person.
Book a Scoping Call