Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 by bussyjd · Pull Request #657 · ObolNetwork/obol-stack · GitHub
Skip to content

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19 - #657

Draft
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default
Draft

feat: ship pay_mcp hermes plugin by default (CopyPlugins) + bump hermes v2026.6.19#657
bussyjd wants to merge 2 commits into
mainfrom
feat/pay-mcp-embed-default

Conversation

@bussyjd

@bussyjdbussyjd commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Ship the pay_mcp hermes plugin by default

This makes obol-stack embed and seed the pay_mcp hermes plugin onto every
agent — the same way embedded skills are seeded — so an agent can settle paid
(x402) MCP tools out of the box, with no image rebuild and the user's profile
left untouched. obol pins the upstreamnousresearch/hermes-agent image and
doesn't build it, so the plugin can't ride in via the image; we vendor it into
the binary and drop it into the agent's user-plugins dir on stack-up / reconcile.

The branch also folds in the nousresearch/hermes-agentv2026.6.5 → v2026.6.19
image bump (the image these agents run).

What pay_mcp does

A paid MCP server answers a tool call with an in-band x402 402 (the
amount/asset/payTo travels in _meta). The plugin detects it, signs an
EIP-3009 exact USDC voucher through the pod's remote-signer (no key
in-process), retries with the voucher in _meta["x402/payment"], and returns
the paid result — so a 402 never surfaces to the agent. Settlement requires an
in-chat confirm by default; an opt-in unattended policy (per-call + session caps

  • asset/recipient allowlists) lets autonomous turns settle.

How it integrates now

 obol stack up / agent reconcile
│ agent image (pinned, UPSTREAM): nousresearch/hermes-agent:v2026.6.19
│ env already on the agent: REMOTE_SIGNER_URL=http://remote-signer:9000
│
│ MASTER agent (internal/hermes) SELL sub-agent (serviceoffercontroller
│ syncObolPlugins → CopyPlugins + agentcrd) SeedHostPlugins → CopyPlugins
│ generateConfig: plugins.enabled renderHermesConfig: plugins.enabled
│ │ │ (signer only if wallet-bearing)
├─ CopySkills ──► /data/.hermes/obol-skills ✓ exists already
└─ CopyPlugins ──► /data/.hermes/plugins/pay_mcp/ ◀── NEW
│
agent boots → PluginManager scans ~/.hermes/plugins
│ pay_mcp is source=user → gated by plugins.enabled
│ config seeds plugins.enabled:[pay_mcp] → LOADS
│ register() sees REMOTE_SIGNER_URL → builds rails
│ stock image (no core seam) → recovery.install()
▼
every MCP tool call now auto-pays (confirm default; unattended opt-in)

The plugin stays inert unless a signer is configured, so seeding it
everywhere is safe; sell sub-agents only get REMOTE_SIGNER_URL when
wallet-bearing.

Two non-obvious things this had to get right

  1. User-seeded plugins are opt-in.kind: backend only auto-loads when the
    plugin is bundled in the image; a plugin seeded into ~/.hermes/plugins/
    is source=user and must be named in plugins.enabled. So seeding the
    files isn't enough — the generated config also writes
    plugins.enabled: [pay_mcp] on both agent paths.
  2. Relative imports. Hermes loads a user-dir plugin under the synthetic
    package name hermes_plugins.pay_mcp, and a stock image has no bundled
    plugins.pay_mcp to satisfy an absolute self-import. The plugin's
    intra-package imports were converted to relative (from . import x402) so it
    loads identically whether bundled or user-seeded. (Upstream fix:
    bussyjd/hermes-agent@feat/pay-mcp-plugin; locked by
    tests/plugins/test_pay_mcp_userdir_load.py.)

Changes

  • internal/embedCopyPlugins + GetEmbeddedPluginNames (mirror
    CopySkills); vendored plugin under internal/embed/plugins/pay_mcp/
    (VENDORED.md records provenance + the relative-import invariant).
  • internal/hermes (master agent) — syncObolPlugins seeds
    $HERMES_HOME/plugins; generateConfig adds plugins.enabled: [pay_mcp].
  • internal/serviceoffercontroller + internal/agentcrd (sell sub-agents) —
    renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
  • Image pin v2026.6.5 → v2026.6.19.

