1. Conduid
  2. Developer Tools
  3. Durable MCP Python
MCP server · Developer Tools

Durable MCP Python

Reboot library for MCP, leverages Reboot's durability and workflows for safer, scalable MCP servers.

Unclaimed Apache-2.0 last commit 8 months ago devtools
58Fair

Scored 2 days ago · breakdown

About Durable MCP Python

Durable MCP Python is an MCP server published by reboot-dev in the Developer Tools category: reboot library for MCP, leverages Reboot's durability and workflows for safer, scalable MCP servers. It has been installed 0 times through Conduid.

The repository has 13 stars and 1 forks, with the last commit 8 months ago. Six months or more without a commit doesn't mean the server is broken, but check the open issues (0) before depending on it in production.

Install

Install
npx durable-mcp-python

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

Ask AI about Durable MCP Python

Powered by Claude · Grounded in docs

I know everything about Durable MCP Python. Ask me about installation, configuration, usage, or troubleshooting.

Security checks

  • ·README presentNot checked yet.
  • ·License declaredNot checked yet.
  • ·Tests presentNot checked yet.
  • ·Dependencies pinnedNot checked yet.
  • ·No dynamic code executionNot checked yet.
  • !Scoped permissionsDoesn't declare a permission scope. Assume it can do anything its process can.

Releases

0.7.10.7.1 · 3 Dec 2025`pip install durable-mcp>=0.7.1` Full Changelog**: https://github.com/reboot-dev/durable-mcp-python/compare/0.7.0...0.7.1
0.7.00.7.0 · 3 Dec 2025`pip install durable-mcp>=0.7.0` What's Changed Update to (python-sdk) mcp v1.22.0 by @skaar in https://github.com/reboot-dev/durable-mcp-python/pull/17 Add meta=tool.meta passthrough to FastMCP for ext-apps by @skaar in…
0.6.00.6.0 · 14 Nov 2025Upgrade reboot to 0.40.1 Add Auth
0.5.30.5.3 · 10 Nov 2025Upgrade reboot version to 0.39.3
0.5.20.5.2 · 4 Nov 2025Upgrade reboot version from `0.38.4` to `0.39.2`, which fixes bug in `at_most_once`. `mcp.application` can now optionally take `servicers` to serve custom Reboot durable data types and `initialize`, to be called after the application's…

README

Reboot Durable MCP

A framework for building durable MCP servers.

  • Takes advantage of the protocols ability to resume after disconnection, e.g., due to the server getting rebooted.

  • Any existing requests will be retried safely using Reboot workflows.

  • Using Reboot you can run multiple replicas of your server, and session messages will always be routed to the same replica.

Requirements

  • macOS or Linux
  • Python >= 3.12.11
  • Docker

Install

We recommend using uv, as it will manage the version of Python for you. For example, to start a new project in the directory foo:

uv init --python 3.12.11 .
uv add durable-mcp

Activate the venv:

source .venv/bin/activate

Make sure you have Docker running:

docker ps

Building an MCP server

Instead of using FastMCP from the MCP SDK, you use DurableMCP. Here is a simple server to get you started:

import asyncio
from reboot.aio.applications import Application
from reboot.mcp.server import DurableContext, DurableMCP
from reboot.std.collections.v1.sorted_map import SortedMap

# `DurableMCP` server which will handle HTTP requests at path "/mcp".
mcp = DurableMCP(path="/mcp")


@mcp.tool()
async def add(a: int, b: int, context: DurableContext) -> int:
    """Add two numbers and also store result in `SortedMap`."""
    result = a + b
    await SortedMap.ref("adds").insert(
        context,
        entries={f"{a} + {b}": f"{result}".encode()},
    )
    return result


async def main():
    # Reboot application that runs everything necessary for `DurableMCP`.
    await mcp.application().run()


if __name__ == '__main__':
    asyncio.run(main())

You can run the server via:

rbt dev run --python --application=path/to/main.py --working-directory=. --no-generate-watch

While developing you can tell rbt to restart your server when you modify files by adding one or more --watch=path/to/**/*.py to the above command line.

We recommend you move all of your command line args to a .rbtrc:

# This file will aggregate all of the command line args
# into a single command line that will be run when you
# use `rbt`.
#
# For example, to add args for running `rbt dev run`
# you can add lines that start with `dev run`. You can add
# one or more args to each line.
dev run --no-generate-watch
dev run --python --application=path/to/your/main.py
dev run --watch=path/to/**/*.py --watch=different/path/to/**/*.py

Then you can just run:

rbt dev run

Testing your MCP server

You can use the MCP Inspector to test out the server, or create a simple client.

import asyncio
from reboot.mcp.client import connect, reconnect

URL = "http://localhost:9991"


