This guide gets you from zero to a working cross-language call. You will write a math provider in TypeScript and call it from Python.

Prerequisites

Node.js 18+, Python 3.11+, and the Saikuro runtime running locally (or use in-memory transport for same-process demos). See Installation.

  1. Install adapters

    TypeScript
    Python
    npm install @nisoku/saikuro
    
    pip install saikuro
    
  2. Write a TypeScript provider

    // provider.ts
    import { SaikuroProvider } from "@nisoku/saikuro";
    
    const provider = new SaikuroProvider("math");
    
    provider.register("add", (a: number, b: number) => a + b);
    provider.register("multiply", (a: number, b: number) => a * b);
    
    await provider.serve("unix:///tmp/saikuro.sock");
    

    Run it:

    npx tsx provider.ts
    
  3. Call it from Python

    # caller.py
    import asyncio
    from saikuro import SaikuroClient
    
    async def main():
        async with SaikuroClient.connect("unix:///tmp/saikuro.sock") as client:
            result = await client.call("math.add", [10, 32])
            print(f"10 + 32 = {result}")  # 10 + 32 = 42
    
            result = await client.call("math.multiply", [6, 7])
            print(f"6 * 7 = {result}")  # 6 * 7 = 42
    
    asyncio.run(main())
    
    python caller.py
    

    You just called TypeScript from Python. No HTTP server, no serialization glue, no shared interface file.

  4. Go the other direction

    Same provider in Python:

    # provider.py
    import asyncio
    from saikuro import SaikuroProvider
    
    provider = SaikuroProvider("math")
    
    @provider.register("add")
    def add(a: int, b: int) -> int:
        return a + b
    
    @provider.register("multiply")
    def multiply(a: int, b: int) -> int:
        return a * b
    
    asyncio.run(provider.serve("unix:///tmp/saikuro.sock"))
    

    Call it from TypeScript:

    import { SaikuroClient } from "@nisoku/saikuro";
    
    const client = await SaikuroClient.connect("unix:///tmp/saikuro.sock");
    const result = await client.call("math.add", [10, 32]);
    console.log(`10 + 32 = ${result}`);
    

    The caller does not know or care which language is on the other end.

What just happened

When your provider called serve(), it:

  1. Connected to the Saikuro runtime over the transport
  2. Announced its namespace and function signatures
  3. Started handling incoming invocations

When your caller ran client.call("math.add", [10, 32]):

  1. The adapter serialized arguments to MessagePack
  2. Sent an envelope to the runtime
  3. The runtime validated and routed it to the math provider
  4. The provider deserialized, ran your function, and sent back the result
  5. The adapter returned the deserialized result to your code

All of that is invisible. From your code’s perspective you just called a function.

Next Steps