> ## Documentation Index
> Fetch the complete documentation index at: https://tbd-6fc993ce-hypeship-docker-sandboxes-integration.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Developing

In addition to our browser API, Kernel provides a code execution platform for deploying and invoking code. Typically, Kernel's code execution platform is used for deploying and invoking browser automations or web agents.

When using Kernel's code execution platform, we co-locate your code with any Kernel browser environments you instantiate in your app. This solves common issues with browser connections over CDP:

* **Reduced latency:** Your code runs directly alongside the browser, reducing round-trip latency
* **Improved reliability:** Fewer unexpected disconnects between your code and browser
* **Higher throughput:** Eliminates bandwidth bottlenecks during data-intensive operations like screenshots

<Tip>
  Install our [MCP server](/reference/mcp-server) to give your coding agent our `search_docs` tool.
</Tip>

## Apps, Actions, and Invocations

An `App` is a codebase deployed on Kernel. You can deploy any codebase in Typescript or Python on Kernel.

An `Action` is an invokable method within an app. Actions allow your to register entry points or functions that can be triggered on-demand. Actions can call non-action methods. Apps can have multiple actions.

An `Invocation` is a single execution of an action. Invocations can be triggered via API, scheduled as a job, or run on-demand.

## Getting started: create an app

First, install the Kernel SDK for your language:

<CodeGroup>
  ```bash Typescript/Javascript theme={null}
  npm install @onkernel/sdk
  ```

  ```bash Python theme={null}
  uv pip install kernel
  ```
</CodeGroup>

Then create an app:

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  import Kernel, { type KernelContext } from '@onkernel/sdk';

  const kernel = new Kernel();
  const app = kernel.app('my-app-name');
  ```

  ```python Python theme={null}
  from kernel import Kernel, App, KernelContext

  kernel = Kernel()
  app = App("my-app-name")
  ```
</CodeGroup>

Then, define and register an action that you want to invoke.

## Registering actions

Action methods receive two parameters:

* `runtimeContext`: Contextual information provided by Kernel during execution
* `payload`: Optional runtime data that you provide when invoking the action (max 64 KB). [Read more](/apps/invoke#payload-parameter)

You can register actions using either approach:

### Inline definition (recommended)

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  app.action('my-action-name', async (ctx: KernelContext, payload) => {
    const { tshirt_size, color, shipping_address } = payload;
    // Your action logic here
    return { order_id: 'example-order-id' };
  });
  ```

  ```python Python theme={null}
  @app.action("my-action-name")
  async def my_action_method(ctx: KernelContext, payload):
      tshirt_size = payload["tshirt_size"]
      color = payload["color"]
      shipping_address = payload["shipping_address"]
      # Your action logic here
      return {"order_id": "example-order-id"}
  ```
</CodeGroup>

### Define then register

This approach is better for larger apps, unit testing, and team collaboration since functions can be tested independently and reused across multiple actions.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const myActionMethod = async (ctx: KernelContext, payload) => {
    const { tshirt_size, color, shipping_address } = payload;
    // Your action logic here
    return { order_id: 'example-order-id' };
  };

  app.action('my-action-name', myActionMethod);
  ```

  ```python Python theme={null}
  async def my_action_method(ctx: KernelContext, payload):
      tshirt_size = payload["tshirt_size"]
      color = payload["color"]
      shipping_address = payload["shipping_address"]
      # Your action logic here
      return {"order_id": "example-order-id"}

  app.action("my-action-name")(my_action_method)
  ```
</CodeGroup>

## Environment variables

You can set environment variables when [deploying](/apps/deploy#environment-variables) your app. They then can be accessed in the usual way:

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const ENV_VAR = process.env.ENV_VAR;
  const myActionMethod = async (runtimeContext, payload) => {
    // ...
  };
  ```

  ```python Python theme={null}
  import os

  ENV_VAR = os.getenv("ENV_VAR")
  def my_action_method(runtime_context, payload):
      # ...
  ```
</CodeGroup>

## Return values

Action methods can return values, which will be included in the invocation's final response.

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  const myActionMethod = async (runtimeContext, payload) => {
    const { tshirt_size, color, shipping_address } = payload;
    // ...
    return {
      order_id: "example-order-id",
    }
  };
  ```

  ```python Python theme={null}
  def my_action_method(runtime_context, payload):
      tshirt_size, color, shipping_address = (
          payload["tshirt_size"],
          payload["color"],
          payload["shipping_address"]
      )
      # ...
      return {"order_id": "example-order-id"}
  ```
</CodeGroup>

The examples above show actions returning data.

## Building browser automations with Kernel apps

To implement a browser automation or web agent, instantiate an app and define an action that creates a Kernel browser.

<Info>
  Kernel browsers launch with a default context and page. Make sure to access
  the [existing context and
  page](https://playwright.dev/docs/api/class-browsertype#browser-type-connect-over-cdp)
  (`contexts()[0]` and `pages()[0]`), rather than trying to create a new one.
</Info>

<CodeGroup>
  ```typescript Typescript/Javascript theme={null}
  import Kernel, { type KernelContext } from '@onkernel/sdk';
  import { chromium } from 'playwright';

  const kernel = new Kernel();
  const app = kernel.app('browser-automation');

  app.action('get-page-title', async (ctx: KernelContext, payload) => {
    const kernelBrowser = await kernel.browsers.create({
      invocation_id: ctx.invocation_id,
    });

    const browser = await chromium.connectOverCDP(kernelBrowser.cdp_ws_url);
    const context = browser.contexts()[0] || (await browser.newContext());
    const page = context.pages()[0] || (await context.newPage());

    try {
      await page.goto('https://www.google.com');
      const title = await page.title();
      return { title };
    } finally {
      await browser.close();
    }
  });
  ```

  ```python Python theme={null}
  from kernel import Kernel, App, KernelContext
  from playwright.async_api import async_playwright

  kernel = Kernel()
  app = App("browser-automation")

  @app.action("get-page-title")
  async def get_page_title(ctx: KernelContext, payload):
      kernel_browser = kernel.browsers.create(invocation_id=ctx.invocation_id)

      async with async_playwright() as playwright:
          browser = await playwright.chromium.connect_over_cdp(kernel_browser.cdp_ws_url)
          context = browser.contexts[0] if browser.contexts else await browser.new_context()
          page = context.pages[0] if context.pages else await context.new_page()

          try:
              await page.goto("https://www.google.com")
              title = await page.title()
              return {"title": title}
          finally:
              await browser.close()
  ```
</CodeGroup>

<Info>
  Web agent frameworks sometimes require environment variables (e.g. LLM API keys). Set them when [deploying](/apps/deploy#environment-variables) your app.
</Info>

## Next steps

Once you're happy with your app, follow [these steps](/apps/deploy) to deploy and invoke it on the Kernel platform.
