Add Tenchi to an existing project
Use this path when you already have a Python project and want to add a Tenchi API without starting from the generated application. The result is one working JSON operation, a direct use-case test, OpenAPI and health routes, and the same complete validation command used by generated projects.
Install Tenchi and development tools
Tenchi requires Python 3.12 or newer. From the project root, add the runtime and local development dependencies:
uv add tenchi
uv add --dev uvicorn ruff pyright pytest pytest-asyncioCreate this initial structure. Empty __init__.py files make each package
explicit:
app/
__init__.py
features/
__init__.py
greetings/
__init__.py
contracts.py
routes.py
schemas.py
use_cases/
__init__.py
greet.py
tests/
__init__.py
test_greet.py
server/
__init__.py
asgi.py
context.py
jobs.py
preflight.py
routes.py
runtime.py
tasks.py
tools.pyDeclare the boundary
Define the validated query and response in
app/features/greetings/schemas.py:
from pydantic import BaseModel, Field
class GreetQuery(BaseModel):
name: str = Field(min_length=1)
class Greeting(BaseModel):
message: strDescribe the HTTP operation in app/features/greetings/contracts.py:
from tenchi.contracts import contract
from .schemas import Greeting, GreetQuery
greet_contract = contract(
method="GET",
path="/greet",
query=GreetQuery,
response=Greeting,
name="greet",
summary="Greet someone",
public=True,
)public=True gives a future authentication hook an explicit exemption signal.
It does not change access by itself.
Implement and bind the use case
Create the application context in app/server/context.py. This first operation
has no external dependencies, so the context is empty:
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class AppContext:
passImplement the behavior in app/features/greetings/use_cases/greet.py:
from app.server.context import AppContext
from ..schemas import Greeting, GreetQuery
async def greet(query: GreetQuery, context: AppContext) -> Greeting:
return Greeting(message=f"Hello, {query.name}!")Bind the contract to the use case in app/features/greetings/routes.py:
from tenchi.routes import route, route_group
from .contracts import greet_contract
from .use_cases.greet import greet
routes = route_group(route(greet_contract, greet))route() checks the query and return annotations immediately. Importing this
module fails if the use-case signature no longer matches the contract.
Compose the ASGI application
Compose the application and documentation routes in app/server/routes.py:
from tenchi.health import health_route
from tenchi.openapi import openapi_route, swagger_ui_route
from tenchi.routes import route_group
from app.features.greetings.routes import routes as greeting_routes
OPENAPI_TITLE = "Existing app"
OPENAPI_VERSION = "0.1.0"
OPENAPI_DESCRIPTION = "Greeting API"
api_routes = route_group(greeting_routes)
routes = route_group(
api_routes,
openapi_route(
api_routes,
title=OPENAPI_TITLE,
version=OPENAPI_VERSION,
description=OPENAPI_DESCRIPTION,
),
swagger_ui_route(title=f"{OPENAPI_TITLE} documentation"),
health_route(),
)Put entrypoint-neutral context wiring in app/server/runtime.py:
from app.server.context import AppContext
def create_context() -> AppContext:
return AppContext()Expose the Starlette application from app/server/asgi.py:
from tenchi.server import create_app
from app.server.routes import routes
from app.server.runtime import create_context
app = create_app(routes=routes, context_factory=create_context)When the application needs a database or SDK client, replace the direct context factory with the lifespan and request-scope pattern from routes and server.
Create an empty operational runner in app/server/tasks.py:
from tenchi.tasks import create_task_runner, task_group
from app.server.runtime import create_context
tasks = task_group()
runner = create_task_runner(tasks=tasks, context_factory=create_context)This keeps tenchi map and tenchi task list useful before the application
has a maintenance command. Add tasks later by following Operational
tasks.
Create an empty background-job composition in app/server/jobs.py:
from tenchi.jobs import job_group
jobs = job_group()This gives tenchi map its conventional job target before the application has
durable work. Add producer and consumer bindings later by following
Background jobs.
Create an empty application-tool composition in app/server/tools.py:
from tenchi.tools import tool_group
tools = tool_group()This gives tenchi map, tenchi tools, and tenchi check their conventional
target before the application exposes a use case to machine callers. Add
declarations and authenticated runner wiring later by following Application
tools.
Create an empty deployment gate in app/server/preflight.py:
from tenchi.preflight import preflight_group
checks = preflight_group()The empty group lets tenchi preflight pass before the application has an
external dependency. Add read-only database, secret-manager, service, or
worker observations by following deployment preflight.
Add a direct behavior test
Create app/features/greetings/tests/test_greet.py:
from app.features.greetings.schemas import GreetQuery
from app.features.greetings.use_cases.greet import greet
from app.server.context import AppContext
async def test_greet() -> None:
result = await greet(GreetQuery(name="Tenchi"), AppContext())
assert result.message == "Hello, Tenchi!"Merge these settings into pyproject.toml so the aggregate check knows where
to find the application and tests:
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["app"]
pythonpath = ["."]
[tool.ruff]
line-length = 88
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
[tool.pyright]
include = ["app"]
typeCheckingMode = "strict"
pythonVersion = "3.12"Keep any existing test and source paths when merging these tables into the project's configuration.
Create the baseline and verify the app
Write the first canonical OpenAPI and application-tool snapshots, then run the complete project gate:
uv run tenchi openapi \
--routes app.server.routes:api_routes \
--title "Existing app" \
--version 0.1.0 \
--description "Greeting API" \
--write openapi.json
uv run tenchi tools --write tools.json
uv run tenchi check
uv run tenchi preflight
uv run tenchi task listStart the development server after the checks pass:
uv run tenchi devOpen http://127.0.0.1:8000/docs, or call the operation directly:
curl "http://127.0.0.1:8000/greet?name=Tenchi"The response is {"message":"Hello, Tenchi!"}. Continue with app
architecture before adding ports, adapters, policies, and
authenticated operations.