Skip to main content

Service layer for Mindtrace

Project description

PyPI version License Downloads

Mindtrace Services

The Services module provides Mindtrace’s typed microservice framework. It enables you to define a Service once with TaskSchema endpoint contracts, launch it as a process, connect to it through an auto-generated client, and optionally expose those endpoints as MCP tools.

Features

  • Typed service definition with Service + TaskSchema
  • FastAPI-backed HTTP services with standard lifecycle endpoints
  • Auto-generated clients via ConnectionManager generation
  • Built-in launch/connect workflow for local service processes
  • First-class MCP support through FastMCP
  • Service composition utilities such as Gateway and proxy connection managers
  • Concrete integrations such as Discord service wrappers and sample services

Quick Start

import time

from pydantic import BaseModel

from mindtrace.core import TaskSchema
from mindtrace.services import Service


class EchoInput(BaseModel):
    message: str
    delay: float = 0.0


class EchoOutput(BaseModel):
    echoed: str


echo_task = TaskSchema(
    name="echo",
    input_schema=EchoInput,
    output_schema=EchoOutput,
)


class EchoService(Service):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.add_endpoint("echo", self.echo, schema=echo_task)

    def echo(self, payload: EchoInput) -> EchoOutput:
        if payload.delay > 0:
            time.sleep(payload.delay)
        return EchoOutput(echoed=payload.message)


cm = EchoService.launch(host="localhost", port=8080, wait_for_launch=True)
print(cm.status())
print(cm.echo(message="Hello"))
cm.shutdown()

You can inspect the generated FastAPI docs at http://localhost:8080/docs while the service is running.

EchoService FastAPI docs

You can also call the service directly over HTTP:

curl -X POST http://localhost:8080/echo \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello from curl", "delay": 0.0}'

If you are working in Python, you usually do not need to make raw curl requests at all. You can connect to the same running service and get a convenient cm instead:

cm = EchoService.connect("http://localhost:8080")
print(cm.echo(message="Hello again"))

Service

Service is the server-side abstraction. A service instance:

  • builds a FastAPI app
  • tracks registered endpoints and their schemas
  • mounts an MCP server
  • provides standard lifecycle endpoints
  • can be launched in a separate process with Service.launch()

A minimal service subclass looks like this:

from mindtrace.services import Service


class EchoService(Service):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.add_endpoint("echo", self.echo, schema=echo_task)

TaskSchema

TaskSchema is the typed contract for an endpoint. It defines:

  • the endpoint name
  • the input schema
  • the output schema

That same schema is reused across the package for:

  • FastAPI request validation
  • generated connection manager methods
  • output parsing on the client side
  • MCP tool exposure

Example:

from pydantic import BaseModel

from mindtrace.core import TaskSchema


class EchoInput(BaseModel):
    message: str


class EchoOutput(BaseModel):
    echoed: str


echo_task = TaskSchema(
    name="echo",
    input_schema=EchoInput,
    output_schema=EchoOutput,
)

Defining Endpoints

Register endpoints with add_endpoint():

self.add_endpoint(
    path="echo",
    func=self.echo,
    schema=echo_task,
    as_tool=True,
)

Important behavior:

  • routes are registered as POST endpoints by default
  • the endpoint schema is stored in the service for later client generation
  • the function is wrapped with service logging/instrumentation
  • setting as_tool=True exposes the same function as an MCP tool

Built-in Endpoints

Every Service automatically registers a standard set of lifecycle and introspection endpoints:

  • endpoints — list registered endpoint names
  • status — current service status
  • heartbeat — structured health/liveness payload
  • server_id — unique server ID
  • pid_file — PID file path for the launched process
  • shutdown — stop the running service

These endpoints are available over HTTP, and some are also exposed as MCP tools.

ConnectionManager

ConnectionManager is the client-side abstraction for talking to a running service over HTTP. It provides common lifecycle methods such as:

  • status() / astatus()
  • shutdown() / ashutdown()
  • mcp_client for talking to the same service via MCP

Example:

from mindtrace.services import Service


cm = Service.connect("http://localhost:8080")
print(cm.status())
cm.shutdown(block=False)

Auto-Generated Connection Managers

If a service does not register a custom client class, Mindtrace generates one automatically from the service’s registered endpoint schemas. Each endpoint becomes:

  • a synchronous client method
  • an asynchronous client method prefixed with a

For example, an echo endpoint becomes:

  • cm.echo(...)
  • await cm.aecho(...)

