Metadata-Version: 2.4
Name: marona-sdk
Version: 0.1.9
Summary: Official developer SDK for building Marona-compatible apps, tool servers, skills, and workflows.
Author: Blessing Nyuwani
License-Expression: MIT
Project-URL: Homepage, https://www.marona.ai
Project-URL: Repository, https://github.com/BlessingNyuwani/agentnet-mcp-sdk
Project-URL: Documentation, https://hub.marona.ai
Keywords: marona,sdk,mcp,agents,ai,tools,workflows
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi<1.0,>=0.115
Requires-Dist: httpx<1.0,>=0.28
Requires-Dist: mcp<2.0,>=1.28
Requires-Dist: pydantic<3.0,>=2.10
Requires-Dist: uvicorn[standard]<1.0,>=0.34
Provides-Extra: dev
Requires-Dist: build<2.0,>=1.2; extra == "dev"
Requires-Dist: pytest<9.0,>=8.3; extra == "dev"
Requires-Dist: twine<7.0,>=6.0; extra == "dev"
Dynamic: license-file

# Marona SDK

Official developer SDK for building Marona-compatible apps, MCP servers, tool
servers, skills, and workflows.

Use `marona-sdk` when you are building an app that exposes tools to Marona Hub.
Use `marona` when you are building an application or interface that calls a
Marona-compatible runtime.

The SDK helps your app expose the Marona standard automatically:

- `GET /health`
- `GET /manifest`
- `GET /hub-registration`
- `POST /mcp/` using the official MCP Python SDK `FastMCP`
- tool outputs with `status`, `success`, `message`, `content`,
  `content_type`, `presentation_hint`, and `context`

## Install

Python:

```bash
pip install marona-sdk
```

In `requirements.txt`:

```text
marona-sdk
```

Import it as:

```python
from marona_sdk import AgentApp, HostingConfig, success
```

Dart:

```bash
dart pub add marona_sdk
dart pub global activate marona_sdk
```

TypeScript:

```bash
npm install marona-sdk
```

## 1. Create An App

Every app needs a unique app ID, also called a slug. Examples:

- `weather-mcp`
- `knowledge-mcp`
- `company-crm-mcp`

```python
from marona_sdk import AgentApp, success

agent_app = AgentApp(
    name="Weather Tools",
    slug="weather-mcp",
    description="Weather lookup tools for agents and AI interfaces.",
    category="Weather",
    visibility="public",
)
```

Use `visibility="public"` when the app should be discoverable in Hub.
Use `visibility="private"` when the app is only for the owner's workspace.

Apps also declare where they can run:

```python
agent_app = AgentApp(
    name="Knowledge Tools",
    slug="knowledge-mcp",
    description="Search company knowledge.",
    category="Knowledge",
    execution_modes=["online", "offline", "hybrid"],
    execution_targets=[
        {
            "mode": "offline",
            "type": "oci_image",
            "image": "registry.example.com/knowledge-mcp:1.0.0",
            "transport": "streamable_http",
            "endpoint": "/mcp",
            "port": 62750,
            "assets": ["knowledge-index", "document-cache"],
        }
    ],
)
```

`execution_modes` can be `online`, `offline`, or `hybrid`. `execution_targets`
must be portable descriptors such as `remote_mcp`, `marona_hosted`,
`oci_image`, `python_package`, `local_process`, `wasm`, `static_cache`, or
`builtin`. Do not submit device-local runtime fields; Marona clients resolve
those after an app is installed on a device or private network.

## 2. Add A Tool

```python
@agent_app.tool(
    title="Get forecast",
    description="Return the weather forecast for a city.",
    input_schema={
        "type": "object",
        "properties": {
            "city": {"type": "string"},
        },
        "required": ["city"],
        "additionalProperties": False,
    },
)
def get_forecast(city: str) -> dict:
    return success(
        f"The forecast for {city} is sunny.",
        content=f"The forecast for {city} is sunny.",
        content_type="text",
        presentation_hint="display_as_provided",
        context="Display the forecast directly unless the user asks for another format.",
        data={"city": city, "condition": "sunny"},
    )
```

## 3. Run The Server

```python
app = agent_app.create_fastapi_app()
```

Run locally:

```bash
uvicorn examples.hello_server:app --host 127.0.0.1 --port 62900
```

Inspect:

```text
http://127.0.0.1:62900/health
http://127.0.0.1:62900/manifest
http://127.0.0.1:62900/hub-registration
http://127.0.0.1:62900/mcp/
```

## 4. Choose Hosting

Marona supports two hosting models.

### Option A: self-hosted

You run the server yourself and register its public MCP URL in Marona Hub.

```python
agent_app = AgentApp(
    name="Weather Tools",
    slug="weather-mcp",
    description="Weather lookup tools.",
    category="Weather",
    hosting=HostingConfig(mode="self_hosted"),
)
```

