> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended**:
> ```bash
> npx @scalekit-inc/cli setup
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for 40+ agents.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect Hermes to AgentKit

[Hermes](https://hermes-agent.nousresearch.com/docs/) is the self-improving AI agent from Nous Research. Hermes runs where you put it: a laptop, a small VPS, or a GPU cluster. You reach Hermes from the command line, or from a messaging app such as Slack or Telegram, and Hermes also runs work on a cron schedule.

Hermes ships with 60+ built-in tools, and the built-in tools stop at the edge of your machine. A Hermes agent that reads your Gmail, posts to your Slack, or opens a GitHub pull request needs an access token for each app. Every access token belongs to one user.

Scalekit holds the connected account for that user, stores the access token, and refreshes the access token. Hermes reaches Scalekit through a **skill**. A skill is an on-demand instruction set plus scripts, and Hermes loads a skill from `~/.hermes/skills/` when a chat calls for one. The skill tells Hermes how to find the Scalekit connection, how to check that the user finished auth, and how to call the tool with the stored token. The steps below install the skill.

```d2
direction: right

Hermes: "Hermes Agent" {
  style.font-size: 18
}

Scalekit: {
  label: "Scalekit AgentKit"
  Auth: "OAuth handler"
  Vault: "Token vault"
}

Providers: {
  label: "Third-party services"
  Gmail: "Gmail"
  Slack: "Slack"
  Salesforce: "Salesforce"
  More: "200+ more"
}

Hermes -> Scalekit.Auth: "Execute tools"
Scalekit.Vault -> Providers.Gmail
Scalekit.Vault -> Providers.Slack
Scalekit.Vault -> Providers.Salesforce
Scalekit.Vault -> Providers.More
```

## Prerequisites

- [Hermes Agent](https://hermes-agent.nousresearch.com/docs/getting-started/quickstart) installed
- A Scalekit account with AgentKit enabled: [sign up at app.scalekit.com](https://app.scalekit.com)
- At least one connection in **Dashboard > AgentKit > Connections**. Create the connection before you ask Hermes to use the app. See [connections](/agentkit/connectors/).
- [`uv`](https://docs.astral.sh/uv/) on your `PATH`

## How the skill works

The skill runs the following loop when you name an app:

1. The skill looks up the Scalekit connection you already created for [GitHub](/agentkit/connectors/github/), [Gmail](/agentkit/connectors/gmail/), or another app.
2. The skill checks the user's connected account. `ACTIVE` means auth is complete and Scalekit holds a token.
3. The skill returns a magic link when the connected account is not `ACTIVE`.
4. The skill fetches the tool schema, calls the tool, and returns the result.
5. The skill calls the Scalekit HTTP proxy when the connector has no named tool.

A magic link is a one-time URL. The hosted page shows OAuth consent, or an API-key form.

## Get started

<ProductAgentBlock skill="hermes-delegated-auth" />

1. ### Install the skill

   Install `hermes-delegated-auth` from authstack:

   ```bash showLineNumbers=false
   hermes skills install scalekit-inc/authstack/kits/agentkit/host/hermes-delegated-auth
   ```

   Confirm the skill is enabled:

   ```bash showLineNumbers=false
   hermes skills list
   ```

   ```txt showLineNumbers=false
   │ hermes-delegated-auth │          │ local  │ local │ enabled │
   ```

   Install the Python dependencies:

   ```bash showLineNumbers=false
   cd "${HERMES_HOME:-$HOME/.hermes}/skills/hermes-delegated-auth"
   uv sync
   ```

   New chats load `/hermes-delegated-auth`. Run `/reset` in a chat that is already open.

   `HERMES_HOME` changes the skill path. The commands on this page use `~/.hermes`.
**Install from the authstack path:** A raw `SKILL.md` URL leaves out `scripts/`, and the skill then fails at run time. The `integrate-agentkit-host` skill is for coding agents, not for Hermes.

2. ### Configure credentials

   Put only Scalekit client credentials in `~/.hermes/.env`. Scalekit stores and refreshes the provider tokens for GitHub, Gmail, and Slack. Provider tokens never belong in a Hermes file.

   ```bash title="~/.hermes/.env"
   SCALEKIT_CLIENT_ID=skc_your_client_id
   # Threat: a leaked secret lets anyone call tools as any ACTIVE identifier.
   SCALEKIT_CLIENT_SECRET=your_client_secret
   SCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.cloud
   SCALEKIT_IDENTIFIER=usr_8f3a2c
   ```

   | Parameter | Description |
   |-----------|-------------|
   | `SCALEKIT_CLIENT_ID` | Your Scalekit client ID Required |
   | `SCALEKIT_CLIENT_SECRET` | Your Scalekit client secret Required |
   | `SCALEKIT_ENVIRONMENT_URL` | Your Scalekit environment URL Required |
   | `SCALEKIT_IDENTIFIER` | Default user the host acts as Required |

   Copy the client ID, client secret, and environment URL from **Dashboard > Developers > Settings > API Credentials**.

   `SCALEKIT_IDENTIFIER` is not a dashboard credential. Pick an opaque id that only your system knows, for example `usr_8f3a2c`. Every `tool_exec.py` command needs `SCALEKIT_IDENTIFIER`, including `--list-connections`.
**Keep the identifier unguessable:** An email address or a first name is guessable. `SCALEKIT_CLIENT_SECRET` lets anyone call tools as any `ACTIVE` identifier, so a guessable identifier widens the damage. Add [user verification](/agentkit/user-verification/) in a multi-user product so the wrong person cannot activate the slot.

3. ### Ask Hermes to act

   Start a chat and ask for a real action. Hermes acts as `SCALEKIT_IDENTIFIER` by default, and Hermes passes `--identifier` to the skill when you name another user in the prompt.

   ```txt showLineNumbers=false
   You: Who am I on GitHub?
   ```

   Hermes loads the skill and runs these steps:

1. Hermes looks up the GitHub connection.
2. Hermes checks that the connected account is `ACTIVE`, and returns a magic link when the account is not.
3. Hermes fetches the tool schema.
4. Hermes calls the tool and returns your GitHub login.
   ```txt showLineNumbers=false
   You: Show me my latest unread emails
   ```

   Hermes runs these steps:

1. Hermes looks up the Gmail connection.
2. Hermes returns a magic link when you have not authorized Gmail yet.
3. Hermes fetches the tool schema.
4. Hermes returns the mail.
   ```txt showLineNumbers=false
   You: Read my Notion page https://notion.so/My-Page-abc123
   ```

   Hermes runs these steps:

1. Hermes looks up the Notion connection.
2. Hermes returns a magic link when you have not authorized Notion yet.
3. Hermes fetches the page tool schema.
4. Hermes returns the page content.
   ```txt showLineNumbers=false
   You: As identifier usr_8f3a2c, list my unread Slack DMs
   ```

   Hermes runs these steps:

1. Hermes looks up the Slack connection.
2. Hermes returns a magic link when the Slack connected account is not `ACTIVE`.
3. Hermes fetches the Slack tool schema.
4. Hermes returns the direct messages (DMs) as `usr_8f3a2c`.

   Create the Slack connection with **User scope** for the prompt above. A Slack connection with **Bot scope** acts as your Slack app instead of acting as the person. See the [Slack connector](/agentkit/connectors/slack/).
   ```txt showLineNumbers=false
   You: Create an out-of-office event tomorrow on the calendar for usr_8f3a2c
   ```

   Hermes runs these steps:

1. Hermes looks up the Google Calendar connection for `usr_8f3a2c`.
2. Hermes returns a magic link when the connected account is not `ACTIVE`.
3. Hermes calls the create-event tool.
4. Scalekit writes the event with the `usr_8f3a2c` token.
   Name the skill in the prompt to force the same path:

   ```txt showLineNumbers=false
   /hermes-delegated-auth who am I on GitHub?
   ```

## Verify it works

Confirm all of the following:

- The skill is listed (`hermes skills list` or `/skills`) as `hermes-delegated-auth`
- The connected account is `ACTIVE` after you finish the magic link
- Hermes returns data from the provider (the GitHub login, or unread mail)

Open the magic link again and retry the same prompt if the account stays inactive.

## Use Slack as a bot or as a user

Slack issues two kinds of token, and the token decides whose name appears on a message. A bot token posts as your Slack app. A user token posts as the person who authorized the app. Authorizing the Hermes gateway grants a bot token, so the gateway alone cannot post as a person.

| Job | Use |
|-----|-----|
| Chat with the agent in a channel or DM | Hermes Slack **gateway**. Bot tokens (`xoxb-` + `xapp-`) in `~/.hermes/.env`. See the [Hermes Slack setup](https://hermes-agent.nousresearch.com/docs/user-guide/messaging/slack). |
| Read private history, post, or act as a user | Scalekit Slack connection created with **User scope**. See the [Slack connector](/agentkit/connectors/slack/). |

The gateway bot is a channel into the agent. The gateway bot is not `usr_8f3a2c`.

Always-on hosts use both identities. The gateway hears the channel. Scalekit acts as the user.

## Run jobs on a schedule

Attach the skill to a Hermes cron job. Each fire is a fresh session. The skill uses Scalekit client credentials. You do not mint a session token.

```bash showLineNumbers=false
hermes cron create "0 9 * * *" "List unread emails for usr_8f3a2c and post a 5-line summary" --skill hermes-delegated-auth
```

Or in chat:

```txt showLineNumbers=false
/cron add "every weekday at 9am" "List unread emails for usr_8f3a2c and post a 5-line summary" --skill hermes-delegated-auth
```

The connected account must already be `ACTIVE`. Cron cannot click a magic link.

Leave the gateway running on a headless host. A new magic link can then land in Slack or Telegram. Do not put Gmail or Calendar refresh tokens in `~/.hermes/.env`.

## Choose how Hermes reaches Scalekit

Three patterns connect Hermes to Scalekit. Pick the one that matches your host.

**Pattern A: delegated skill.** The default for Hermes. The host holds Scalekit client credentials, and each turn names an identifier. No token sits on the host, so nothing expires.

**Pattern B: Virtual MCP on the host.** A Virtual MCP server exposes only the tools you list. Hermes reads one bearer token, so the host acts as one operator. You mint the token and reload the host.

**Pattern C: Virtual MCP in your app.** Your application mints a token before each agent run and passes it to the agent framework. Claude Managed Agents, Mastra, and CrewAI use Pattern C.

| Question | A: delegated skill | B: Virtual MCP on the host | C: Virtual MCP in your app |
|---|---|---|---|
| Who runs the agent | Hermes | Hermes | your app |
| How many end users | many; switch with `--identifier` | one operator | many |
| Credential on the host | Scalekit client ID and secret | one session-token bearer | none |
| Anything expires? | no | yes, about 1 hour by default | yes, per run |
| Who mints a replacement | nobody; there is no token | you, on a schedule, then reload | your code, before each run |
| Tool scoping | the full connector catalog | only the tools in the config | only the tools in the config |
| Best for | multi-user hosts and cron | a scoped, single-operator gateway | short agent runs |

The rest of this page covers Pattern A. For Pattern B, see [Run Virtual MCP on a Hermes host](#run-virtual-mcp-on-a-hermes-host). For Pattern C, see [Set up and connect a Virtual MCP server](/agentkit/mcp/configure-mcp-server/).

## Run Virtual MCP on a Hermes host
**Draft — not live-verified:** This flow has not been live-verified yet. Confirm the remint and `/reload-mcp` loop on a Hermes host before you treat this section as production guidance.

This is the long-running host setup. One operator. One bearer. You remint.

Use this when you want a scoped tool list on a single-operator Hermes gateway. Create the Virtual MCP config once. Mint a session token with `create_session_token`. Put the token in Hermes `config.yaml`. Reload the host before the token expires.

1. ### Create the Virtual MCP config once

   Create the server once per operator role. Save the static `mcp_server_url`. Do not regenerate the config on each remint.

   See [Set up and connect a Virtual MCP server](/agentkit/mcp/configure-mcp-server/).

2. ### Mint a session token

   Call `create_session_token` for one identifier. Set `expiry` above your reload interval.

   ```python showLineNumbers=false
   from datetime import timedelta

   token_response = scalekit_client.actions.mcp.create_session_token(
       mcp_config_id=config_id,
       identifier="usr_8f3a2c",
       expiry=timedelta(hours=2),
   )

   token = token_response.token
   ```

   `create_session_token` is the remint call. Call it again whenever you need a new token.

3. ### Put the token in Hermes config

   Write the token to `SCALEKIT_MCP_SESSION_TOKEN` in `~/.hermes/.env`. Point `~/.hermes/config.yaml` at the static URL and that env var.

   ```yaml title="~/.hermes/config.yaml"
   mcp_servers:
     scalekit:
       url: "<mcp_server_url>"
       headers:
         Authorization: "Bearer ${SCALEKIT_MCP_SESSION_TOKEN}"
   ```

   Do not set `auth: oauth`. A Scalekit Virtual MCP server takes a bearer token, not MCP OAuth.

   Do not run `hermes mcp login` against this URL.

4. ### Reload Hermes

   Restart Hermes, or run `/reload-mcp` so Hermes rereads the bearer.

5. ### Remint on a schedule

   On a cron, mint a new token, write `SCALEKIT_MCP_SESSION_TOKEN`, then run `/reload-mcp`. Repeat before the token expires. Set `expiry` above that interval.
An expired bearer fails at the next tool call, not at startup.

One Hermes host is one operator. Mint per-end-user tokens in your own application. See [Set up and connect a Virtual MCP server](/agentkit/mcp/configure-mcp-server/).

## Avoid these mistakes

- Do not put provider tokens in `~/.hermes/.env`. Put only Scalekit credentials there.
- Do not set `SCALEKIT_IDENTIFIER` to an email or another guessable value.
- Do not use the bundled Google Workspace skill (`~/.hermes/google_token.json`) if AgentKit owns the Google user. `~/.hermes/google_token.json` is one laptop login, not a per-user identifier.
- Do not run `hermes mcp login` against Scalekit Virtual MCP (a Scalekit MCP URL plus a session token). Virtual MCP uses a static bearer session token, and a static bearer token is not MCP OAuth.
- Do not treat the Slack bot token as “send as usr_8f3a2c”.
- Do not use one Hermes host as a multi-tenant identity provider. One gateway is one operator. Switch identifiers on the same host with `--identifier`. Mint per-end-user Virtual MCP session tokens in your own application, not inside Hermes.

## Next steps

[Browse connections](/agentkit/connectors/)
  [Authorize a user](/agentkit/tools/authorize/)
  [Example repo](https://github.com/scalekit-developers/hermes-agentkit-example)
## Common scenarios

<details>
<summary>How do I authorize a new connection?</summary>

The skill returns a magic link if the account is not `ACTIVE`. Open the link, finish the flow on the hosted page, then return to Hermes and retry.

The hosted page adapts to the connection. An OAuth connector asks for your consent. An API key connector, such as Snowflake, asks for the credential. Hermes never collects the credential in chat.

</details>

<details>
<summary>How do I switch users?</summary>

Set `SCALEKIT_IDENTIFIER` in `~/.hermes/.env` as the default.

Name another user in the prompt to override the default for one turn, for example `as identifier usr_8f3a2c`. Hermes then passes `--identifier usr_8f3a2c` to the skill, and Scalekit scopes the tools to that user's connected account.

</details>

<details>
<summary>Why do I see "connection not found"?</summary>

1. Confirm the connection exists in **Dashboard > AgentKit > Connections**
2. Confirm the connection is complete, not a draft
3. Confirm `SCALEKIT_ENVIRONMENT_URL` matches the environment that holds the connection

Use the connection `key_id` as the connection name, for example `github-connect`. Do not use the `conn_…` id.

</details>

<details>
<summary>The connected account is not ACTIVE</summary>

Check the state in **Dashboard > AgentKit > Connected accounts**:

| State | What you do |
|-------|-------------|
| `PENDING_AUTH` | Open the magic link and finish OAuth |
| `PENDING_VERIFICATION` | Complete [user verification](/agentkit/user-verification/) |
| `EXPIRED` | Open a new magic link. Scalekit cannot refresh a dead refresh token |
| `DISCONNECTED` | Reconnect from the Dashboard, then retry |

See [Manage connected accounts](/agentkit/connected-accounts/).

</details>

<details>
<summary>Can I use Virtual MCP with Hermes?</summary>

Yes. Pick the pattern that matches your host.

**The skill is the default.** `hermes-delegated-auth` calls `execute_tool` with Scalekit client credentials and an identifier. No token sits on the host, so nothing expires. Use the skill for a host that serves more than one user, and for cron.

**Virtual MCP is the scoped alternative.** A Virtual MCP server exposes only the tools you list. Tool scoping is the reason to choose it. Hermes reads the bearer from `config.yaml` as `${SCALEKIT_MCP_SESSION_TOKEN}`. Hermes resolves that variable when it connects, and again when you run `/reload-mcp`. For the host how-to, see [Run Virtual MCP on a Hermes host](#run-virtual-mcp-on-a-hermes-host).

Hermes never calls `create_session_token`. Nothing inside Hermes refreshes the bearer, so you refresh it yourself:

1. Create the Virtual MCP config once. Save the static `mcp_server_url`.
2. Mint a token with `create_session_token` for one identifier. Set `expiry` above your reload interval.
3. Write the token to the environment variable named in `config.yaml`.
4. Restart Hermes, or run `/reload-mcp`.
5. Repeat step 2 through step 4 before the token expires.

An expired bearer fails at the next tool call, not at startup.

One Hermes host is one operator. Do not mint a session token per end user inside the host. Mint per-user tokens in your own application instead, as in [Set up a Virtual MCP server](/agentkit/mcp/configure-mcp-server/).

Do not set `auth: oauth` on a Scalekit Virtual MCP server. The server takes a bearer token, not MCP OAuth.

</details>

<details>
<summary>Why does `hermes mcp login` fail against Scalekit?</summary>

`hermes mcp login` performs MCP OAuth against a vendor's MCP server. A Scalekit Virtual MCP server does not use MCP OAuth. It takes a static URL plus a session token that **your application** mints with `create_session_token`.

Set the bearer header in `config.yaml` instead. See [Set up a Virtual MCP server](/agentkit/mcp/configure-mcp-server/).

</details>

---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