For a generated client, this means you can write:

cm = EchoService.launch(wait_for_launch=True)
result = cm.echo(message="Hello")
print(result.echoed)

If no custom connection manager is registered, generate_connection_manager() creates one dynamically from the service definition.

For each registered endpoint:

  • a sync method is generated
  • an async method is generated
  • input kwargs are validated against the endpoint input schema
  • HTTP responses are parsed into the endpoint output schema

Method naming

  • endpoint echoecho() and aecho()
  • dotted endpoint names are converted to valid Python method names by replacing . with _

Validation controls

Generated methods support:

  • validate_input=True
  • validate_output=True

These can be disabled when raw payload handling is needed.

Examples:

# Default behavior: validate kwargs against the input schema
# and parse the response into the output schema
result = cm.echo(message="Hello")
print(result.echoed)
# Skip input validation and send the payload as-is
result = cm.echo(validate_input=False, message="Hello", delay=0.0)

# Skip output validation to receive the raw response dict
raw_result = cm.echo(message="Hello", validate_output=False)
print(raw_result)

When to write a custom connection manager

A custom connection manager is worth using when you want:

  • richer convenience methods than one-method-per-endpoint
  • custom retry or caching behavior
  • special authentication flows
  • a more domain-specific client surface

Otherwise, the generated client is usually enough.

Example:

import requests

from mindtrace.services import ConnectionManager, Service


class EchoConnectionManager(ConnectionManager):
    def echo(self, message: str, delay: float = 0.0):
        response = requests.post(
            f"{str(self.url).rstrip('/')}/echo",
            json={"message": message, "delay": delay},
            timeout=60,
        )
        response.raise_for_status()
        return response.json()

    def echo_twice(self, message: str):
        first = self.echo(message)
        second = self.echo(message)
        return [first["echoed"], second["echoed"]]


class EchoService(Service):
    pass


EchoService.register_connection_manager(EchoConnectionManager)
cm = EchoService.connect("http://localhost:8080")
print(cm.echo_twice("Hello"))

After registering the custom connection manager, EchoService.connect(...) or EchoService.launch(...) returns an EchoConnectionManager instead of an auto-generated one.

Launching and Connecting

launch()

Service.launch():

  • resolves the target URL
  • checks that no service is already running there
  • spawns a subprocess launcher
  • optionally waits for the service to become reachable
  • returns a connection manager when wait_for_launch=True

Common arguments:

  • url — explicit full service URL
  • host / port — host and port override
  • wait_for_launch — wait until the service is available
  • timeout — startup timeout in seconds
  • block — keep the calling process blocked while the service runs
  • num_workers — worker count for the launched service

Example:

cm = EchoService.launch(
    host="localhost",
    port=8080,
    wait_for_launch=True,
    timeout=30,
)
print(cm.status())

connect()

Service.connect() attaches to an already-running service and returns the appropriate connection manager.

Example:

cm = EchoService.connect("http://localhost:8080")
print(cm.status())
print(cm.echo(message="Connected"))

URL and Configuration Behavior

In most cases, you can either provide an explicit URL or let the service use its configured defaults.

Examples:

# Explicit full URL
cm = EchoService.connect("http://localhost:8080")
# Host/port convenience when launching
cm = EchoService.launch(host="localhost", port=8080, wait_for_launch=True)

If you do not pass either, the service falls back to its configured default URL.

MCP paths are also configuration-driven, so the mounted MCP endpoint is built from the service URL plus the configured MCP mount and app paths.

MCP Integration

MCP (Model Context Protocol) is a standard way to expose application functionality as structured tools for AI clients. If you are familiar with FastAPI, you can think of MCP as a tool-oriented interface sitting alongside your normal HTTP routes: instead of calling REST endpoints directly, an MCP client can discover available tools and invoke them with structured inputs.

In mindtrace-services, every service creates and mounts a FastMCP app alongside its normal FastAPI routes.

Expose an endpoint as a tool

self.add_endpoint("echo", self.echo, schema=echo_task, as_tool=True)

This makes the same function available:

  • as an HTTP endpoint
  • as an MCP tool

Register an MCP-only tool

def reverse_message(payload: EchoInput) -> EchoOutput:
    """Reverse the input message."""
    return EchoOutput(echoed=payload.message[::-1])


self.add_tool("reverse_message", reverse_message)

MCP client access

You can connect to a service over MCP in three common ways.

Class-level connect

client = EchoService.mcp.connect("http://localhost:8080")

Class-level launch

