Announcing CIMD support for MCP Client registration
Learn more

Connecting Hermes Agent to Your Tools and APIs

Srinivas Karre
Founding Engineer

TL;DR

  • Hermes Agent already knows how to talk to MCP servers, but you still have to figure out where credentials are stored, how they refresh, and what happens when you're juggling multiple tools or users.
  • Scalekit AgentKit fixes that by serving as a gateway in front of Hermes, so you authorize a connector once instead of managing credentials yourself.
  • Behind that gateway are three SDK calls, get_or_create_connected_account, create_config, and create_session_token, that you'll run yourself in the walkthrough below.
  • Once it's set up, Hermes gets a single mcp_servers entry with a session token in it that expires in 8 hours by default, with no raw GitHub token or PAT anywhere in sight.
  • Everything you need is in hermes-github-gateway-agent, a small project you can clone and run against your own GitHub connection.

Most people get Hermes running, have a good first conversation with it, and then hit the same wall: talking is easy, doing is not. The moment you want Hermes to actually touch GitHub, Slack, or an internal API, you're deciding where a credential lives and how it gets refreshed — decisions Hermes's own MCP client was never built to make for you. This post covers what that gap actually looks like, why a gateway fixes it better than hand-rolling auth per tool, and then builds the real thing: a working Scalekit AgentKit gateway in front of Hermes, wired to a live GitHub connection, tested and run end to end rather than described in the abstract.

This walkthrough is scoped to one person running their own Hermes host, which is what the pattern below is actually built for. If you're wiring Hermes up as a shared bot for a whole team instead, Scalekit's docs point to a different, simpler pattern for that case: a Hermes skill called hermes-delegated-auth, covered in the Multi-User Access Control for AI Agents on Hermes.

Why Coding Agents Can Reason Well but Still Struggle to Act

Coding agents have gotten good at reasoning. Ask one to plan a refactor, debug a stack trace, or draft a migration script, and most modern agents, including Hermes Agent, handle it well. The harder problem has always been the next step: getting that reasoning to touch the real world. An agent that can plan a GitHub issue triage is only useful once it can actually read the issue, apply a label, and post a comment.

Hermes bridges that gap with a built-in Model Context Protocol client, which lets it call tools exposed by any MCP server: a GitHub connector, a Slack connector, an internal API someone on the team wrapped last month. That client is real and well-built. It is not, on its own, a complete answer to "give Hermes safe access to my tools." Auth, credential storage, and support for multiple tools or users are left to the developer to solve.

The naive path
The gateway path
~/.hermes/config.yaml holds
GITHUB_TOKEN: ghp_xxxxxxxxxxxx in plaintext (a classic PAT; fine-grained PATs, prefixed github_pat_, can be given a mandatory expiry when scoped to an organization, though personal-use fine-grained PATs can still be created with no expiration)
Authorization: Bearer
Lifespan
Never expires unless you set one and rotate it by hand
Expires on a TTL, re-minted by re-running one script
Per tool
One more raw secret to store and rotate
No change to this file at all

What does Hermes's MCP client already solve?

Hermes's MCP client fully covers the protocol layer. It's worth listing this out in full, because the gap in the next section only makes sense once it's clear how much Hermes already does correctly:

  • Connects over both MCP transport types — stdio for local subprocess servers, HTTP for remote ones, configured under mcp_servers: in ~/.hermes/config.yaml.
  • Speaks MCP's OAuth 2.1-aligned authorization flow with PKCE. Set auth: oauth plus an oauth: block, and Hermes runs the flow and stores the resulting tokens as plaintext JSON at ~/.hermes/mcp-tokens/<server>.json. This flow is for a server's own vendor OAuth, not for Scalekit's Virtual MCP, which uses a static bearer token instead.
  • Filters which tools a server can expose — tools.include and tools.exclude (globs supported) narrow a forty-tool server down to the three Hermes actually needs.
  • Reloads and verifies connections without a restart — /reload-mcp picks up config changes live, and hermes mcp test <server> confirms a server is reachable before you trust it in a real session.

