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, oroffline.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"],
identity_token=identity_token,
)
Omit apps to expose every App installed on this device:
tools = await marona.hub.connect(
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 An OpenAI Agent
connect() returns executable OpenAI Agents SDK FunctionTool objects already
bound to Marona. Pass them directly to Agent(tools=tools):
from agents import Agent
tools = await marona.hub.connect(
apps=["sda-books", "zimsec"],
skills=["create-group-fund"],
identity_token=identity_token,
conversation_id="conversation-1",
model="openai/gpt-4o",
)
agent = Agent(
name="Marona OpenAI Tool Agent",
model="gpt-5-mini",
tools=tools,
)
When a Skill returns status="planned", call it again only after the user
approves, passing {"request": "Yes", "approved": True}.
Calling a returned tool automatically executes its original descriptor through Marona. 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 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file marona-0.5.5.tar.gz.
File metadata
- Download URL: marona-0.5.5.tar.gz
- Upload date:
- Size: 61.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3cbd5a6928d41d8e9be825f1ba0e6a4d3ec11de38de61d04065102062a3f4144
|
|
| MD5 |
56c98ff73b4da1e8e27fc7d3e07dc889
|
|
| BLAKE2b-256 |
cb38fbd9b869fb4d4d2df8dddcf4f88b0c73a6809d52d43db95297bef12d8f2e
|
File details
Details for the file marona-0.5.5-py3-none-any.whl.
File metadata
- Download URL: marona-0.5.5-py3-none-any.whl
- Upload date:
- Size: 51.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2932c200d737b8eac918e1494cd2652875638347bad673f4dde8e650b8ae4953
|
|
| MD5 |
5cab441ca0ef1dad259ff36b40261221
|
|
| BLAKE2b-256 |
71f8d0e807e96a19dbd8da000bc223def5610994dcb3b7ec4618d790cff31e33
|