Your deployment is responsible for uptime, logs, scaling, secrets, storage,
domains, and monitoring. Marona Hub can still list and review the app, and
runtime users can discover it after approval.

### Option B: Marona-hosted

Marona-hosted apps are packaged as OCI images. Marona deploys the image through
the Marona hosting control plane, then manages health, logs, metrics, secrets,
regions, scaling, storage, and public URLs.

```python
agent_app = AgentApp(
    name="Weather Tools",
    slug="weather-mcp",
    description="Weather lookup tools.",
    category="Weather",
    hosting=HostingConfig(
        mode="marona_hosted",
        source_type="oci_image",
        image_ref="registry.marona.ai/example/weather-mcp:1.0.0",
        version="1.0.0",
        runtime="container",
        region="africa-south-1",
        resource_class="shared-small",
        replicas=1,
        storage_gb=1,
        secrets={"OPENAI_API_KEY": "set-in-portal"},
        environment={"LOG_LEVEL": "info"},
        runtime_config={"port": 8000, "endpoint": "/mcp"},
    ),
)
```

Secret values are not exposed in `/hub-registration`; only configured secret
names are shown. Developers do not need to manage hosting infrastructure. Use
runtime config for MCP runtime settings such as port and endpoint.

## 5. Publish To Marona Hub

`DeveloperHubClient` is for developer workflow commands such as create, sync,
submit, and hosted deployment setup.

```python
from marona_sdk import DeveloperHubClient

client = DeveloperHubClient(
    hub_url="https://hub.marona.ai",
    token="DEVELOPER_PORTAL_TOKEN",
)
```

Apps, devices, bots, and user interfaces should use the separate `marona`
runtime client package.

Create, sync, submit, then publish and deploy:

```python
detail = await client.create_agent_app(agent_app.hub_registration(
    server_url="https://weather.example.com/mcp/",
    health_check_url="https://weather.example.com/health",
))
await client.sync_agent_app(detail["id"])
await client.submit_agent_app(detail["id"])

deployment = await client.create_hosted_deployment(
    detail["id"],
    source_type="oci_image",
    image_ref="ghcr.io/example/weather-mcp:latest",
    runtime="container",
    region="africa-south-1",
    runtime_config={"port": 8000, "endpoint": "/mcp"},
    secrets={"OPENAI_API_KEY": "set-in-portal"},
)
```

Or use the CLI. This is a Marona Hub core deliverable for developers who want
to build, publish, and deploy MCP apps without managing Hub API calls manually.

```bash
marona publish --image registry.marona.ai/example/weather-mcp:1.0.0
marona deploy \
  --app-id app_123 \
  --image registry.marona.ai/example/weather-mcp:1.0.0 \
  --version 1.0.0 \
  --region africa-south-1 \
  --runtime-config '{"port":8000,"endpoint":"/mcp"}'
```

Marona returns a deployment record with status, region, workload id, public URL,
MCP URL, health URL, logs URL, and metrics URL. Admin activation provisions the
workload and points the app’s live MCP endpoint at the hosted URL after health
checks pass.

## Standard Tool Results

All Marona-compatible tools should return the SDK result shape:

- `status`: machine-readable result status
- `success`: boolean result success flag
- `message`: short user-facing status line
- `content`: primary user-facing text or content
- `content_type`: provider-neutral content type, such as `text`, `document`,
  `message`, `list`, `media`, or `search_results`
- `presentation_hint`: provider-neutral usage hint, such as
  `display_as_provided`
- `context`: short guidance for interpreting the result

Provider-specific data should live under generic fields:

- `data`: one structured object
- `items`: a list of structured objects
- `count`: item count when `items` is used
- `artifacts`: generic artifact descriptors
- `job`: generic async job metadata

If a tool declares its own `output_schema`, the SDK merges these standard fields
into that schema before exposing `/manifest` and `/hub-registration`.

## Standard Tool Inputs For User Files

Tools that consume uploaded files should declare the SDK-standard
`attachments` input property. The runtime injects the current conversation
files into that array automatically.

```python
from marona_sdk import standard_attachments_property

input_schema = {
    "type": "object",
    "properties": {
        "question": {"type": "string"},
        "attachments": standard_attachments_property(),
    },
    "required": [],
    "additionalProperties": False,
}
```

Each attachment can include `artifact_id`, `filename`, `mime_type`, `kind`,
`url`, `file_url`, `content_base64`, `content`, and `metadata`.

## Package Names

- Python developer SDK: `marona-sdk`, import `marona_sdk`
- Dart developer SDK: `marona_sdk`, import `package:marona_sdk/marona_sdk.dart`
- TypeScript developer SDK: `marona-sdk`, import from `"marona-sdk"`
- Runtime client SDKs: `marona`
