Tuesday, May 27, 2025

Creating MCP Server and Client in python using FastMCP

 The jlowin-fastmcp https://github.com/jlowin/fastmcp project is a Python-based toolkit, licensed under Apache License Version 2.0, designed for building Model Context Protocol (MCP) servers and clients. FastMCP aims to simplify and accelerate the development of AI applications by providing a high-level, Pythonic interface for interacting with LLMs in a standardized way.

Key Features and Capabilities:

  • FastMCP Server: The core component for creating MCP applications, enabling the exposure of tools, resources, and prompts, and managing connections
  • Tools: Allows LLMs to execute Python functions (sync or async) for tasks like computations, API calls, or side effects, with automatic schema generation from type hints and docstrings Tools can return various data types, including text, JSON-serializable objects, and images
  • Resources & Templates: Provides read-only data sources accessible via URIs, supporting dynamic templates with placeholders for specific data subsets
  • Prompts: Defines reusable message templates to guide LLM interactions
  • Context: Enables access to MCP session capabilities within tools, resources, or prompts, offering functionalities like logging, LLM sampling, HTTP requests, resource access, and progress reporting
  • MCP Clients: Facilitates programmatic interaction with any MCP server, supporting various transports (Stdio, SSE, In-Memory) and advanced patterns like server-initiated LLM sampling requests. It also allows for efficient in-memory testing of servers.
  • Advanced Features:
    • Proxy Servers: Create an intermediary FastMCP server for other local or remote MCP servers, useful for bridging transports or adding logic.
    • Composing MCP Servers: Allows building modular applications by mounting multiple FastMCP instances onto a parent server.
    • OpenAPI & FastAPI Generation: Automatically generates FastMCP servers from existing OpenAPI specifications or FastAPI applications, integrating web APIs into the MCP ecosystem.

Project Structure:

The project directory (jlowin-fastmcp/) includes:

  • README.md: Provides an overview of the project, its features, installation instructions, and core concepts.
  • AGENTS.md: Offers guidance for LLM-driven engineering agents working within the repository.
  • justfile: Contains commands for building, testing, and type-checking the project.
  • LICENSE: Specifies the Apache License, Version 2.0 under which the project is distributed.
  • pyproject.toml: Defines project metadata, dependencies, scripts, and build system configuration.
  • uv.lock: A lock file for managing package dependencies and resolutions.
  • Windows_Notes.md: Contains notes specific to Windows environments.
  • .pre-commit-config.yaml: Configuration for pre-commit hooks, enforcing code formatting, linting, and type-checking.
  • .python-version: Specifies the Python version used[cite: 1].
  • docs/: Contains the project documentation in Mintlify-flavored Markdown, published to gofastmcp.com.
  • examples/: Provides minimal runnable demonstrations of FastMCP usage.
  • src/fastmcp/: Houses the main library source code.
    • client/: High-level client SDK and helpers.
    • cli/: Command-line interface related code.
    • contrib/: Contains contributions like bulk_tool_caller and mcp_mixin.
    • low_level/: Low-level implementations.
    • prompts/: Prompt templates and manager.
    • resources/: MCP resources, resource templates, and manager.
    • server/: Server implementation, FastMCP, authentication, and networking components.
    • tools/: Tool implementations and manager.
    • utilities/: General utility functions and modules.
  • tests/: Comprehensive Pytest test suite for the project.
  • .cursor/: Contains rules for core MCP objects.
  • .github/: Includes GitHub-specific configurations like labeler, release workflows, and issue templates.

Installation and Development:

