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

# MCP servers

> Connect MCP servers and configure their tools, permissions, and authentication

[MCP (Model Context Protocol)](https://modelcontextprotocol.io) is an open standard for connecting tools to models. Add a server such as Linear, Notion, or your own internal service to an agent config, and Omnara discovers its tools and makes them available to the model.

## Declaring a server

Each entry under `mcp:` defines one server:

```yaml theme={null}
mcp:
  linear:
    url: https://mcp.linear.app/mcp
    auth:
      type: oauth
      secret_id: sec_gik7mv4qtrwz3jehcyd5n6a2bf
    permission:
      mode: always_ask
    tools:
      get_issue:
        permission:
          mode: always_allow
      delete_issue:
        enabled: false
  internal-docs:
    url: https://mcp.internal.acme.dev/mcp
    auth:
      type: bearer
      secret_id: sec_7jehcyd5n6a2bfgik7mv4qtrwz
    default_enabled: false      # opt-in: only listed tools are exposed
    tools:
      search_docs:
        enabled: true
```

| Field | Notes |
| - | - |
| `url` | The server's MCP endpoint. |
| `auth.type` | `bearer` (a token in a [generic secret](/organization/secrets)), `oauth` (an `oauth_token_set` secret), or `sigv4` (an `aws_credentials` secret). Omit for unauthenticated servers. |
| `auth.secret_id` | The `sec_…` reference. The config never contains the credential itself. |
| `auth.service` | SigV4 signing service. Required for `sigv4`. |
| `auth.region` | AWS signing region. Required for `sigv4`. |
| `permission` | Server-wide default mode; per-tool `permission` overrides it. MCP tools default to `always_ask` — third-party code asks first. |
| `default_enabled` | `false` disables tools by default. Set `enabled: true` for each tool you want to expose. |
| `deferred` | `true` keeps the server's tools out of the model's context until `tool_search` loads them; per-tool `deferred` overrides it. See [Deferred tools](/agents/configuration#deferred-tools). |

### How MCP tools are named

MCP tool names combine the server and tool: `mcp__{server}__{tool}`. For example, Linear's `get_issue` tool appears as `mcp__linear__get_issue`.

These names appear in `model_output` tool calls and in [`GET /tool-calls`](/tools/custom#find-calls-waiting-for-you) with `type: "mcp"`. Server names cannot contain underscores, and the combined name must fit within 64 characters.

## Protocol versions and tool caching

* Omnara supports both stateless (`2026-07-28`) and session-based (`2025-11-25` and earlier) MCP servers. Stateless MCP servers use the cache TTLs returned by the server with a minimum freshness window of five minutes; capabilities and tools are cached keyed on org, server, and authentication information (if present).

## Bearer auth

For servers that take a static token, store the token as a generic secret and reference it:

**API**

The examples assume the client, `$ORG`/`orgID`, and `$PROJ`/`projectID` setup from the [quickstart](/quickstart).

```bash CLI theme={null}
omnara secrets create \
  --owner-kind project \
  --owner-project-id proj_7mv4qtrwz3jehcyd5n6a2bfgik \
  --name internal-docs-mcp-token \
  --material-kind generic \
  --material-value "mcp-token-..."
```

```bash REST theme={null}
curl "$OMNARA_API/orgs/$ORG/secrets" \
  -H "Authorization: Bearer $OMNARA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "owner": { "kind": "project", "project_id": "'"$PROJ"'" },
    "name": "internal-docs-mcp-token",
    "material": { "kind": "generic", "value": "mcp-token-..." }
  }'
```

```typescript SDK theme={null}
const secret = await sdk.createSecret({
  client,
  path: { orgID },
  body: {
    owner: { kind: 'project', project_id: projectID },
    name: 'internal-docs-mcp-token',
    material: { kind: 'generic', value: 'mcp-token-...' },
  },
})
console.log(secret.data.id)
```

**Dashboard**

Open **Secrets** — the **User** or **Organization** tab with **All projects** selected in the project switcher, or the **Project** tab inside a project — and click **New secret**. Name it, choose the **Generic** kind, and paste the token. You can pick projects under **Share with** in the same dialog.

The response's `id` (`sec_…`) goes in `auth.secret_id` with `type: bearer`. Rotation is a [new secret version](/organization/secrets#rotate-a-secret) — configs keep referencing the same ID.

For a complete example you can deploy, using PostHog's MCP server with tool filtering and optional Slack delivery and daily schedule, see the [PostHog analytics agent](/examples/posthog-analytics-agent).

## AWS Signature Version 4 auth

For a SigV4-protected MCP server, create an **AWS credentials** secret in the dashboard, share it with the project, and select **AWS Signature V4** for server authentication:

```yaml theme={null}
mcp:
  aws:
    url: https://aws-mcp.us-east-1.api.aws/mcp
    auth:
      type: sigv4
      secret_id: sec_7jehcyd5n6a2bfgik7mv4qtrwz
      service: aws-mcp
      region: us-east-1
```

Omnara optionally assumes the role stored with the credentials, then signs each request with AWS Signature Version 4 using the configured service and region. It does not modify the MCP request body or create a bearer token.

`auth.region` is the MCP endpoint's signing region; it does not limit the AWS resource regions selected in tool calls. Omnara does not refresh a session token stored in the AWS credentials secret, so rotate that secret before the token expires.

## Connect with OAuth

For servers such as Linear, Omnara handles OAuth registration, authorization, and token exchange. It stores the resulting credentials in an `oauth_token_set` secret that your config references.

**API**

The CLI command runs the whole flow — it opens the server's authorization page in your browser, waits for approval, and prints the created secret's ID (`--no-browser` prints the authorization URL instead of opening it). The REST API and the SDK start the flow with one request and return an authorization URL to open yourself:

```bash CLI theme={null}
omnara secrets mcp-oauth \
  --owner-kind project \
  --owner-project-id proj_7mv4qtrwz3jehcyd5n6a2bfgik \
  --name linear-mcp \
  --mcp-url https://mcp.linear.app/mcp
```

```bash REST theme={null}
curl "$OMNARA_API/orgs/$ORG/secrets/mcp-oauth" \
  -H "Authorization: Bearer $OMNARA_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "owner": { "kind": "project", "project_id": "'"$PROJ"'" },
    "name": "linear-mcp",
    "mcp_url": "https://mcp.linear.app/mcp",
    "return_to": "/projects/'"$PROJ"'/secrets"
  }'
```

```typescript SDK theme={null}
const start = await sdk.startSecretMcpoAuth({
  client,
  path: { orgID },
  body: {
    owner: { kind: 'project', project_id: projectID },
    name: 'linear-mcp',
    mcp_url: 'https://mcp.linear.app/mcp',
    return_to: `/projects/${projectID}/secrets`,
  },
})
console.log(start.data.authorization_url)
```

```json theme={null}
{
  "authorization_url": "https://linear.app/oauth/authorize?client_id=…&state=…",
  "expires_at": "2026-08-03T20:28:00Z"
}
```

Open `authorization_url` in a browser and approve access. Omnara reads the server's OAuth metadata, registers a client when supported, exchanges the authorization code, and stores the result as an `oauth_token_set` secret. You can also pass an existing client ID and secret (`--client-id` and `--client-secret` on the CLI, `client_id` and `client_secret` in the start request).

The browser returns to the dashboard-relative `return_to` path with `?mcp_oauth=success&secret_id=sec_…` appended. Absolute return URLs are rejected. Use that secret ID with `auth.type: oauth`. The authorization link is single-use and expires in 10 minutes.

To authorize a server and attach it to an existing config in one step, use `omnara agents mcp-add {agent-id} --server-name linear --mcp-url …` or `omnara profiles mcp-add {agent-profile-id} --server-name linear --mcp-url …`. Both create the OAuth secret and add the `mcp:` entry to the agent's or profile's config for you.

**Dashboard**

On any Secrets page, click **New secret**, pick the **MCP OAuth secret** kind, name it, and enter the MCP server URL. **Authorize and Create Secret** sends you to the server's authorization page; approve, and you land back in the dashboard with the secret saved.

The secret's row action **Copy MCP config** then puts a ready-to-paste `mcp:` YAML block — the server URL plus `auth.type: oauth` and the `secret_id` — on your clipboard.

Re-running the flow with the same owner and name creates a new *version* of the same secret, so re-authorizing never forces a config change.

Troubleshooting:

* If the MCP server doesn't actually require auth, the start call returns `409` — declare it without an `auth` block instead.
* If a token expires and cannot be refreshed, affected tool calls return `mcp_connection_failed`. Re-run the OAuth flow to create a fresh secret version; the config can keep using the same secret ID.

> [!NOTE]
>
> Full schema and playground: [Start MCP OAuth for a new secret](/api-reference/endpoints/secrets/start-mcp-oauth-for-a-new-secret).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.