client = EchoService.mcp.launch(host="localhost", port=8080, wait_for_launch=True)

From an existing connection manager

cm = EchoService.launch(host="localhost", port=8080, wait_for_launch=True)
client = cm.mcp_client

Minimal MCP example

import asyncio

from mindtrace.services.samples.echo_mcp import EchoService


async def main():
    client = EchoService.mcp.launch(
        host="localhost",
        port=8080,
        wait_for_launch=True,
        timeout=30,
    )
    async with client:
        tools = await client.list_tools()
        print([tool.name for tool in tools])
        result = await client.call_tool("echo", {"payload": {"message": "Hello"}})
        print(result)


asyncio.run(main())

Remote MCP usage

Any Mindtrace service exposing MCP tools can also be used from MCP-capable clients such as Cursor by pointing the client at the service’s mounted MCP endpoint.

Example configuration:

{
  "mcpServers": {
    "mindtrace_echo": {
      "url": "http://localhost:8080/mcp-server/mcp/"
    }
  }
}

Once configured, the client can list and invoke tools exposed by the service.

Gateway and Proxy Routing

The package includes service-composition helpers for routing traffic through a central gateway.

Gateway

Gateway is a service that can register downstream FastAPI apps and forward requests to them.

It supports:

  • dynamic app registration
  • HTTP request forwarding
  • enhanced connection behavior for registered apps

ProxyConnectionManager

ProxyConnectionManager routes endpoint calls through a gateway instead of calling a service directly. It uses service endpoint metadata to create proxy methods matching the downstream service surface.

This is useful when a service needs to be accessed indirectly through a central gateway.

Example:

from mindtrace.services import EchoService, Gateway


# Launch a normal service
backend_cm = EchoService.launch(host="localhost", port=8081, wait_for_launch=True)

# Launch the gateway
gateway_cm = Gateway.launch(host="localhost", port=8080, wait_for_launch=True)

# Register the service with the gateway and attach a proxy client
gateway_cm.register_app(
    name="echo",
    url="http://localhost:8081",
    connection_manager=backend_cm,
)

# Calls are now forwarded through the gateway
print(gateway_cm.echo.echo(message="Hello through gateway"))

Examples in this package

See the sample implementations in this package for end-to-end reference:

Testing

If you are working in the full Mindtrace repo, run tests for this module specifically:

# Run the services test suite
ds test: services

# Run only unit tests for services
ds test: --unit services

If you need a fresh checkout first:

git clone https://github.com/Mindtrace/mindtrace.git && cd mindtrace
uv sync --dev --all-extras

Practical Notes and Caveats

  • Generated endpoint methods use POST requests.
  • Protected client methods such as status and shutdown are not overwritten by generated endpoint methods.
  • A lightweight service instance may be created during client generation in order to inspect registered endpoints.
  • Endpoint names should be chosen with both route readability and Python client naming in mind.
  • launch() manages subprocesses and PID files, so it should be treated as a service runtime tool, not just object instantiation.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mindtrace_services-0.13.0.tar.gz (44.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mindtrace_services-0.13.0-py3-none-any.whl (50.4 kB view details)

Uploaded Python 3

File details

Details for the file mindtrace_services-0.13.0.tar.gz.

File metadata

  • Download URL: mindtrace_services-0.13.0.tar.gz
  • Upload date:
  • Size: 44.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for mindtrace_services-0.13.0.tar.gz
Algorithm Hash digest
SHA256 ae521205fdc29c07620ef9d8373388b2f8081311b05bc942842ef1bf892a8873
MD5 97fa8ef79044339bd48ce009c7fc88e9
BLAKE2b-256 8bdf77d12fd23cf0bfaedfe2ff65e2e0694927a7a1bb6c172454fed7291dccf5

See more details on using hashes here.

File details

Details for the file mindtrace_services-0.13.0-py3-none-any.whl.

File metadata

  • Download URL: mindtrace_services-0.13.0-py3-none-any.whl
  • Upload date:
  • Size: 50.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.25 {"installer":{"name":"uv","version":"0.11.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for mindtrace_services-0.13.0-py3-none-any.whl
Algorithm Hash digest
SHA256 494b65caa8481f83709224dbebfb04e3e5babe6c2ae5cf985b9c540a41966261
MD5 3d3bf9ead5e4ad9ef98b73a35aeda74c
BLAKE2b-256 94547ec7d6e05d0b12654d124a92584a2b0edd93a3ba4939133aa902f03373a9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page