Every one of these is protocol-level plumbing, and Hermes gets it right. What it doesn't cover is what happens once a real credential needs to sit behind that plumbing, which is the subject of the next section.

Hermes handles
Left to you, once there's more than one tool
Native MCP client (stdio + HTTP)
Where the credential lives
OAuth 2.1 with PKCE
Token refresh for hand-set headers
Per-server tool filtering
A new secret per new tool
Config reload + test command
Multiple users or identities

Where a Simple Hermes Setup Starts to Break Down

Four problems show up in order as a Hermes setup grows past a personal weekend project:

  • Credential storage: A bearer token pasted into a headers: block sits in cleartext YAML on whatever machine runs Hermes, readable by anything with file access on that box. That's fine until a second person touches the same machine, the config file ends up in a backup, or a screen share includes it.
  • Refresh: Hermes's own OAuth flow automatically refreshes tokens for servers it manages end-to-end, but a raw Authorization: Bearer header typed in by hand does not. If that hardcoded token expires, Hermes has no way to know it's stale until a call fails.
  • Scaling past one integration: Every new tool means a new mcp_servers entry with its own credential to store and its own refresh story to solve. Five tools means five separate places a secret can leak or go stale, not one.
  • Multiple users: Hermes's config is local and single-identity, with no concept inside it of "this session is Alice" versus "this session is Bob." Anyone who wants Hermes to act on behalf of different people needs an identity layer that Hermes was never built to provide.

None of this is a design flaw in Hermes. An agent client isn't supposed to double as a credential vault or an identity provider, any more than a web browser is supposed to double as a password manager. In practice, that means the moment a Hermes setup goes beyond a single tool for a single person, something needs to sit in front of it and own auth, rather than living inside its config file.

Why build a gateway with Scalekit AgentKit?

The fix isn't hardening each mcp_servers entry one by one. It's collapsing all tool integrations into a single gateway that Hermes talks to, with actual credential handling happening behind that gateway rather than inside Hermes's config.

Scalekit AgentKit's Python SDK exposes exactly this as a first-class primitive: client.actions.mcp. Building this gateway doesn't require standing up a custom MCP server from scratch. Three SDK calls provision the whole thing:

  • actions.get_or_create_connected_account() creates the per-user OAuth link to GitHub, or any other connector. A human approves access once, and Scalekit's token vault refreshes it from then on.
  • actions.mcp.create_config() maps that connection to a specific, narrow list of tool names. This step turns the gateway into an actual gateway rather than a pass-through: Hermes only sees the tools named here, not anything else the connector exposes.
  • actions.mcp.create_session_token() mints a bearer token scoped to that config and this identifier, expiring in SESSION_TOKEN_TTL_HOURS hours (8 by default). This token, not a raw GitHub credential, goes into Hermes's config file.

Run those three calls, and Hermes ends up with exactly one new mcp_servers entry, pointed at the config's own URL — that's already authenticated and scoped. The next section builds this end-to-end against a real GitHub connection.

How Do You Connect Hermes to the Gateway?

This section builds the gateway against a real GitHub connection using hermes-github-gateway-agent, the code backing this post.

Install and Configure the Gateway Project

To start, clone the repo and install its dependencies:

git clone https://github.com/scalekit-developers/workflow-agents-demos.git cd workflow-agents-demos/hermes-github-gateway-agent python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt pip install hermes-agent

Create a .env from .env.example:

cp .env.example .env # fill in SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, SCALEKIT_CLIENT_SECRET # and SCALEKIT_GITHUB_CONNECTION (the connection name from your dashboard)

The four Scalekit values in .env come from your Scalekit dashboard: the environment URL and client credentials from API Config, and the connection name from wherever you set up GitHub under Connections.