FastMCP can be installed using uvuv pip install fastmcp. For development, Python 3.10+ anduvare recommended. The development workflow involves installing dependencies (uv sync), running static checks (uv run pre-commit run –all-files), and executing unit tests (uv run pytest`). All pull requests are expected to include appropriate tests and pass all static checks.

in this Blog we are going to use this FastMcp to creat an MCP server from the restAPI exposed https://restful-api.dev/
. Furhter we will consume the same MCPServer using MCP client. You can also use the other management window like cursor, void ide, Trea, VS code pluin like timmy or roo code/roo cline etc.

To create an MCP server and client using FastMCP that interfaces with the given REST API endpoints, follow these steps:


✅ Objective

Wrap this REST API:
📍 https://api.restful-api.dev/objects
Into MCP tools and resources using FastMCP, so that clients can call them via the MCP protocol.


📁 Project Layout

We’ll create two key files:

  • server.py – Defines the MCP server exposing tools to access the REST API.
  • client.py – A FastMCP client that calls those tools.

🔧 server.py – MCP Server with Tools & Context

server.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from fastmcp import FastMCP, Context
import httpx
 
mcp = FastMCP(
    name="RESTful API Wrapper <img draggable="false" role="img" class="emoji" alt="🌐" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f310.svg">",
    description=(
        "This MCP server wraps a public REST API (https://api.restful-api.dev/objects) "
        "and exposes it as a set of MCP tools. It allows clients to fetch, create, update, "
        "patch, and delete object data using MCP protocol functions. "
        "Each tool uses the context (ctx) to log messages and send HTTP requests."
    ),
)
 
 
@mcp.tool()
async def get_all_objects(ctx: Context):
    async with httpx.AsyncClient() as client:
        resp = await client.get(BASE_URL)
        print("get_all_objects",resp.json())
    return resp.json()
 
@mcp.tool()
async def get_objects_by_ids(ids: list[str], ctx: Context):
    query = "&".join([f"id={i}" for i in ids])
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"{BASE_URL}?{query}")
        print("get_objects_by_ids",resp.json())
    return resp.json()
 
@mcp.tool()
async def get_object_by_id(object_id: str, ctx: Context):
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"{BASE_URL}/{object_id}")
        print("get_object_by_id",resp.json())
    return resp.json()
 
@mcp.tool()
async def add_object(data: dict, ctx: Context):
    async with httpx.AsyncClient() as client:
        resp = await client.post(BASE_URL, json=data)
        print("add_object",resp.json())
    return resp.json()
 
@mcp.tool()
async def update_object(object_id: str, data: dict, ctx: Context):
    async with httpx.AsyncClient() as client:
        resp = await client.put(f"{BASE_URL}/{object_id}", json=data)
        print("update_object",resp.json())
    return resp.json()
 
@mcp.tool()
async def patch_object(object_id: str, data: dict, ctx: Context):
    async with httpx.AsyncClient() as client:
        resp = await client.patch(f"{BASE_URL}/{object_id}", json=data)
        print("patch_object",resp.json())
    return resp.json()
 
@mcp.tool()
async def delete_object(object_id: str, ctx: Context):
    async with httpx.AsyncClient() as client:
        resp = await client.delete(f"{BASE_URL}/{object_id}")
        print("delete_object",resp.json())
    return {"status_code": resp.status_code, "message": "Deleted" if resp.status_code == 200 else "Failed"}
 
if __name__ == "__main__":
    mcp.run(transport="streamable-http", host="127.0.0.1", port=8000, path="/mcp")

👤 client.py – Call Tools via FastMCP Client

client.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import asyncio
from fastmcp import Client
import json
 
 
async def main():
    async with Client("http://127.0.0.1:8000/mcp") as client:
        # Test GET all
        # all_objects = await client.call_tool("get_all_objects", {})  
        # print("All objects:", all_objects)
 
        # Test POST
        new_obj = {"name": "MCP Widget", "data": {"type": "demo"}}
        created = await client.call_tool("add_object", {"data": new_obj})
        print("Created object:", created)
 
        # 1. Access the TextContent object (it's the first and only item in the list)
        text_content_obj = created[0]
 
        # 2. Get the 'text' attribute, which contains the JSON string
        json_string = text_content_obj.text
 
        # 3. Parse the JSON string into a Python dictionary
        parsed_data = json.loads(json_string)
 
        # 4. # Extract ID Fetch the 'id' from the dictionary
        object_id = parsed_data.get("id")
 
        print("Fetched created_id:", object_id)
        # Test GET single
        single = await client.call_tool("get_object_by_id", {"object_id": object_id})
        print("Fetched single:", single)
 
        # Test PUT
        updated = await client.call_tool("update_object", {
            "object_id": object_id,
            "data": {"name": "Updated Widget", "data": {"type": "full update"}}
        })
        print("Updated:", updated)
 
        # Test PATCH
        patched = await client.call_tool("patch_object", {
            "object_id": object_id,
            "data": {"name": "Partially Updated Widget"}
        })
        print("Patched:", patched)
 
        # Test DELETE
        deleted = await client.call_tool("delete_object", {"object_id": object_id})
        print("Deleted:", deleted)
 
if __name__ == "__main__":
    asyncio.run(main())

📦 requirements.txt

fastmcp
asyncio

✅ How to Run

  1. Install FastMCP:
1
uv pip install fastmcp

or pip install -r requitements.txt

  1. Start the MCP Server:
1
python server.py
  1. Run the Client:
1
python client.py

📦 Optional: Mount in a Cloud Management Window

If you want this server to be mountable under a parent cloud server:

1
2
3
4
5
6
7
8
9
# parent.py
from fastmcp import FastMCP
from server import mcp as rest_api_server
 
parent = FastMCP("Cloud Control Panel <img draggable="false" role="img" class="emoji" alt="🌩️" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f329.svg">")
parent.mount("rest", rest_api_server)
 
if __name__ == "__main__":
    parent.run(transport="streamable-http", host="0.0.0.0", port=9000)

💡 What Context Gives You in FastMCP

We can also add context to the file. To add context support in server.py, you simply need to use the Context object from FastMCP and accept it as a parameter in any tool where you want to use it. This context allows:

Feature Example
Logging await ctx.info(“message”)
HTTP Request await ctx.http_request(…)
LLM Sampling await ctx.sample(“prompt”)
Resource Access await ctx.read_resource(uri)
Progress Reports await ctx.report_progress(x, total)

server output :-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
get_all_objects [{'id': '1', 'name': 'Google Pixel 6 Pro', 'data': {'color': 'Cloudy White', 'capacity': '128 GB'}}, {'id': '2', 'name'
: 'Apple iPhone 12 Mini, 256GB, Blue', 'data': None}, {'id': '3', 'name': 'Apple iPhone 12 Pro Max', 'data': {'color': 'Cloudy White',
'capacity GB': 512}}, {'id': '4', 'name': 'Apple iPhone 11, 64GB', 'data': {'price': 389.99, 'color': 'Purple'}}, {'id': '5', 'name': '
Samsung Galaxy Z Fold2', 'data': {'price': 689.99, 'color': 'Brown'}}, {'id': '6', 'name': 'Apple AirPods', 'data': {'generation': '3rd
', 'price': 120}}, {'id': '7', 'name': 'Apple MacBook Pro 16', 'data': {'year': 2019, 'price': 1849.99, 'CPU model': 'Intel Core i9', '
Hard disk size': '1 TB'}}, {'id': '8', 'name': 'Apple Watch Series 8', 'data': {'Strap Colour': 'Elderberry', 'Case Size': '41mm'}}, {'
id': '9', 'name': 'Beats Studio3 Wireless', 'data': {'Color': 'Red', 'Description': 'High-performance wireless noise cancelling headpho
nes'}}, {'id': '10', 'name': 'Apple iPad Mini 5th Gen', 'data': {'Capacity': '64 GB', 'Screen size': 7.9}}, {'id': '11', 'name': 'Apple
 iPad Mini 5th Gen', 'data': {'Capacity': '254 GB', 'Screen size': 7.9}}, {'id': '12', 'name': 'Apple iPad Air', 'data': {'Generation': '4th', 'Price
': '419.99', 'Capacity': '64 GB'}}, {'id': '13', 'name': 'Apple iPad Air', 'data': {'Generation': '4th', 'Price': '519.99', 'Capacity': '256 GB'}}]
?[32mINFO?[0m:     127.0.0.1:54901 - "?[1mPOST /mcp HTTP/1.1?[0m" ?[33m307 Temporary Redirect?[0m
?[32mINFO?[0m:     127.0.0.1:54901 - "?[1mPOST /mcp/ HTTP/1.1?[0m" ?[32m200 OK?[0m
add_object {'id': 'ff80818196f2a23f01971166e24b3cc4', 'name': 'MCP Widget', 'createdAt': '2025-05-27T11:00:44.747+00:00', 'data': {'type': 'demo'}}
?[32mINFO?[0m:     127.0.0.1:54906 - "?[1mPOST /mcp HTTP/1.1?[0m" ?[33m307 Temporary Redirect?[0m
?[32mINFO?[0m:     127.0.0.1:54906 - "?[1mPOST /mcp/ HTTP/1.1?[0m" ?[32m200 OK?[0m
get_object_by_id {'id': 'ff80818196f2a23f01971166e24b3cc4', 'name': 'MCP Widget', 'data': {'type': 'demo'}}
?[32mINFO?[0m:     127.0.0.1:54915 - "?[1mPOST /mcp HTTP/1.1?[0m" ?[33m307 Temporary Redirect?[0m
?[32mINFO?[0m:     127.0.0.1:54915 - "?[1mPOST /mcp/ HTTP/1.1?[0m" ?[32m200 OK?[0m
update_object {'id': 'ff80818196f2a23f01971166e24b3cc4', 'name': 'Updated Widget', 'updatedAt': '2025-05-27T11:00:47.138+00:00', 'data': {'type': 'fu
ll update'}}
?[32mINFO?[0m:     127.0.0.1:54917 - "?[1mPOST /mcp HTTP/1.1?[0m" ?[33m307 Temporary Redirect?[0m
?[32mINFO?[0m:     127.0.0.1:54917 - "?[1mPOST /mcp/ HTTP/1.1?[0m" ?[32m200 OK?[0m
patch_object {'id': 'ff80818196f2a23f01971166e24b3cc4', 'name': 'Partially Updated Widget', 'updatedAt': '2025-05-27T11:00:48.206+00:00', 'data': {'t
ype': 'full update'}}
?[32mINFO?[0m:     127.0.0.1:54919 - "?[1mPOST /mcp HTTP/1.1?[0m" ?[33m307 Temporary Redirect?[0m
?[32mINFO?[0m:     127.0.0.1:54919 - "?[1mPOST /mcp/ HTTP/1.1?[0m" ?[32m200 OK?[0m
delete_object {'message': 'Object with id = ff80818196f2a23f01971166e24b3cc4 has been deleted.'}
?[32mINFO?[0m:     127.0.0.1:54923 - "?[1mDELETE /mcp HTTP/1.1?[0m" ?[33m307 Temporary Redirect?[0m
?[32mINFO?[0m:     127.0.0.1:54923 - "?[1mDELETE /mcp/ HTTP/1.1?[0m" ?[32m200 OK?[0m

Client Output:-

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(env) C:\vscode-python-workspace\FastMCP-Server-Client>python client.py
All objects: [TextContent(type='text', text='[\n  {\n    "id": "1",\n    "name": "Google Pixel 6 Pro",\n    "data": {\n      "color": "
Cloudy White",\n      "capacity": "128 GB"\n    }\n  },\n  {\n    "id": "2",\n    "name": "Apple iPhone 12 Mini, 256GB, Blue",\n    "da
ta": null\n  },\n  {\n    "id": "3",\n    "name": "Apple iPhone 12 Pro Max",\n    "data": {\n      "color": "Cloudy White",\n      "cap
acity GB": 512\n    }\n  },\n  {\n    "id": "4",\n    "name": "Apple iPhone 11, 64GB",\n    "data": {\n      "price": 389.99,\n      "c
olor": "Purple"\n    }\n  },\n  {\n    "id": "5",\n    "name": "Samsung Galaxy Z Fold2",\n    "data": {\n      "price": 689.99,\n      "color": "Brow
n"\n    }\n  },\n  {\n    "id": "6",\n    "name": "Apple AirPods",\n    "data": {\n      "generation": "3rd",\n      "price": 120\n    }\n  },\n  {\n
    "id": "7",\n    "name": "Apple MacBook Pro 16",\n    "data": {\n      "year": 2019,\n      "price": 1849.99,\n      "CPU model": "Intel Core i9",
\n      "Hard disk size": "1 TB"\n    }\n  },\n  {\n    "id": "8",\n    "name": "Apple Watch Series 8",\n    "data": {\n      "Strap Colour": "Elderb
erry",\n      "Case Size": "41mm"\n    }\n  },\n  {\n    "id": "9",\n    "name": "Beats Studio3 Wireless",\n    "data": {\n      "Color": "Red",\n
   "Description": "High-performance wireless noise cancelling headphones"\n    }\n  },\n  {\n    "id": "10",\n    "name": "Apple iPad Mini 5th Gen",\
n    "data": {\n      "Capacity": "64 GB",\n      "Screen size": 7.9\n    }\n  },\n  {\n    "id": "11",\n    "name": "Apple iPad Mini 5th Gen",\n
"data": {\n      "Capacity": "254 GB",\n      "Screen size": 7.9\n    }\n  },\n  {\n    "id": "12",\n    "name": "Apple iPad Air",\n    "data": {\n
    "Generation": "4th",\n      "Price": "419.99",\n      "Capacity": "64 GB"\n    }\n  },\n  {\n    "id": "13",\n    "name": "Apple iPad Air",\n
"data": {\n      "Generation": "4th",\n      "Price": "519.99",\n      "Capacity": "256 GB"\n    }\n  }\n]', annotations=None)]
Created object: [TextContent(type='text', text='{\n  "id": "ff80818196f2a23f01971166e24b3cc4",\n  "name": "MCP Widget",\n  "createdAt": "2025-05-27T1
1:00:44.747+00:00",\n  "data": {\n    "type": "demo"\n  }\n}', annotations=None)]
Fetched created_id: ff80818196f2a23f01971166e24b3cc4
Fetched single: [TextContent(type='text', text='{\n  "id": "ff80818196f2a23f01971166e24b3cc4",\n  "name": "MCP Widget",\n  "data": {\n    "type": "de
mo"\n  }\n}', annotations=None)]
Updated: [TextContent(type='text', text='{\n  "id": "ff80818196f2a23f01971166e24b3cc4",\n  "name": "Updated Widget",\n  "updatedAt": "2025-05-27T11:0
0:47.138+00:00",\n  "data": {\n    "type": "full update"\n  }\n}', annotations=None)]
Patched: [TextContent(type='text', text='{\n  "id": "ff80818196f2a23f01971166e24b3cc4",\n  "name": "Partially Updated Widget",\n  "updatedAt": "2025-
05-27T11:00:48.206+00:00",\n  "data": {\n    "type": "full update"\n  }\n}', annotations=None)]
Deleted: [TextContent(type='text', text='{\n  "status_code": 200,\n  "message": "Deleted"\n}', annotations=None)]

Source Code:- https://github.com/shdhumale/FastMCP-Server-Client