Test proofs

obol-stack (Go) — all green:

internal/embed TestCopyPlugins_SeedsPayMCP / _NoPycache /
_RelativeImportsOnly / _ManifestNamesPayMCP /
_PreservesUserPlugins ; TestGetEmbeddedPluginNames
internal/hermes TestSyncObolPlugins_SeedsPayMCP ;
TestGenerateConfig_EnablesPayMCPPlugin
internal/serviceoffercontroller TestRenderHermesConfig_EnablesPayMCPPlugin
internal/agentcrd TestSeedHostPlugins_SeedsAndPreserves ;
TestSeedHostFiles_FreshAgent (now asserts pay_mcp)

hermes-agent (Python) — inject-by-default integration, GREEN:
scripts/validate_pay_mcp_injection.py drives the realPluginManager
against an obol-seeded agent home (CopyPlugins layout + plugins.enabled) while
simulating a stock image (empty bundled dir + blocked core seam):

[found] source=user kind=backend enabled=True error=None
[wire] self-wired: ClientSession.call_tool wrapped, rails=['obol']
[PASS] obol-seeded layout -> PluginManager discovered + ENABLED + LOADED pay_mcp,
built rails, and self-wired ClientSession.call_tool on a stock image.

On-chain settlement (spark1, Base-Sepolia USDC) — 3 prior smokes GREEN:
crypto 0xba99dd88…, autonomous 0xb22f8d84…, self-wire 0xc5a8835f…
(each Bob→Alice 1000 atomic USDC, balances verified exact). The runtime
settlement is already proven on-chain; this PR adds the seeding + enablement.

Remaining

A live spark1 master-agent redeploy with the rebuilt obol binary is the one
optional end-to-end confirmation not run here; every code path is covered by the
Go tests + the real-PluginManager integration validation + the prior on-chain
smokes.

Latest hermes-agent release (2026-06-19; image verified published on Docker Hub).
Bumps both pinned constants (agent_render.go defaultHermesImage, hermes.go
defaultImage). Per the agent-contract integration test header, re-run
flow-16-sell-agent.sh + internal/agentcrd contract test (bundled-skills-empty +
marker invariants) before merging.
Embed the pay_mcp plugin in the obol-stack binary and seed it onto every
agent's user-plugins dir, mirroring how embedded skills are seeded — so an
agent can settle paid (x402) MCP tools out of the box. obol pins the upstream
hermes image and can't ride the plugin in via the image, so we vendor + seed.
- internal/embed: CopyPlugins + GetEmbeddedPluginNames (mirror CopySkills),
embed internal/embed/plugins/pay_mcp (vendored, relative-imports invariant).
- internal/hermes (master agent): syncObolPlugins seeds $HERMES_HOME/plugins;
generateConfig sets plugins.enabled: [pay_mcp] (user plugins are opt-in).
- internal/serviceoffercontroller + internal/agentcrd (sell sub-agents):
renderHermesConfig enables pay_mcp; SeedHostPlugins seeds it on the host PVC.
Inert unless the pod has REMOTE_SIGNER_URL (wallet-bearing agents only).
- Tests: CopyPlugins (seed, no __pycache__, relative-imports-only, manifest
name, preserve user plugins), config-enables-pay_mcp on both agent paths,
SeedHostPlugins seed+preserve.
Plugin stays inert without a signer, so it's safe to ship everywhere; users
remain free to customize their own profile/plugins.
@bussyjd
bussyjdforce-pushed the feat/pay-mcp-embed-default branch from 3567648 to 7604d76CompareJune 21, 2026 07:46
@bussyjd
bussyjd marked this pull request as draft June 21, 2026 13:29
@OisinKyne

Copy link
Copy Markdown
Contributor

Needs to be checked against the new MCP spec, and we need to come up with some candidate use cases before merging. I still don't think people will pay for MCP or tool calls versus just handing this complexity in their coding output. Would re-evaluate if i saw an example in the wild or was given a use case where its the obvious choice. https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne