Background
Multiple agents (Claude Code, Codex, and local tools like
newt-agent) can all
benefit from the same set of enterprise MCP services (Confluence, Jira,
GitLab, PagerDuty, OneDrive, Outlook, etc.). These services are hosted by
NVIDIA's MaaS platform (nvaihub.nvidia.com/maas/…) and require ECI OAuth
bearer tokens that are managed per-session by whichever agent harness is
currently running.
The problem: each agent has to independently solve authentication.
Claude Code has its own OAuth flow. Codex has another. A local Rust agent
like newt has neither, so all 15+ MaaS servers fail with HTTP 401 at startup
(see newt-agent#303).
Proposed feature
Extend modulex-mcp to act as a meta-MCP proxy — a single stdio-based
MCP server that:
- Holds credentials for downstream HTTP MCP services (ECI tokens, OAuth
flows, static API keys, etc.)
- Proxies tool calls from any MCP client through to the real upstream
service, injecting auth headers transparently
- Presents a unified tool surface to the calling agent over stdio,
so clients never need to speak HTTP or manage tokens themselves
- Handles token lifecycle — TTL-aware refresh, OAuth PKCE flows,
env-var token injection — all hidden from the client
Why modulex-mcp?
modulex-mcp already runs as a stdio server that aggregates multiple
tool surfaces (core, store, board, routines). The brokering
architecture is already present. Adding a proxy layer that forwards to
external HTTP MCP endpoints is a natural extension of this pattern.
Desired interface
Config (e.g. modulex.toml)
[[proxy.upstream]]
name = "MaaS-Confluence"
url = "https://nvaihub.nvidia.com/maas/confluence/mcp/"
auth = { type = "bearer_env", env = "ECI_TOKEN" }
[[proxy.upstream]]
name = "MaaS-Jira"
url = "https://nvaihub.nvidia.com/maas/jira/mcp/"
auth = { type = "bearer_env", env = "ECI_TOKEN" }
[[proxy.upstream]]
name = "MaaS-Outlook"
url = "https://maas.prd.astra.nvidia.com/maas/outlook/mcp"
auth = { type = "oauth_pkce", client_id = "eci-prd-pub-9ab40d21-…", token_cache = "~/.modulex/outlook_token.json" }
Runtime behavior
- At startup, modulex-mcp reads the proxy config, connects to each upstream
(using the configured auth), and fetches the upstream tool list
- Tools from each upstream are namespaced
upstream__toolname and
advertised to the downstream stdio client
- On
tools/call, modulex-mcp forwards the call to the appropriate upstream
with full auth headers, and relays the response back
- If an upstream token expires mid-session, modulex-mcp silently refreshes
it (or prompts once via stderr) without any change to the client
Auth strategies to support
| Strategy |
How it works |
bearer_env |
Read $ENV_VAR at startup; inject as Authorization: Bearer |
bearer_file |
Read first non-empty line of a file; inject as bearer |
oauth_pkce |
Full browser-redirect PKCE flow; cache + refresh token |
static_header |
Arbitrary Key: Value header from config |
stdio-only transport
The downstream client (newt, or any other MCP consumer) speaks pure stdio
JSON-RPC to modulex-mcp. It never needs to:
- Know the upstream URLs
- Handle HTTP auth
- Manage token TTL or refresh
- Implement OAuth flows
This makes modulex-mcp an auth firewall: one place where credentials
are managed, one binary to configure, and every agent gets the same tool
surface for free.
Use cases
-
newt-agent: configure modulex as a stdio MCP server; newt gets all 15
MaaS tools through a single stdio connection with zero HTTP auth code in
the Rust codebase. Fixes newt-agent#303.
-
Codex: wire modulex-mcp as the single MCP server entry. All enterprise
tools work without Codex needing per-service auth.
-
Claude Code (sharing session): instead of each Claude Code session
re-authenticating to 15 services, a long-lived modulex-mcp daemon holds
refreshed tokens and all sessions use it. Token refresh happens once, not
once per agent session.
-
Any MCP client that can only do stdio: VSCode extensions, custom
agents, or CLI tools that support stdio MCP but not OAuth HTTP.
Implementation sketch
Phase 1 — bearer_env / bearer_file (simplest, highest immediate value):
- New
[proxy] config section in modulex.toml
- At init, spawn HTTP connections to each upstream, fetch tool lists
- Expose proxied tools in
tools/list response (namespaced)
- Route
tools/call → HTTP POST to upstream with injected header
- Log upstream errors clearly; skip failed upstreams at startup without
aborting the whole server
Phase 2 — OAuth PKCE:
- Browser-redirect flow for first auth
- Token serialized to
~/.modulex/<name>_token.json
- Background refresh goroutine / task
Phase 3 — daemon mode:
modulex-mcp --daemon runs as a persistent process
- Clients connect via Unix domain socket (still stdio framing)
- Single token store shared by all agent sessions on the machine
What this is NOT
- Not a security proxy / WAF — it trusts the credentials it holds
- Not a remote service — it runs locally, the agent connects to it via stdio
- Not trying to solve multi-user or server-side auth (that's MaaS's job)
References
- newt-agent#303 — the immediate pain point this fixes
- MCP spec: streamable-HTTP transport (2025-03-26)
- ECI / MaaS:
nvaihub.nvidia.com/maas/
- MaaS-Outlook PKCE config:
client_id = "eci-prd-pub-9ab40d21-b129-4075-8e82-842df4cb5045"
Background
Multiple agents (Claude Code, Codex, and local tools like
newt-agent) can all
benefit from the same set of enterprise MCP services (Confluence, Jira,
GitLab, PagerDuty, OneDrive, Outlook, etc.). These services are hosted by
NVIDIA's MaaS platform (
nvaihub.nvidia.com/maas/…) and require ECI OAuthbearer tokens that are managed per-session by whichever agent harness is
currently running.
The problem: each agent has to independently solve authentication.
Claude Code has its own OAuth flow. Codex has another. A local Rust agent
like
newthas neither, so all 15+ MaaS servers fail with HTTP 401 at startup(see newt-agent#303).
Proposed feature
Extend
modulex-mcpto act as a meta-MCP proxy — a single stdio-basedMCP server that:
flows, static API keys, etc.)
service, injecting auth headers transparently
so clients never need to speak HTTP or manage tokens themselves
env-var token injection — all hidden from the client
Why modulex-mcp?
modulex-mcpalready runs as a stdio server that aggregates multipletool surfaces (
core,store,board,routines). The brokeringarchitecture is already present. Adding a proxy layer that forwards to
external HTTP MCP endpoints is a natural extension of this pattern.
Desired interface
Config (e.g.
modulex.toml)Runtime behavior
(using the configured auth), and fetches the upstream tool list
upstream__toolnameandadvertised to the downstream stdio client
tools/call, modulex-mcp forwards the call to the appropriate upstreamwith full auth headers, and relays the response back
it (or prompts once via stderr) without any change to the client
Auth strategies to support
bearer_env$ENV_VARat startup; inject asAuthorization: Bearerbearer_fileoauth_pkcestatic_headerKey: Valueheader from configstdio-only transport
The downstream client (newt, or any other MCP consumer) speaks pure stdio
JSON-RPC to
modulex-mcp. It never needs to:This makes
modulex-mcpan auth firewall: one place where credentialsare managed, one binary to configure, and every agent gets the same tool
surface for free.
Use cases
newt-agent: configure modulex as a stdio MCP server; newt gets all 15
MaaS tools through a single stdio connection with zero HTTP auth code in
the Rust codebase. Fixes newt-agent#303.
Codex: wire modulex-mcp as the single MCP server entry. All enterprise
tools work without Codex needing per-service auth.
Claude Code (sharing session): instead of each Claude Code session
re-authenticating to 15 services, a long-lived
modulex-mcpdaemon holdsrefreshed tokens and all sessions use it. Token refresh happens once, not
once per agent session.
Any MCP client that can only do stdio: VSCode extensions, custom
agents, or CLI tools that support stdio MCP but not OAuth HTTP.
Implementation sketch
Phase 1 — bearer_env / bearer_file (simplest, highest immediate value):
[proxy]config section inmodulex.tomltools/listresponse (namespaced)tools/call→ HTTP POST to upstream with injected headeraborting the whole server
Phase 2 — OAuth PKCE:
~/.modulex/<name>_token.jsonPhase 3 — daemon mode:
modulex-mcp --daemonruns as a persistent processWhat this is NOT
References
nvaihub.nvidia.com/maas/client_id = "eci-prd-pub-9ab40d21-b129-4075-8e82-842df4cb5045"