Skip to main content

Unified AI runtime and MCP/Skill gateway for online, hybrid, and offline applications.

Project description

marona

Unified AI runtime and MCP/Skill gateway for Python.

pip install marona

Marona has three request concepts:

  • mode: where execution is allowed: online, hybrid, or offline.
  • model: the model handling this request.
  • input: the user's text, image, document, or mixed request.

Apps, Skills, discovery, identity, permissions, approvals, service connections, and MCP execution stay inside the same Marona runtime.

1. Configure Access

MARONA_API_KEY authenticates your application with Marona.

export MARONA_API_KEY="mrn_live_..."
export OPENAI_API_KEY="..."          # Only for openai/...
export ANTHROPIC_API_KEY="..."       # Only for anthropic/...
export GEMINI_API_KEY="..."          # Only for gemini/...
export LITELLM_API_BASE="https://llm.example.com"
export LITELLM_API_KEY="..."
export OLLAMA_API_BASE="http://127.0.0.1:11434"

2. Send A Request

import asyncio
import os
from marona import Marona


async def main():
    identity_token = os.environ["MARONA_IDENTITY_TOKEN"]

    async with Marona(
        api_key=os.environ["MARONA_API_KEY"],
        mode="online",
    ) as marona:
        await marona.sync(interface="api", identity_token=identity_token)

        response = await marona.client(
            model="marona/default",
            input=[{"role": "user", "content": "What is my group fund?"}],
            interface="api",
            identity_token=identity_token,
        )
        print(response.text)


asyncio.run(main())

3. Images And Documents

response = await marona.client(
    model="openai/gpt-4o",
    input=[
        {
            "role": "developer",
            "content": "Keep answers clear and concise.",
        },
        {
            "role": "user",
            "content": [
                {"type": "input_text", "text": "Compare the image and report."},
                {"type": "input_image", "image_url": "https://example.com/match.jpg"},
                {
                    "type": "input_file",
                    "filename": "report.pdf",
                    "file_data": "data:application/pdf;base64,...",
                    "detail": "high",
                },
            ],
        },
    ],
    interface="api",
    identity_token=identity_token,
)

4. Change Models

The request shape does not change:

model="marona/default"
model="openai/gpt-4o"
model="anthropic/claude-sonnet-4"
model="gemini/gemini-2.5-pro"
model="ollama/llama3.2"
model="litellm/local-llama"
model="office/company-assistant"
model="local/gemma-4-e2b"

Known providers need no registration. Register only custom access or an in-process model:

async def office_native_adapter(request):
    result = await office_sdk.generate(request)
    return {"choices": [{"message": {"role": "assistant", "content": result.text}}]}


marona.models.register(
    name="office/company-assistant",
    provider="custom",
    endpoint="https://models.office.example/v1",
    model="company-assistant-v2",
    api_key=os.environ["OFFICE_MODEL_API_KEY"],
    adapter=office_native_adapter,
)

marona.models.register(
    name="local/gemma-4-e2b",
    executor=gemma_executor,
    context_window=2048,
    max_output_tokens=128,
)

Registration configures access. Select the model on each client(...) or message(...) request. models.use(...) is deprecated.

5. Install Apps For Offline Use

Install only while network access is available. Marona downloads and verifies the selected offline packages, imports their synchronized state, and indexes their capabilities locally.

await marona.hub.install(
    apps=["sda-books", "zimsec"],
    identity_token=identity_token,
)

Marona imposes no fixed installed-App limit. The practical limit is available device storage divided by each package and its data size.

6. Connect Installed Apps

In offline mode, connect() performs no network request. It exposes one lazy discovery gateway backed by the local SQLite capability index.

tools = await marona.hub.connect(
    apps=["sda-books", "zimsec"],
    adapter="tools",
    identity_token=identity_token,
)

Omit apps to expose every App installed on this device:

tools = await marona.hub.connect(
    adapter="tools",
    identity_token=identity_token,
)

The model initially receives only marona__discover_capabilities. Marona searches capabilities.sqlite after the model requests discovery, supplies only the matching exact schemas, and executes the selected package locally.

7. Use Marona Tools In Any Agent

Connect Apps, governed Skills, or both after creating Marona with an API key:

tools = await marona.hub.connect(
    apps=["sda-books", "zimsec"],
    skills=["create-group-fund"],
    adapter="tools",
    identity_token=identity_token,
)

Give tools to your agent framework. When it returns a tool call, execute the matching descriptor through Marona:

result = await marona.tools.execute(
    selected_tool,
    tool_arguments,
    identity_token=identity_token,
    conversation_id="conversation-1",
    model="openai/gpt-4o",
)

When a Skill returns status="planned", call it again only after the user approves, passing {"request": "Yes", "approved": True}.

App tools execute their MCP target. Skill tools execute through Marona's governed runtime. Internal Skill steps and Skill-managed raw capabilities are not exported to the external agent.

8. Publish A Skill

Every workflow entry uses step(); type selects reasoning, approval, or App execution.

from marona.skills import skill, step


@skill(
    name="create-group-fund",
    description="Create a group fund after explicit user approval.",
    governs=["group-fund.create_group"],
)
def create_group_fund():
    request = step(
        id="understand-request",
        type="reasoning",
        instruction="Extract the group name and currency.",
        inputs={"message": "{{ context.user_message }}"},
        outputs={"name": "string", "currency": "string"},
    )
    permission = step(
        id="confirm-create",
        type="approval",
        message=f"Create '{request.name}' in {request.currency}?",
        outputs={"approved": "boolean"},
    )
    return step(
        id="create-group",
        type="app",
        app="group-fund",
        capability="group-fund.create_group",
        instruction="Create the approved group.",
        condition=permission.approved,
        inputs={"name": request.name, "currency": request.currency},
        outputs={"group_id": "string", "name": "string"},
    )


marona.skills.publish(create_group_fund, version="1.0.0")

Mode Rules

  • online: network models and online MCP targets are allowed.
  • hybrid: try the selected local/private model, then use Edge fallback.
  • offline: use cached data, a local model, and installed offline-capable targets only.

Changing model never changes App, Skill, permission, approval, or MCP rules.

Project details


Release history Release notifications | RSS feed

Download files

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

Source Distribution

marona-0.5.4.tar.gz (59.6 kB view details)

Uploaded Source

Built Distribution

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

marona-0.5.4-py3-none-any.whl (50.5 kB view details)

Uploaded Python 3

File details

Details for the file marona-0.5.4.tar.gz.

File metadata

  • Download URL: marona-0.5.4.tar.gz
  • Upload date:
  • Size: 59.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for marona-0.5.4.tar.gz
Algorithm Hash digest
SHA256 684fe089287e74b9dda0a64d67e07394f76f81904ead553df41ff8695c8e3116
MD5 b7d6db303ae34f4c8fb09a891ffebff1
BLAKE2b-256 0b312f48e136e17896b0ae992a3c61092a428e067e58bc5cd165e8d3ab9296cf

See more details on using hashes here.

File details

Details for the file marona-0.5.4-py3-none-any.whl.

File metadata

  • Download URL: marona-0.5.4-py3-none-any.whl
  • Upload date:
  • Size: 50.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for marona-0.5.4-py3-none-any.whl
Algorithm Hash digest
SHA256 7399ca7dc0c7d4a52b90ac7e82a39bf95286824f60974cd59d580cd955be124d
MD5 ccb56f14a58448d07d2f5aa563d9cf5b
BLAKE2b-256 ebd81ebc0d7fafa04b57399eb58871bcc97fd1e7e57f08705f7d0b4487c58179

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