requirements.txt pins mcp==1.29.1 deliberately. Installing hermes-agent alongside scalekit-sdk-python can otherwise pull in a newer mcp release that Hermes's own HTTP transport doesn't yet support, so installing in the order shown keeps the pinned version in place.

The Connection created above (AgentKit → Connections) is the only manual step in the dashboard. The gateway itself, what Scalekit calls a Virtual MCP Server, gets created by setup_gateway.py in the next step, through a direct call to client.actions.mcp.create_config() in the Scalekit Python SDK, the same underlying API the dashboard's "Add Virtual Server" button calls. You don't need to click that button manually; running the script the first time creates the server object in your account.

How do you run the provisioning script end to end?

With .env filled in, one command runs all three provisioning steps:

python setup_gateway.py

On a first run, the connected account won't be authorized yet, so the script prints a link and pauses:

[1/3] Checking connected account... status = PENDING_AUTH Open this link and approve access: https://your-env.scalekit.com/magicLink/... Press Enter once you've authorized access...

How do you authorize GitHub access the first time?

Open the printed link, approve access on GitHub, and press Enter again in the terminal. This is the only OAuth click a human ever makes in this flow; Scalekit's token vault handles all subsequent refreshes without asking again. The script re-checks the connected account and, once it reads ACTIVE, continues through the remaining three steps automatically:

✓ connected account is ACTIVE [2/3] Ensuring MCP config... config_id = cfg_141981117258400514 mcp_server_url = https://hey.scalekit.dev/mcp/v3/servers/b09a59c1-...

That config_id isn't just a log line; it's a real object you can see in your own Scalekit dashboard, under AgentKit → Virtual MCP Servers, and it's the same one your session token will be minted against:

The script continues through the final step on its own:

[3/3] Minting session token for Hermes... expires_at = 2026-09-12 13:49:18.128461

Now that authorization is complete, the Hermes config has been written to hermes_mcp_config.snippet.yaml.

hey.scalekit.dev is this account's own sandbox environment, not a shared or generic Scalekit domain. Signing up gives you your own environment URL (SCALEKIT_ENVIRONMENT_URL in .env), and the server URL you see will be under that domain instead.

Your own config_id, mcp_server_url, and expires_at will be different values, specific to your Scalekit environment and this run.

How do you point Hermes's config at the gateway?

The setup script writes a ready-to-use config block to hermes_mcp_config.snippet.yaml. Paste it into ~/.hermes/config.yaml:

mcp_servers: github_gateway: url: "https://your-env.scalekit.com/mcp/v3/servers/" headers: Authorization: "Bearer " tools: include: [github_search_issues, github_issue_labels_add, github_issue_get] prompts: false resources: false

Notice what's absent from this file: no personal access token, nothing to rotate by hand. The token here is a Scalekit session token with a TTL. When it expires, re-running setup_gateway.py mints a fresh one against the same connected account and config, without asking for authorization again. The URL itself never changes between runs; it's the config's own static address, so only the token half of this block needs replacing.

Test the MCP Connection

hermes mcp test reads config.yaml directly, so it works standalone without a chat session open:

hermes mcp test github_gateway

A successful connection looks like this:

Testing 'github_gateway' ... Transport: HTTP → https://hey.scalekit.dev/mcp/v3/servers/b09a59c1-... Authorization: Bear***1E6Q ✓ Connected (4806ms) ✓ Tools discovered: 3 github_issue_get Get a single issue in a repository by its number ... github_issue_labels_add Add labels to an issue, appending to any existing... github_search_issues Search for issues and pull requests across GitHub...

If this fails instead with mcp.client.streamable_http is not available, the installed mcp package is newer than the version supported by PyPI's current hermes-agent release.

That bug is already fixed upstream, but it isn't in a new PyPI release yet; installing Hermes with npm install hermes-agent instead of pip install hermes-agent sidesteps it entirely. Otherwise, reinstall the pinned version and try again:

pip install "mcp==1.29.1"

Run Hermes Against the Gateway

