> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meshagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Service YAML

> Write, validate, deploy, update, and delete MeshAgent services from a service manifest.

Write a service manifest when you want to review, customize, validate, deploy, update, or delete the service configuration yourself.

Use [`meshagent service`](../../reference/meshagent_cli_help#meshagent-service) with service manifests. Use [Deploy a built in Agent](./deploy_builtin_agent) when you want to deploy a process-backed agent from CLI flags instead.

In MeshAgent, you can deploy fixed or templated services project-wide or to specific rooms, either as MeshAgent-managed `container` services or as `external` services that MeshAgent routes to; for more detail, see [Intro to Services](../intro).

The examples below show three common cases:

* a `Service` with a `container`
* a `ServiceTemplate` with a `container`
* a `Service` with an `external` runtime

### Example 1: `Service` with `container`

Use `kind: Service` when the configuration is fixed at deploy time.

<CodeGroup>
  ```yaml Yaml theme={null}
  kind: Service
  version: v1
  metadata:
    name: my-chatbot
    description: "A chatbot with web search and storage"
    annotations:
      meshagent.service.id: "my-chatbot"
  agents:
    - name: my-chatbot
      description: "A helpful chatbot"
      annotations:
        meshagent.agent.type: "ChatBot"
  container:
    image: "meshagent/cli:default"
    command: >-
      meshagent process join
      --agent-name my-chatbot
      --channel chat
      --web-search
      --storage
      --rule "You are a helpful assistant. Use web search to find current information and save important findings to storage."
      --room-rules "agents/my-chatbot/rules.md"
    environment:
      - name: MESHAGENT_TOKEN
        token:
          identity: my-chatbot
    storage:
      room:
        - path: /data
          read_only: false

  ```
</CodeGroup>

This example uses `kind: Service` with a `container`. MeshAgent runs the `meshagent/cli:default` image, and the container starts `meshagent process join ...` inside it.

Validate and deploy it:

```bash bash theme={null}
meshagent service validate --file meshagent.yaml
meshagent service create --file meshagent.yaml --room myroom
```

Use `--global` instead of `--room myroom` to deploy the same manifest as a project service.

### Example 2: `ServiceTemplate` with `container`

Use `kind: ServiceTemplate` when the manifest shape stays the same but some values should be supplied during deployment or install.

<CodeGroup>
  ```yaml Yaml theme={null}
  kind: ServiceTemplate
  version: v1
  metadata:
    name: my-language-chatbot
    description: "A chatbot with web search and storage that responds in your chosen language"
    annotations:
      meshagent.service.id: "my-language-chatbot"
  variables:
    - name: language
      description: "The language the chatbot should respond in (e.g. English, Spanish, French)"
  agents:
    - name: my-language-chatbot
      description: "A helpful chatbot"
      annotations:
        meshagent.agent.type: "ChatBot"
  container:
    image: "meshagent/cli:default"
    command: >-
      meshagent process join
      --agent-name my-language-chatbot
      --channel chat
      --web-search
      --storage
      --rule "You are a helpful assistant. Always respond in {{ language }}. Use web search to find current information and save important findings to storage."
      --room-rules "agents/my-language-chatbot/rules.md"
    environment:
      - name: MESHAGENT_TOKEN
        token:
          identity: my-language-chatbot
    storage:
      room:
        - path: /data
          read_only: false

  ```
</CodeGroup>

This example adds a `language` variable and uses `{{ language }}` inside the `container.command`.

Validate and deploy it:

```bash bash theme={null}
meshagent service validate-template --file meshagent.yaml

meshagent service create-template \
  --file meshagent.yaml \
  --value language=Spanish \
  --room myroom
```

Use `--global` instead of `--room myroom` to deploy the rendered service project-wide.

If you want to inspect the rendered YAML first, use `meshagent service render-template --file meshagent.yaml --value language=Spanish`.

### Example 3: `Service` with `external`

Use `external` when MeshAgent should route to a service you already host elsewhere instead of running a container for you.

<CodeGroup>
  ```yaml Yaml theme={null}
  kind: Service
  version: v1
  metadata:
    name: mcp-deepwiki
    description: "Expose the DeepWiki MCP server"
  ports:
    - num: 443
      type: http
      endpoints:
        - path: /mcp
          mcp:
            label: "mcp-deepwiki"
            description: "MCP DeepWiki tools"
            allowed_tools:
              - tool_names: ["search", "read_page"]
                read_only: true
  external:
    url: "https://mcp.deepwiki.com"

  ```
</CodeGroup>

This example is a fixed `Service` that routes to an MCP server hosted outside MeshAgent. MeshAgent does not run the MCP server. It uses the `external.url` and `ports.endpoints` config to register that server's tools in the room.

Validate and deploy it:

```bash bash theme={null}
meshagent service validate --file mcp-service.yaml
meshagent service create --file mcp-service.yaml --room myroom
```

If you need installer-provided values such as a URL, label, or OAuth settings, you can use `external` inside a `ServiceTemplate` too.

For a longer walkthrough, see [Connect to an External MCP Server](./external_mcp_service).

## Manage deployed services

Use `meshagent service` after deployment to inspect and manage what you deployed.

```bash bash theme={null}
# List project services
meshagent service list

# List room services
meshagent service list --room myroom

# View details for one service
meshagent service get SERVICE_ID

# Update a deployed Service
meshagent service update --file meshagent.yaml --room myroom --id SERVICE_ID

# Update a deployed ServiceTemplate
meshagent service update-template --file meshagent.yaml --value key=value --room myroom --id SERVICE_ID

# Delete a deployed service
meshagent service delete SERVICE_ID --room myroom
```

Omit `--room myroom` when you are updating, listing, or deleting a project service instead.

## Service configuration field reference

### Top-level fields

| Field       | Required | Description                                                                 |
| ----------- | -------- | --------------------------------------------------------------------------- |
| `version`   | Yes      | Schema version. Always `v1`.                                                |
| `kind`      | Yes      | `Service` or `ServiceTemplate`.                                             |
| `metadata`  | Yes      | Service identity and display information.                                   |
| `agents`    | No       | Agent identities exposed by this service.                                   |
| `files`     | No       | Files MeshAgent should seed into room storage if they do not already exist. |
| `ports`     | No       | Network ports and HTTP endpoints MeshAgent can route to.                    |
| `container` | \*       | Container configuration. Mutually exclusive with `external`.                |
| `external`  | \*       | External service URL. Mutually exclusive with `container`.                  |
| `variables` | No       | User-provided inputs for templating. `ServiceTemplate` only.                |

\* Either `container` or `external` is required, but not both.

### metadata

Identifies the service and provides information displayed in the UI.

| Field         | Type   | Required | Description                                          |
| ------------- | ------ | -------- | ---------------------------------------------------- |
| `name`        | string | Yes      | Unique service name.                                 |
| `description` | string | No       | Description shown in UI.                             |
| `repo`        | string | No       | Source code repository URL.                          |
| `icon`        | string | No       | Icon or emoji for UI display.                        |
| `annotations` | object | No       | Key-value metadata. See [Annotations](#annotations). |

### agents

Declares the participant identities this service provides. MeshAgent uses this to route requests, apply policies, and display agents in the UI.

| Field         | Type   | Required | Description                                                                |
| ------------- | ------ | -------- | -------------------------------------------------------------------------- |
| `name`        | string | Yes      | Unique agent identity within the service.                                  |
| `description` | string | No       | Display text describing the agent.                                         |
| `annotations` | object | No       | Key-value metadata. See [Annotations](#annotations).                       |
| `channels`    | object | No       | Channel-specific routing config for mail, messaging, queues, and toolkits. |
| `email`       | object | No       | Mailbox settings for this agent identity.                                  |
| `heartbeat`   | object | No       | Recurring queue turn for this agent.                                       |

#### agents.channels

Use `channels` when one deployed agent should participate in more than one entry point.

| Field       | Type | Required | Description                                    |
| ----------- | ---- | -------- | ---------------------------------------------- |
| `email`     | list | No       | Inbound email channels for the agent.          |
| `messaging` | list | No       | Messaging channels and optional named prompts. |
| `queue`     | list | No       | Queue channels the agent should consume.       |
| `toolkit`   | list | No       | Toolkits the agent should register or expose.  |

Queue channel entries support:

| Field            | Type   | Required | Description                                                     |
| ---------------- | ------ | -------- | --------------------------------------------------------------- |
| `queue`          | string | Yes      | Queue name the agent should consume.                            |
| `threading_mode` | string | No       | Threading mode for queue-delivered work, such as `default-new`. |
| `message_schema` | object | No       | JSON schema describing expected queue payload shape.            |

Messaging channel entries support:

| Field      | Type   | Required | Description                                                   |
| ---------- | ------ | -------- | ------------------------------------------------------------- |
| `protocol` | string | No       | Messaging protocol. Defaults to `meshagent.agent-message.v1`. |
| `prompts`  | list   | No       | Named prompts available on that messaging channel.            |

#### agents.email

Use `email` when the agent identity itself should have mailbox settings attached to it.

| Field     | Type    | Required | Description                                         |
| --------- | ------- | -------- | --------------------------------------------------- |
| `address` | string  | Yes      | Email address for the agent.                        |
| `public`  | boolean | No       | Whether anyone can send to it. Defaults to `false`. |

#### agents.heartbeat

Use `heartbeat` when the agent should enqueue a recurring turn for itself without creating a separate scheduled-task resource by hand.

| Field       | Type    | Required | Description                                                                                                                                                                                                           |
| ----------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queue`     | string  | Yes      | Queue that receives the recurring work item.                                                                                                                                                                          |
| `thread_id` | string  | No       | Optional thread path or template. Time tokens such as `{year}`, `{month}`, `{day}`, `{hour}`, and `{minute}` are expanded at runtime. Token matching is case-insensitive, so `{YYYY}/{MM}/{DD}/{HH}/{mm}` also works. |
| `prompt`    | list    | Yes      | Prompt content for each heartbeat turn. Uses typed content items such as `text` and `file`.                                                                                                                           |
| `minutes`   | integer | Yes      | Interval, in minutes, between heartbeats.                                                                                                                                                                             |

Heartbeat `prompt` items use the same typed content model as agent turns:

| Field  | Type   | Required | Description                                                     |
| ------ | ------ | -------- | --------------------------------------------------------------- |
| `type` | string | Yes      | `text` or `file`.                                               |
| `text` | string | No       | Text content when `type: text`.                                 |
| `url`  | string | No       | File URL when `type: file`, including `room:///...` references. |

Example:

```yaml theme={null}
agents:
  - name: assistant
    annotations:
      meshagent.agent.type: ChatBot
      meshagent.chatbot.threading: "default-new"
      meshagent.chatbot.thread-dir: agents/assistant/threads
    email:
      address: assistant@example.com
      public: true
    heartbeat:
      queue: assistant-scheduled-tasks
      thread_id: /agents/assistant/threads/heartbeats/{year}/{month}/{day}/{hour}.{minute}.thread
      prompt:
        - type: file
          url: room:///agents/assistant/heartbeat.md
      minutes: 60

files:
  - path: /agents/assistant/heartbeat.md
    text: |
      # Assistant Heartbeat
      Review recent room activity, unresolved requests, and any obvious follow-up work.
      If there is clear useful work to do, do it directly.
      If there is nothing useful to add, end the turn quietly without posting a filler message.
      Previous threads can be found looking at the threads in the path format based on the time: /data/agents/assistant/threads/heartbeats/{year}/{month}/{day}/{hour}.{minute}.thread
      Read the last few heartbeat threads to make sure you are up to date with what has been done so far
```

This mirrors the built-in assistant service template: each heartbeat run is queued to the assistant, uses the heartbeat prompt file from room storage, and writes the run into a timestamped thread under the assistant thread directory.

### files

Top-level `files` seed room storage before the service starts. MeshAgent creates each file only if it does not already exist, which makes this useful for editable starter content such as prompts, rules, or templates.

| Field  | Type   | Required | Description                  |
| ------ | ------ | -------- | ---------------------------- |
| `path` | string | Yes      | Room-storage path to create. |
| `text` | string | Yes      | Initial file contents.       |

Use top-level `files` when the content should live in room storage and remain editable after deploy. Use `container.storage.files` when the file should be mounted inside the running container itself.

### container

Defines a container for MeshAgent to run. Fields marked **†** are only available in `Service`, not `ServiceTemplate`.

| Field              | Type                  | Required | Description                                                                                                  |
| ------------------ | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------ |
| `image`            | string                | Yes      | Container image (for example `meshagent/cli:default`).                                                       |
| `template`         | `"agent"` or `"none"` | No       | Runtime defaults to apply when the container starts. Defaults to `"agent"`.                                  |
| `command`          | string                | No       | Command to execute when the container starts.                                                                |
| `working_dir`      | string                | No       | Absolute working directory used when starting the container command.                                         |
| `environment`      | list                  | No       | Environment variables to set in the container.                                                               |
| `run_as`           | object                | No       | Service account identity and runtime scopes. Required when using `SecretValue` environment variables.        |
| `storage`          | object                | No       | Storage volumes to mount into the container.                                                                 |
| `on_demand`        | boolean               | No       | When true, the container runs only when explicitly invoked.                                                  |
| `writable_root_fs` | boolean               | No       | Allow writes to the container's root filesystem for the life of that container instance. Default: read-only. |
| `private`          | boolean               | No       | Advanced setting that keeps interactive container access private to the owning service. Default: true.       |

#### container.template

`container.template` controls the MeshAgent runtime defaults added when the container starts:

* `"agent"` (default): mount room storage at `/data`, create a participant token for the container name with role `agent` and default agent API permissions, and inject MeshAgent/OpenAI/Anthropic runtime environment variables.
* `"none"`: do not add those defaults. Only values explicitly configured in the manifest or container start request are passed through.

The `"agent"` template sets defaults only when the environment variable is not already configured. Values in `container.environment` override template defaults.

The `"agent"` template may set:

| Variable                | Description                                                                                         |
| ----------------------- | --------------------------------------------------------------------------------------------------- |
| `MESHAGENT_TOKEN`       | Room participant token for the container name, with role `agent` and default agent API permissions. |
| `OPENAI_API_KEY`        | Same participant token, for OpenAI-compatible clients using MeshAgent's proxy.                      |
| `ANTHROPIC_API_KEY`     | Same participant token, for Anthropic-compatible clients using MeshAgent's proxy.                   |
| `SMTP_PASSWORD`         | Same participant token, for SMTP authentication.                                                    |
| `SMTP_USERNAME`         | Container name, for SMTP authentication.                                                            |
| `SMTP_HOSTNAME`         | MeshAgent SMTP hostname, set from `MESHAGENT_MAIL_DOMAIN` when available.                           |
| `SMTP_PORT`             | SMTP port. Defaults to `587`.                                                                       |
| `OPENAI_BASE_URL`       | OpenAI-compatible MeshAgent proxy URL for the room runtime.                                         |
| `ANTHROPIC_BASE_URL`    | Anthropic-compatible MeshAgent proxy URL for the room runtime.                                      |
| `MESHAGENT_API_URL`     | MeshAgent API URL reachable from the container.                                                     |
| `MESHAGENT_ROOM_URL`    | MeshAgent room API base URL reachable from the container.                                           |
| `MESHAGENT_ROOM`        | Current room name.                                                                                  |
| `MESHAGENT_PROJECT_ID`  | Current project ID, when available.                                                                 |
| `MESHAGENT_SESSION_ID`  | Current session ID, when available.                                                                 |
| `OTEL_ENDPOINT`         | OpenTelemetry collector endpoint reachable from the container.                                      |
| `OTEL_PYTHON_LOG_LEVEL` | Python OpenTelemetry log level forwarded from the room runtime, when available.                     |
| `MESHAGENT_MAIL_DOMAIN` | MeshAgent mail domain forwarded from the room runtime, when available.                              |

#### container.run\_as

`container.run_as` selects the service account identity used by the container.
The default scope list grants access to the HTTP/MCP secret proxy without
granting broad project administration.

| Field    | Type            | Required | Description                                                                    |
| -------- | --------------- | -------- | ------------------------------------------------------------------------------ |
| `email`  | string          | Yes      | Service account email the container runs as.                                   |
| `scopes` | list of strings | No       | Runtime scopes for the service-account token. Defaults to `["secrets:proxy"]`. |

<a id="container-environment" />

#### container.environment

Each entry sets an environment variable in the container. A variable can come from a literal `value`, a MeshAgent `token`, or a service-account `secret`.

| Field    | Type   | Required | Description                                                                                      |
| -------- | ------ | -------- | ------------------------------------------------------------------------------------------------ |
| `name`   | string | Yes      | Environment variable name.                                                                       |
| `value`  | string | No       | Literal string value.                                                                            |
| `token`  | object | No       | Request a participant token to be generated and injected as the value.                           |
| `secret` | object | No       | Load a secret available to the service account in `container.run_as` and inject it as the value. |

Use:

* `value` for plain configuration
* `token` for MeshAgent API access
* `secret` for service-account credentials. `container.run_as` is required when any environment variable uses `secret`.

`token` fields:

| Field      | Type   | Required | Description                                                                  |
| ---------- | ------ | -------- | ---------------------------------------------------------------------------- |
| `identity` | string | Yes      | Participant identity the token is issued for.                                |
| `api`      | object | No       | API scope granted to the token. See [API Scopes](../../rest_api/api_scopes). |
| `role`     | string | No       | Participant role (for example `user`, `agent`, or `tool`).                   |

`secret` fields:

| Field | Type   | Required | Description                                                      |
| ----- | ------ | -------- | ---------------------------------------------------------------- |
| `id`  | string | Yes      | Secret ID to load for the service account in `container.run_as`. |

This pattern is common for services: `token` gives the service access to MeshAgent, while `secret` gives it an external credential through its service account.

<a id="container-storage" />

#### container.storage

Mounts storage volumes into the container.

| Field        | Type | Description                                                             |
| ------------ | ---- | ----------------------------------------------------------------------- |
| `room`       | list | Per-room storage. Read/write by default.                                |
| `project`    | list | Project-wide shared storage. Read-only by default.                      |
| `images`     | list | Content from another container image. Read-only by default.             |
| `files`      | list | Inline text content mounted as a file. Read-only by default.            |
| `empty_dirs` | list | Writable temporary directories mounted into the container.              |
| `configs`    | list | MeshAgent runtime config mounts such as `spec.json` and `members.json`. |

Room and project mount fields:

| Field       | Type    | Required | Description                             |
| ----------- | ------- | -------- | --------------------------------------- |
| `path`      | string  | Yes      | Mount path inside the container.        |
| `subpath`   | string  | No       | Subdirectory within the storage volume. |
| `read_only` | boolean | No       | Whether the mount is read-only.         |

Image mount fields:

| Field       | Type    | Required | Description                      |
| ----------- | ------- | -------- | -------------------------------- |
| `image`     | string  | Yes      | Source container image.          |
| `path`      | string  | Yes      | Mount path inside the container. |
| `subpath`   | string  | No       | Subdirectory within the image.   |
| `read_only` | boolean | No       | Whether the mount is read-only.  |

File mount fields:

| Field       | Type    | Required | Description                      |
| ----------- | ------- | -------- | -------------------------------- |
| `path`      | string  | Yes      | Mount path inside the container. |
| `text`      | string  | Yes      | File contents.                   |
| `read_only` | boolean | No       | Whether the mount is read-only.  |

Empty directory mount fields:

| Field       | Type    | Required | Description                                   |
| ----------- | ------- | -------- | --------------------------------------------- |
| `path`      | string  | Yes      | Mount path inside the container.              |
| `read_only` | boolean | No       | Whether the temporary directory is read-only. |

Config mount fields:

| Field  | Type   | Required | Description                                                                                                              |
| ------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `path` | string | Yes      | Directory inside the container where MeshAgent runtime config files should be mounted. Defaults to `/var/run/meshagent`. |

Config mounts are useful when a shell tool or helper process inside the container needs access to runtime files such as `spec.json` and `members.json`.

### external

Routes traffic to a service running outside MeshAgent. Requires `ports` to define how MeshAgent reaches the service.

| Field | Type   | Required | Description                       |
| ----- | ------ | -------- | --------------------------------- |
| `url` | string | Yes      | URL where the service is running. |

### ports

Defines network ports the service listens on and how MeshAgent routes HTTP traffic to them.

| Field         | Type         | Required | Description                                            |
| ------------- | ------------ | -------- | ------------------------------------------------------ |
| `num`         | `"*"` or int | Yes      | Port number, or `"*"` for auto-assignment.             |
| `type`        | string       | No       | Protocol: `http` or `tcp`.                             |
| `liveness`    | string       | No       | HTTP path for health checks.                           |
| `endpoints`   | list         | No       | Endpoints served on this port.                         |
| `published`   | boolean      | No       | Expose the port to the internet.                       |
| `public`      | boolean      | No       | When false, requests must include a participant token. |
| `annotations` | object       | No       | Key-value metadata.                                    |

<a id="ports-endpoints" />

#### ports.endpoints

Each endpoint maps a URL path to either a MeshAgent-native service or an MCP server.

| Field         | Type   | Required | Description                                               |
| ------------- | ------ | -------- | --------------------------------------------------------- |
| `path`        | string | Yes      | URL path for this endpoint.                               |
| `meshagent`   | object | No       | MeshAgent-native endpoint. Mutually exclusive with `mcp`. |
| `mcp`         | object | No       | MCP server endpoint. Mutually exclusive with `meshagent`. |
| `annotations` | object | No       | Key-value metadata.                                       |

##### meshagent endpoint

Connects the endpoint to a MeshAgent participant identity.

| Field      | Type   | Required | Description                                                       |
| ---------- | ------ | -------- | ----------------------------------------------------------------- |
| `identity` | string | Yes      | Participant identity for this endpoint.                           |
| `api`      | object | No       | API scope overrides. See [API Scopes](../../rest_api/api_scopes). |

##### mcp endpoint

Registers an MCP server as a toolkit in the room.

| Field                 | Type   | Required | Description                                 |
| --------------------- | ------ | -------- | ------------------------------------------- |
| `label`               | string | Yes      | Toolkit display name.                       |
| `description`         | string | No       | Description of what the toolkit provides.   |
| `allowed_tools`       | list   | No       | Filters which tools are exposed.            |
| `headers`             | object | No       | Custom HTTP headers to include in requests. |
| `require_approval`    | string | No       | `always` or `never`.                        |
| `oauth`               | object | No       | OAuth client configuration.                 |
| `openai_connector_id` | string | No       | OpenAI connector ID.                        |

`allowed_tools` entries:

| Field        | Type    | Required | Description                   |
| ------------ | ------- | -------- | ----------------------------- |
| `tool_names` | list    | Yes      | Tool names to allow.          |
| `read_only`  | boolean | No       | Treat the tools as read-only. |

`oauth` fields:

| Field                    | Type    | Required | Description                                 |
| ------------------------ | ------- | -------- | ------------------------------------------- |
| `client_id`              | string  | Yes      | OAuth client ID.                            |
| `client_secret`          | string  | No       | OAuth client secret.                        |
| `authorization_endpoint` | string  | Yes      | Authorization endpoint URL.                 |
| `token_endpoint`         | string  | Yes      | Token endpoint URL.                         |
| `no_pkce`                | boolean | No       | Disable PKCE (Proof Key for Code Exchange). |
| `scopes`                 | list    | No       | OAuth scopes to request.                    |

<a id="variables-servicetemplate-only" />

### variables (ServiceTemplate only)

Defines user-provided inputs for a `ServiceTemplate`. Values are substituted into the YAML using `{{ variable_name }}` syntax.

Template values are rendered into the manifest before validation, so they can be used anywhere the resulting YAML remains valid.

| Field         | Type    | Required | Description                                                   |
| ------------- | ------- | -------- | ------------------------------------------------------------- |
| `name`        | string  | Yes      | Variable identifier. Referenced as `{{ name }}` in templates. |
| `title`       | string  | No       | Human-readable label shown in install UI.                     |
| `description` | string  | No       | Help text shown in UI and Powerboards.                        |
| `enum`        | list    | No       | Restricts input to specific values. Displayed as a dropdown.  |
| `optional`    | boolean | No       | Whether the variable can be left blank.                       |
| `obscure`     | boolean | No       | Hides the value in UI. Use for sensitive data.                |
| `type`        | string  | No       | Type hint (for example `email`).                              |
| `annotations` | object  | No       | Key-value metadata. See [Annotations](#annotations).          |

<a id="annotations" />

### Annotations

Annotations are key-value strings attached to services, agents, or variables. MeshAgent and Powerboards use specific annotation keys to control behavior. You can also define custom annotations.

#### Service annotations

Set in `metadata.annotations`.

| Key                        | Description                                          |
| -------------------------- | ---------------------------------------------------- |
| `meshagent.service.id`     | Unique identifier for the service.                   |
| `meshagent.service.readme` | URL or inline content for the service documentation. |

#### Agent annotations

Set in `agents[].annotations`.

| Key                              | Description                                                                                                 |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `meshagent.agent.type`           | Internal agent type metadata used by service manifests and generated specs.                                 |
| `meshagent.agent.widget`         | UI widget to display for this agent.                                                                        |
| `meshagent.agent.schedule`       | JSON string defining a `ScheduledTaskSpec`. Use either `queue: { name, payload }` or `container`, not both. |
| `meshagent.agent.shell.command`  | Shell command for `Shell`-type agents.                                                                      |
| `meshagent.agent.dataset.schema` | Dataset schema metadata for the agent.                                                                      |

#### Variable annotations

Set in `variables[].annotations`.

| Key                     | Description                                                               |
| ----------------------- | ------------------------------------------------------------------------- |
| `meshagent.secret.id`   | Suggested secret identifier for installer-collected credential variables. |
| `meshagent.secret.name` | Display name for installer-collected credential variables.                |
| `meshagent.secret.type` | Credential content type for installer-collected variables.                |

#### Event annotations

Set in `agents[].annotations`. Subscribe an agent to room events. The value is the name of a queue that a queue-consuming agent can process.

| Key                                       | Description                                  |
| ----------------------------------------- | -------------------------------------------- |
| `meshagent.events.service.created`        | Fires when a service is created in the room. |
| `meshagent.events.service.updated`        | Fires when a service is updated.             |
| `meshagent.events.room.user.grant.create` | Fires when a user is added to the room.      |
| `meshagent.events.room.user.grant.delete` | Fires when a user is removed from the room.  |
| `meshagent.events.room.user.grant.update` | Fires when a user's room grant is updated.   |

## Read next

* [Build and Deploy Images](../containers/meshagent_image): build and deploy images with `meshagent build` and `meshagent deploy`
* [Secrets and Credentials](../../secrets/overview): manage user-owned and service-account-owned secrets
* [Observability](../../observability/overview): inspect logs, traces, metrics, and custom OpenTelemetry output
* [Webhook Handoffs](../../services/webhooks): handle `room.call` webhook handoffs in your own services