async def main():
    # `connect()` is a helper that creates a streamable HTTP client
    # and session using the MCP SDK. You can also write a client that
    # directly uses the MCP SDK, or use any other MCP client library!
    async with connect(URL + "/mcp") as (
        session, session_id, protocol_version
    ):
        print(await session.list_tools())
        print(await session.call_tool("add", arguments={"a": 5, "b": 3}))


if __name__ == '__main__':
    asyncio.run(main())

Performing a side-effect "at least once"

Within your tools (and soon within your prompts and resources too), you can perform a side-effect that is safe to try one or more times until success using at_least_once. Usually what makes it safe to perform one or more times is that you can somehow do it idempotently, e.g., passing an idempotency key as part of an API call. Use at_least_once for this, for example:

from reboot.aio.workflows import at_least_once
from reboot.mcp.server import DurableContext, DurableMCP


# `DurableMCP` server which will handle HTTP requests at path "/mcp".
mcp = DurableMCP(path="/mcp")


@mcp.tool()
async def add(a: int, b: int, context: DurableContext) -> int:

    async def do_side_effect_idempotently() -> int:
        """
        Pretend that we are doing a side-effect that we can try
        more than once because we can do it idempotently, hence using
        `at_least_once`.
        """
        return a + b

    result = await at_least_once(
        "Do side-effect _idempotently_",
        context,
        do_side_effect_idempotently,
        type=int,
    )

    # ...

Performing a side-effect "at most once"

Within your tools (and soon within your prompts and resources too), you can perform a side-effect that can only be tried once using at_most_once (if you can safely use at_least_once always prefer it). Here's an example of at_most_once:

from reboot.aio.workflows import at_least_once
from reboot.mcp.server import DurableContext, DurableMCP


# `DurableMCP` server which will handle HTTP requests at path "/mcp".
mcp = DurableMCP(path="/mcp")


@mcp.tool()
async def add(a: int, b: int, context: DurableContext) -> int:

    async def do_side_effect() -> int:
        """
        Pretend that we are doing a side-effect that we can only
        try to do once because it is not able to be performed
        idempotently, hence using `at_most_once`.
        """
        return a + b

    # NOTE: if we reboot, e.g., due to a hardware failure, within
    # `do_side_effect()` then `at_most_once` will forever raise with
    # `AtMostOnceFailedBeforeCompleting` and you will need to handle
    # appropriately.
    result = await at_most_once(
        "Do side-effect",
        context,
        do_side_effect,
        type=int,
    )

    # ...

Debugging

Start by enabling debug logging:

mcp = DurableMCP(path="/mcp", log_level="DEBUG")

The MCP SDK is aggressive about "swallowing" errors on the server side and just returning "request failed" so we do our best to log stack traces on the server. If you find a place where you've needed to add your own try/catch please let us know we'd love to log that for you automatically.

Supported client --> server requests:

  • initialize
  • ping
  • tools/call
  • tools/list
  • prompts/get
  • prompts/list
  • resources/list
  • resources/read
  • resources/templates/list
  • resources/subscribe
  • resources/unsubscribe
  • completion/complete
  • logging/setLevel

Supported client --> server notifications:

  • notifications/initialized
  • notifications/progress (for server initiated requests, e.g., elicitation)
  • notifications/roots/list_changed

Supported client <-- server requests:

  • elicitation/create
  • roots/list
  • sampling/createMessage

Supported client <-- server notifications:

  • notifications/progress
  • notifications/message
  • notifications/prompts/list_changed
  • notifications/resources/list_changed
  • notifications/tools/list_changed
  • notifications/resources/updated

Supported client <--> server notifications:

  • notifications/cancelled

TODO:

  • Auth pass through to MCP SDK
  • Add Auth examples
  • Adding tools, resources, and prompts dynamically
  • Add examples of how to test via Reboot().start/up/down/stop()
  • Add example of rebooting server using MCP Inspector version 0.16.7 which includes modelcontextprotocol/inspector#787
  • yapf
  • Pydantic state for each session

Contributing

First grab all dependencies:

uv sync --extra dev

Activate the venv:

source .venv/bin/activate

Generate code:

rbt generate

Make sure you have Docker running:

docker ps

Make your changes and run the tests:

pytest tests

README mirrored from the source repository 2 days ago. The original is authoritative.

Questions

About Durable MCP Python

How do I install Durable MCP Python?

Run npx durable-mcp-python, then add the server to your MCP client's configuration. Conduid has recorded 0 installs, so the command is known to work with current clients.

Is Durable MCP Python safe to use with an AI agent?

Its trust score is 58 out of 100 (fair). It passes 0 of 1 static security checks; the failures are listed above. It has no ConduID identity yet, so agent calls to it are not receipted.

Is Durable MCP Python still maintained?

The last commit was 8 months ago, with 0 open issues. That's long enough that you should check whether the maintainer is responding to issues before depending on it.