With the gateway confirmed, start an actual chat session:

hermes chat

hermes mcp test and hermes chat both read config.yaml fresh on startup, so a session started after the file above was already in place picks up github_gateway automatically. If a chat session was already running before the config was written, reload it from inside that session instead of restarting:

/reload-mcp

What a Real Request Looks Like

Running the exchange below also needs an LLM provider configured for Hermes (hermes model), separate from the Scalekit gateway itself.

With the gateway connected, Hermes can act on a plain-language instruction rather than a hand-written API call:

You: Search open issues on scalekit-inc/scalekit-sdk-node for anything that looks stale or unlabeled, and label it accordingly.

Hermes calls github_search_issues first, reads the response, and decides which issue, if any, qualifies. If one does, it calls github_issue_labels_add on its own, without a second prompt. The credential authenticating that second call is the session token minted during setup, issued once to a connected account a human approved, and never present in Hermes's config as anything other than a token with an expiry date.

Before adding a new tool name to MCP_TOOLS, confirm it actually exists on the connector by calling client.tools.list_tools(filter=Filter(provider="GITHUB")) (swap the provider for whichever connector is in use; Filter comes from scalekit.v1.tools.tools_pb2). Tool catalogs vary by connector and by account, so a name that works elsewhere isn't guaranteed to exist here.

If you change sk_gateway.py (a different connector, a different tool list, extra provisioning logic), run the test suite before pointing Hermes at the result:

pytest test_gateway.py -v

It checks the connected account, config, and session token calls against the installed SDK models and the generated Hermes YAML, so any customization mistake shows up here rather than in a failed hermes mcp test.

What other jobs can this same gateway cover?

GitHub issue triage is a small, clear example, chosen on purpose so the mechanics stay visible. The same gateway pattern covers most jobs a team hands to an agent. For example, the same pattern powers a daily standup agent pulling from GitHub, GitLab, Jira, and Slack — only the connector names and tool lists change.

Use case
What Hermes does
Connectors involved
Issue triage
Reads new issues, applies labels, pings the right owner
GitHub
On-call support bot
Reads recent messages in a support channel, drafts a reply, posts it back
Slack
Release notes assembly
Pulls merged PRs since the last tag, drafts a changelog, creates a page
GitHub, Notion
Customer research assistant
Searches an org's repos for issues mentioning a customer, summarizes patterns
GitHub
Personal ops assistant
Builds a daily standup summary from a solo developer's own repos and calendar
GitHub, Calendar

Every row in that table swaps the connector name and the tool list in .env. The provisioning flow in sk_gateway.py and the auth model behind it remain exactly the same across all five.

What changes when this is for a team, not just you?

A single-developer setup like the one above skips several things a platform or security team asks about before an agent gets production access. They're worth naming directly, because the fix for each one is a configuration choice, not new code, as long as the gateway is still one Hermes host run by one operator. A host that's meant to stand behind a shared team bot needs the pattern in Multi-User Access Control for AI Agents on Hermes instead, not more identifiers layered onto this one:

  • Per-user auth, not a shared credential. Every connected account is created against an identifier, a single constant for local use in this walkthrough. In a real deployment, that identifier should be an opaque, unguessable value rather than an email, since anyone holding the Scalekit client secret can call tools as any ACTIVE identifier; a guessable one widens what a leaked secret can do, rather than limiting it to a single bot account with standing access to everyone's data.
  • Revocation from one place. Because the only credential Hermes ever sees is a Scalekit session token, offboarding someone or rotating a compromised secret happens once in the Scalekit dashboard, rather than hunting down every machine that ever had a copy of a raw token. This directly mirrors how revoking an employee's AI agent access should work at the organizational level.
  • Scoped tool access by design. tools.include in create_config() is the actual blast-radius control. A team can grant Hermes github_search_issues without github_issue_labels_add, keeping it read-only, and Scalekit enforces that boundary rather than trusting that nobody edits a YAML file later.
  • A central audit trail. Every call routed through the gateway passes through Scalekit's action layer, giving security a single point to review what an agent did on a user's behalf, rather than reconstructing activity from GitHub's logs after something goes wrong. Well-structured audit trails for agent auth are essential for enterprise governance.
  • Token lifetime as a policy, not a guess. SESSION_TOKEN_TTL_HOURS is a config value, not something touching Hermes or the connector code. The real tradeoff: a short TTL (an hour) limits how long a leaked token stays valid, at the cost of a mid-session break every hour if Hermes is left running; a longer TTL (a full work day) means fewer interruptions, at the cost of a longer window before a compromised token stops working.

Because there's no automatic re-minting during an active session (the token here is a static header, not something Hermes's own OAuth flow manages), pick the number based on how long a session realistically runs uninterrupted, and pair it with a remint step (re-running this script, or a cron job calling create_session_token followed by /reload-mcp) if the gateway needs to stay live longer than one TTL window.

None of this requires code different from what's already in this repo. It's the same three calls, run with a real per-user identifier instead of a placeholder, reviewed once by whoever owns the access policy. For a deeper look at secure token management for AI agents at scale, the patterns discussed there map directly onto what the gateway is doing here.

What This Changes for Your Hermes Setup

Hermes Agent's MCP client was never the missing piece; it already speaks the protocol correctly. What was missing was everything sitting behind that protocol: where a credential lives, whether it refreshes itself, how far a setup scales past one tool, and whether it can represent more than one person.

A Scalekit AgentKit gateway answers all four with three SDK calls and one line in config.yaml, turning "paste a token into YAML" into "authorize once, then let the vault handle it."

The GitHub example here is deliberately small. The same three calls — get_or_create_connected_account, create_config, create_session_token — work identically for Slack, Notion, or any other connector, and for more than one identifier against the same config. That's the shape this series builds on next: a custom tool behind the same gateway, then the same underlying mechanism serving a whole team, and where Scalekit's own hermes-delegated-auth skill fits once one Hermes host has to stand behind many people instead of one. This pattern of access control for multi-tenant AI agents becomes the foundation as the setup grows.

FAQs

Does Hermes Agent support MCP natively, or do I need a plugin?

Natively, Hermes ships with an MCP client that handles both stdio and HTTP transport out of the box, configured under mcp_servers in ~/.hermes/config.yaml. No plugin is required to talk to an MCP server. What's missing is a credential and identity layer in front of that client, which is what this post adds.

Do I need to write my own MCP server to use this pattern?

No, Scalekit AgentKit's client.actions.mcp runs the MCP server for you. create_config() provisions a hosted MCP endpoint, scoped to your chosen tools, at one fixed URL; create_session_token() scopes access to that URL down to one identifier. No MCP server code needs to be written or deployed.

What happens when the session token expires?

Hermes sees a 401 with no automatic retry, since this token is a static header, not something its own OAuth flow manages. Re-run python setup_gateway.py to mint a fresh one, then paste the new snippet in and run /reload-mcp. SESSION_TOKEN_TTL_HOURS in .env sets how often that's needed, 8 hours by default; a gateway that needs to stay live unattended longer can automate the same remint-and-reload step with a cron job.

Can this same pattern work with Slack, Notion, or another connector instead of GitHub?

Yes, swap SCALEKIT_GITHUB_CONNECTION for the equivalent connection name, and change MCP_TOOLS to the tool names that connector exposes. The three-call provisioning flow in sk_gateway.py stays the same; only the connector and tool list change.

Is this setup safe to run with multiple people sharing the same Hermes installation?

Yes, for a handful of people each authenticating with their own identifier, since every session token carries that identifier's own claims. What it doesn't solve is a long-running shared bot; someone still has to remint each person's token before it expires. For that case, Scalekit's hermes-delegated-auth skill mints no token at all, as covered in "Multi-User Access Control for AI Agents on Hermes".

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.