Connect Hermes to AgentKit
Connect a Hermes agent to GitHub, Gmail and Slack through Scalekit AgentKit: install the skill, set your credentials and make your first tool call as a user.
Hermes 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.
Prerequisites
Section titled “Prerequisites”- Hermes Agent installed
- A Scalekit account with AgentKit enabled: sign up at app.scalekit.com
- At least one connection in AgentKit > Connections. Create the connection before you ask Hermes to use the app. See Set up a connection.
uvon yourPATH
How the skill works
Section titled “How the skill works”The skill runs the following loop when you name an app:
- The skill looks up the Scalekit connection you already created for GitHub, Gmail, or another app.
- The skill checks the user’s connected account.
ACTIVEmeans auth is complete and Scalekit holds a token. - The skill returns an authorization link when the connected account is not
ACTIVE. - The skill fetches the tool schema, calls the tool, and returns the result.
- The skill calls the Scalekit HTTP proxy when the connector has no named tool.
An authorization link is a one-time URL. The hosted page shows OAuth consent, or an API-key form.
Get started
Section titled “Get started”Using a coding agent?
-
Install the skill
Section titled “Install the skill”Install
hermes-delegated-authfrom authstack:Terminal window hermes skills install scalekit-inc/authstack/kits/agentkit/host/hermes-delegated-authConfirm the skill is enabled:
Terminal window hermes skills list│ hermes-delegated-auth │ │ local │ local │ enabled │Install the Python dependencies:
Terminal window cd "${HERMES_HOME:-$HOME/.hermes}/skills/hermes-delegated-auth"uv syncNew chats load
/hermes-delegated-auth. Run/resetin a chat that is already open.HERMES_HOMEchanges the skill path. The commands on this page use~/.hermes. -
Configure credentials
Section titled “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.~/.hermes/.env SCALEKIT_CLIENT_ID=skc_your_client_id# Keep this secret: anyone who has it can call tools as any ACTIVE identifier.SCALEKIT_CLIENT_SECRET=your_client_secretSCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.devSCALEKIT_IDENTIFIER=usr_8f3a2cParameter Description SCALEKIT_CLIENT_IDYour Scalekit client ID Required SCALEKIT_CLIENT_SECRETYour Scalekit client secret Required SCALEKIT_ENVIRONMENT_URLYour Scalekit environment URL Required SCALEKIT_IDENTIFIERDefault user the host acts as Required Copy the client ID, client secret, and environment URL from Dashboard > Developers > Settings > API Credentials.
SCALEKIT_IDENTIFIERis not a dashboard credential. Pick an opaque id that only your system knows, for exampleusr_8f3a2c. Everytool_exec.pycommand needsSCALEKIT_IDENTIFIER, including--list-connections. -
Ask Hermes to act
Section titled “Ask Hermes to act”Start a chat and ask for a real action. Hermes acts as
SCALEKIT_IDENTIFIERby default, and Hermes passes--identifierto the skill when you name another user in the prompt.You: Who am I on GitHub?Hermes loads the skill and runs these steps:
- Hermes looks up the GitHub connection.
- Hermes checks that the connected account is
ACTIVE, and returns an authorization link when the account is not. - Hermes fetches the tool schema.
- Hermes calls the tool and returns your GitHub login.
You: Show me my latest unread emailsHermes runs these steps:
- Hermes looks up the Gmail connection.
- Hermes returns an authorization link when you have not authorized Gmail yet.
- Hermes fetches the tool schema.
- Hermes returns the mail.
You: Read my Notion page https://notion.so/My-Page-abc123Hermes runs these steps:
- Hermes looks up the Notion connection.
- Hermes returns an authorization link when you have not authorized Notion yet.
- Hermes fetches the page tool schema.
- Hermes returns the page content.
You: As identifier usr_8f3a2c, list my unread Slack DMsHermes runs these steps:
- Hermes looks up the Slack connection.
- Hermes returns an authorization link when the Slack connected account is not
ACTIVE. - Hermes fetches the Slack tool schema.
- 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.
You: Create an out-of-office event tomorrow on the calendar for usr_8f3a2cHermes runs these steps:
- Hermes looks up the Google Calendar connection for
usr_8f3a2c. - Hermes returns an authorization link when the connected account is not
ACTIVE. - Hermes calls the create-event tool.
- Scalekit writes the event with the
usr_8f3a2ctoken.
Name the skill in the prompt to force the same path:
/hermes-delegated-auth who am I on GitHub?
Verify it works
Section titled “Verify it works”Confirm all of the following:
- The skill is listed (
hermes skills listor/skills) ashermes-delegated-auth - The connected account is
ACTIVEafter you finish the authorization link - Hermes returns data from the provider (the GitHub login, or unread mail)
Open the authorization link again and retry the same prompt if the account stays inactive.
Use Slack as a bot or as a user
Section titled “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. |
| Read private history, post, or act as a user | Scalekit Slack connection created with User scope. See the Slack connector. |
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
Section titled “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.
hermes cron create "0 9 * * *" "List unread emails for usr_8f3a2c and post a 5-line summary" --skill hermes-delegated-authOr in chat:
/cron add "every weekday at 9am" "List unread emails for usr_8f3a2c and post a 5-line summary" --skill hermes-delegated-authThe connected account must already be ACTIVE. Cron cannot click an authorization link.
Leave the gateway running on a headless host. A new authorization link can then land in Slack or Telegram. Do not put Gmail or Calendar refresh tokens in ~/.hermes/.env.
Choose how Hermes reaches Scalekit
Section titled “Choose how Hermes reaches Scalekit”Two 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 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 B.
| Question | A: delegated skill | B: Virtual MCP in your app |
|---|---|---|
| Who runs the agent | Hermes | your app |
| How many end users | many; switch with --identifier | many |
| Credential on the host | Scalekit client ID and secret | none |
| Anything expires? | no | yes, per run |
| Who mints a replacement | nobody; there is no token | your code, before each run |
| Tool scoping | the full connector catalog | only the tools in the server |
| Best for | multi-user hosts and cron | short agent runs |
The rest of this page covers Pattern A. For Pattern B, see Create a Virtual MCP server.
Avoid these mistakes
Section titled “Avoid these mistakes”- Do not put provider tokens in
~/.hermes/.env. Put only Scalekit credentials there. - Do not set
SCALEKIT_IDENTIFIERto 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.jsonis one laptop login, not a per-user identifier. - Do not run
hermes mcp loginagainst 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 rely on Hermes to sign in your end users. One gateway belongs to one operator, who holds the Scalekit credentials. That operator’s host acts for many end users, each with their own identifier, passed with
--identifier. Confirm who each user is in your own app with user verification, and mint per-end-user Virtual MCP session tokens in your application, not inside Hermes.
Common scenarios
Section titled “Common scenarios”How do I authorize a new connection?
The skill returns an authorization 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.
How do I switch users?
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.
Why do I see “connection not found”?
- Confirm the connection exists in AgentKit > Connections
- Confirm the connection is complete, not a draft
- Confirm
SCALEKIT_ENVIRONMENT_URLmatches the environment that holds the connection
Use the connection name, for example github-connect. Don’t use the conn_… id.
The connected account is not ACTIVE
Check the state in AgentKit > Connected accounts:
| State | What you do |
|---|---|
PENDING_AUTH | Open the authorization link and finish OAuth |
PENDING_VERIFICATION | Complete user verification |
EXPIRED | Open a new authorization link. The token expired, or the user revoked access at the provider |
DISCONNECTED | The account was disconnected in Scalekit. Open a new authorization link, then retry |
Can I use Virtual MCP with Hermes?
Use the skill on the Hermes host. hermes-delegated-auth calls execute_tool with Scalekit client credentials and an identifier. No token sits on the host, so nothing expires, and one host can serve many users and run cron jobs.
When you want each run limited to a chosen set of tools, mint Virtual MCP session tokens in your own application and pass them to the agent, as in Create a Virtual MCP server. Mint a token per end user in your application, not inside Hermes.
Do not set auth: oauth on a Scalekit Virtual MCP server. The server takes a bearer token, not MCP OAuth.
Why does hermes mcp login fail against Scalekit?
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 Create a Virtual MCP server.