Credential Broker
io.github.SarutobiSasuke8由 GitHub 账号 SarutobiSasuke8 发布v0.1.1更新于 Nov 7, 2026
Local-first credential broker: agents use gated APIs through scoped, audited requests, never keys.
概览
本地 MCP 服务器,让 AI 代理通过受策略约束、可审计的请求调用受控 API,而 API 密钥始终保存在操作系统钥匙串中。
- 可以做什么
- 查看身份与授权:显示已认证的代理、其按凭据划分的操作授权,以及已授权的凭据(不含密钥值)。
- 预演请求:说明某个请求是否会被允许、由哪条规则决定,以及是否需要或已满足审批。
- 调用 API:通过已授权的凭据执行 GET 或 HEAD 读取,以及 POST、PUT、PATCH 或 DELETE 变更,密钥在最后一刻注入,响应会被脱敏。
- 申请密钥:可选地通过打开预填好的密钥管理器向操作者申请凭据,代理看不到页面和令牌。
- 适用场景
- 当代理需要调用需要认证的第三方 API,但不应持有密钥,并且你希望按操作授权、对写操作要求审批、保留不含内容的审计记录时使用。适合由你掌控策略文件和操作系统钥匙串的单机本地环境。
- 运行要求
- 通过 npx 以 stdio 在本地运行,需要 PATH 中有 Node 22 或 24。密钥存放在操作系统钥匙串(Windows 凭据管理器、macOS 登录钥匙串或 Linux 的 Secret Service),也可通过各凭据的 env_var 提供。必须设置 BROKER_AGENT_ID 并在策略文件中预先配置;策略、审计和审批文件由 BROKER_POLICY_FILE、BROKER_AUDIT_FILE 和 BROKER_APPROVALS_FILE 指定。
安装
在 SourceWeft 中
- 打开 控制台中的 Credential Broker,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
Credential Broker MCP
A local-first MCP server that lets AI agents use gated APIs without ever holding the keys.
The agent asks the broker to make a request. The broker canonicalises the URL, checks a declarative per-operation policy, verifies any required approval at execution time, injects the secret at the last moment, calls the upstream, redacts and filters the response, and writes a content-free audit record. The secret lives with the broker process and is never relayed to the agent in any representation the broker creates: raw, Base64, base64url, percent-encoded, form-encoded, or Unicode-normalised. An upstream that transforms a secret with a one-way function of its own defeats string matching; scoped operations and metadata-only responses are the defence there, and SECURITY.md states this boundary honestly.
Early release (0.x). Published to npm with provenance from GitHub Actions. Expect the policy format and tools to change before 1.0.
Five-minute quickstart
Needs Node 22 or 24. Keys are stored in the OS keychain: Windows Credential
Manager, the macOS login keychain, or the Secret Service on Linux
(GNOME Keyring or KWallet through secret-tool). Without one, set each
credential's env_var instead.
-
Open the key manager. It prints a local link and opens it in your browser.
Run through
npx, the policy, audit log and approvals live in~/.credential-broker(%USERPROFILE%\.credential-brokeron Windows), so they survive npm cache clean-ups and upgrades. SetBROKER_HOME, orBROKER_POLICY_FILE,BROKER_AUDIT_FILEandBROKER_APPROVALS_FILE, to put them somewhere else. From a clone of this repo, runnpm cithennpm run keys; the files then stay in the checkout. -
Add one key. Open New key. Use the short name
github, base URLhttps://api.github.com, paste a token, tick the agent that may use it (or add an identity such ascodex), then press Add key. The key goes into your OS keychain and the policy file gets a read-only grant. Press Test on the card to prove the key works. -
Connect your MCP client. Go to Settings → Connect an MCP client, select Codex or Claude Code, then pick the agent. For Codex, paste the copied TOML into
~/.codex/config.tomlor update the existingcredential-brokerentry. For Claude Code, run the copied command. Restart your client. Both options use local stdio and require Node on PATH. The Codex configuration also applies to local sessions in the ChatGPT desktop app on the same host. If you use environment-backed credentials, list the variable names inenv_varsunder the Codex server table. See the Codex MCP configuration guide. -
Make a granted call and a denied call. In your client:
Use
broker_explain_requestfor a GET tohttps://api.github.com/repos/octocat/hello-worldwith credentialgithub.It answers allowed and names the deciding rule. Now ask the same with
DELETE: denied, no grant. Then ask forbroker_readon the GET and you get the response body with the token redacted wherever it might appear. The audit log (data/audit.jsonl) now holds the who, what and outcome, never the content. -
Pause and resume. Press Pause all access in the page header and type
PAUSE. The agent's next call is refused with a message not to retry. Press Resume all access and typeRESUME. From a terminal:npm run pause -- onandnpm run pause -- off.
ChatGPT web
The broker's native connection is local stdio. For a private ChatGPT integration, an eligible workspace can connect through OpenAI Secure MCP Tunnel, with the tunnel client launching the broker under a dedicated policy identity. Run the tunnel client under the OS account that owns the keys and keep the key manager on loopback. This connection needs its own workspace setup and verification. Public distribution as a local ChatGPT desktop plugin is limited to approved partners.
Why
Giving an agent an API key gives it everything the key can do, for as long as the key lives, invisibly. Most agent tasks need a fraction of that: read these repos, query this endpoint, GET but never DELETE. This broker turns "here is my key" into "here is a scoped, observable capability":
- Possession vs use. Agents exercise credentials; they never possess them.
- Deny-by-default. Authority is an enumerated list of named operations per credential. An absent grant is a denial, not an oversight. Deny rules always beat grants.
- Nothing widens sideways. A method, path, query parameter, body rule, or approval attached to one credential never widens any other credential.
- One decision function. The dry-run explainer and the live request
path call the same
evaluate(). What the broker reports and what it enforces cannot drift apart. - Content-free audit. Who reached what, when, with what outcome, and through which lifecycle state. Never what they saw.
Tools
Policy (version 2)
Policy objects reject unknown fields at every level. Correct misspelt keys
and remove unsupported metadata before upgrading; ignored fields could
otherwise remove a restriction, such as a misspelt enabled or deny.
One YAML file declares credentials and per-credential operation grants:
A request must clear every gate: identity provisioned and enabled,
credential granted, URL canonical and provably unambiguous, origin exactly
the credential's origin, path inside the credential's base path, no deny
match, and an operation whose method, path template, query parameter
allowlist, and body rule all match. broker_explain_request reports which
gate decided any outcome.
Semantics worth knowing:
- Operation paths are relative to the credential's base path. With
base_url: https://api.example.com/v2, the template/weatherauthorises/v2/weather. - Path matching is case-sensitive for operations and case-insensitive for deny rules, because many upstreams treat paths case-insensitively.
- Query parameters not named in
query_paramsare denied. - Operations without a
bodyrule reject any request body. Body rules name allowed content types and a byte cap. Mutations must declarerequires_approvalexplicitly, true or false. - Responses default to metadata only.
response_body: truerelays the body, and only for content types inresponse_content_types(defaultapplication/json). - Credentials of kind
querymust sethigh_risk: true: URLs are commonly retained by upstream and proxy telemetry, so a query-string secret has a wider blast radius than a header.
Approvals
An operation with requires_approval: true executes only when the request
carries an approval_id that exists in the approval store, is bound to
exactly this principal, credential, and operation, is unexpired, and is
not revoked, checked at execution time, not config-load time. On stdio the
store is a JSON file (config/approvals.json by default) that is re-read
on every check, so editing it revokes access mid-flight:
Migrating from version 1
Version 1 policies shared one method list and path set across every credential an agent held, so authority needed for one credential widened all of them. The broker refuses v1 files. Convert with:
The output is deliberately conservative (mutations require approval, query parameters must be enumerated) and flagged for review: it makes the old blast radius visible rather than silently preserving it.
Safety properties
- Every URL is canonicalised once; policy and the outbound request consume the same canonical value, and the outbound bytes are round-trip checked. Encoded separators, double encoding, control characters, backslashes, userinfo, non-ASCII path segments, repeated or trailing slashes, and trailing-dot segments are rejected, not guessed at.
- Secrets are injected into exactly one header or query parameter. One redactor covers response bodies, relayed headers, error chains, and audit records, across raw, Base64, base64url, percent-encoded, form-encoded, and NFC/NFD representations, including truncation boundaries.
- Raw upstream error text is never relayed to agents; they receive a stable error category and a trace id, and the redacted detail goes to the audit log.
- Redirects are refused: a redirect could point anywhere, and
fetchwould re-send the injected credential there. - A credential can only be exercised against its exact origin and base path; lookalike hosts and string-prefix tricks are refused.
- Plain-http upstreams are rejected at policy load, except on loopback.
- Response headers are allowlisted;
Set-Cookieand friends are dropped. - Responses are size-capped per operation, metadata-only by default.
- Mutations are a separate, honestly-annotated tool; approval state is
checked at execution time; a dispatch failure on a mutation is recorded
as
indeterminate, never silently retried. - Every decision is audited through its full lifecycle without request or response content, and audit records pass through the redactor too.
Identity boundary
On stdio, BROKER_AGENT_ID is a trusted-launcher assertion: the
operator starting the process vouches for which agent is connected. It is
only a real boundary when the broker runs under the secure-launch profile
in SECURITY.md: dedicated OS identity, owner-only
policy/approvals/audit files, a fixed identity wrapper the agent cannot
edit, and no agent-readable secret environment. The broker loads only its
principal's secrets and scrubs every policy credential variable from its
environment after loading. One broker process serves one principal; never
share a process between agents.
Managing keys
This opens a local page in your browser where you:
- Add an API: name, base URL, how the key is sent, paste the key, then choose individual agents or group shortcuts. The key goes into the OS keychain; the policy file gets persistent group assignments and separate read-only grants for individually selected agents (GET on any path under the base URL, only the query parameters you list).
- Save, replace, test, or remove a key for any API already in the policy. Test makes one GET with the key and reports the status code only. Keys are write-only: the page never shows one back.
- Import keys you already have. "Find my keys" looks in Windows user
environment variables, MCP client configs (Claude Code, Claude Desktop,
Cursor), and
.envfiles underDocumentsandC:\Dev(override withBROKER_IMPORT_ROOTS, semicolon-separated). Known providers (OpenRouter, Gemini, ElevenLabs, DigitalOcean, Nansen, TypeSafe, Clerk, CoinGecko, PixelLab, OpenAI, Anthropic, GitHub) can be added as APIs in one click and are tested straight away; anything else is stored only. Values are read by the key manager process and never sent to the page. Wallet keys are flagged and never offered as APIs. Original copies are not touched. - Add an agent (Agents page): id, display name, enabled, and which APIs it may read. A new agent starts with no access at all; only the APIs you tick become read grants. No key is involved.
- Copy the
claude mcp addcommand that registers the broker for an agent with absolute paths filled in (Settings page).
A key saved in the keychain that no API in the policy uses is listed on the Keys page under Not connected to an agent yet, with a Connect to an API action; no agent can use it until then.
The page is a small site: a sticky header with the page links (Overview,
Keys, Agents, Approvals, Activity, Import, Settings; a menu button on
narrow screens), search (/ or Ctrl+K), a New key button, the
pause control and a status chip. Activity is the content-free audit log,
with filters and CSV export. It follows your system's
light or dark theme, works with the keyboard alone, and is served entirely
by the key manager on loopback: no external script, style, font or image.
Write access (POST, PUT, PATCH, DELETE) is never granted from the page;
add those operations to the policy by hand. Restart the MCP client (or set BROKER_HOT_RELOAD=1, see below) after
changing a key, because the broker loads keys once at start-up.
Optional top-level groups: in broker.yaml maps a name to registered agent
IDs. all: ['*'] means every registered agent. The example policy shows
hermes, grokbot and all; replace their sample members with your own IDs.
Groups cannot be empty, nested or refer to unknown agents. Selecting a group
in the key manager writes a group_grants: entry for that credential. The
broker resolves it for the authenticated agent on every request. Editing
groups: changes access after the broker restarts and reloads its policy.
Membership alone grants nothing; a matching group_grants: entry is required.
Group and direct denials are combined, and group mutations must require
approval. The key cards show groups actually assigned to each credential.
Keys live in the OS keychain as credential-broker/<credential-id>: on
Windows in Control Panel > Credential Manager > Windows Credentials, on
macOS in the login keychain (Keychain Access shows them under that
service name), on Linux in the Secret Service (Seahorse or KWallet
Manager shows them with the credential-broker attribute). The macOS and
Linux backends drive the security and secret-tool commands and send
the key over stdin, never on a command line; they are unit tested against
a fake and still need a real round trip on each platform before you rely
on them. An env_var value, if set, overrides
the stored key. The page listens on 127.0.0.1 only, needs a per-run token
from the printed link, and closes after 30 minutes idle. On other
platforms, set each credential's env_var instead.
Expiry dates and notes
Each key can carry an expiry date and a free-text note (where it was created,
how to rotate it). They are kept in key-notes.json beside the policy file
(override with BROKER_KEY_NOTES_FILE), never in the policy and never with the
secret. Keys that are expired or expire within 30 days raise a banner, sort
first, and show a badge on their card. Nothing in the broker engine reads this
file, so a date never blocks a request; it is a reminder for you.
Letting an agent ask for a key
Set BROKER_ALLOW_KEY_REQUESTS=1 on a broker process to give that agent the
broker_request_credential tool. The agent supplies a lowercase name, a short
reason and, optionally, a known provider id (for example github, openai,
itch). The broker then opens the key manager in your browser, pre-filled with
the name and the reason, with a banner saying which agent asked.
- The agent never receives the page address, the session token or anything you type. The tool result says only that a window opened.
- The base URL and key style come from the broker's own table of known providers, never from the agent. An unknown provider leaves them blank for you to fill in, so an agent cannot steer a key to a host of its choosing.
- Nothing is added until you press the button, and you choose who may use it.
- Requests are limited to one per credential per 10 minutes and five an hour per process, and each one writes a content-free audit line.
- The broker loads keys when it starts, so restart the MCP client after saving. The tool is off by default.
Panic button
The header of the key manager has a red Pause all access button. It opens a
dialog that says what will happen, shows how many agents and keys are affected,
and asks you to type PAUSE. From then on every broker refuses every request on
its next call. Nothing is deleted: policy, keys and approvals stay as they were.
Resume all access asks you to type RESUME, because resuming is the risky
direction. Pausing one agent (Agents panel) or one key (its card) asks a plain
confirmation and then offers an eight second Undo; resuming one asks a
confirmation.
The typed word is a safeguard against a stray click in the page. It is not a security boundary: anyone who can write the policy directory can pause or resume, which is the same trust as editing the policy.
From a terminal, when no browser is to hand, the command acts at once with no prompt, and prints exactly what it changed:
--yes is accepted so a script can say plainly that it means it; it changes
nothing.
The switch is the file pause.json beside the policy (override with
BROKER_PAUSE_FILE). Brokers re-read it on every request, so it needs no
restart, and it fails closed: a missing file means nothing is paused, but a file
that exists and cannot be read pauses everything. Agents are told only that
access is paused and not to retry; your typed reason stays private.
Every pause and resume, from the page or the terminal, appends one line to the audit log: the scope, the direction, where it came from and when. The reason is never written there.
Tamper evidence. Resuming never deletes pause.json; it writes a fresh
file with an explicit resume record. A running broker remembers the last pause
it enforced, so if that pause disappears (the file is deleted, emptied or
replaced) without a matching resume record, the broker stays paused and writes
a PAUSE_TAMPER audit line. Only a valid file carrying the resume releases it:
run npm run pause -- off, or pause and resume once from the page. This catches
accidents and casual meddling. A hostile process running as the same OS user
can still write a valid file; the real protection against that is the
secure-launch profile in SECURITY.md.
Hot reload
Set BROKER_HOT_RELOAD=1 on a broker process and it picks up key manager
changes (a new key, a new API, a changed grant) on the next request, with no
restart. The key manager writes a small marker beside the policy after every
change. If the new policy is invalid, the broker keeps its previous working
state and writes an audit line. Off by default.
Each opt-in control has a short page saying what it does and what it trusts: pause, hot reload, key requests, approvals.
Usage, rotation and approvals
- Each key card shows when it was last used, by which agent, and how its last 30 days of requests ended, read from the content-free audit log.
- Replacing a key records the date. For known providers a link opens the provider's key page so you can revoke the old key.
- The Approvals panel creates and revokes approvals for operations the
policy marks
requires_approval, with a lifetime of 1 minute to 24 hours. It cannot approve anything else, and it never grants write access itself. - About twenty providers are known (OpenAI, Anthropic, GitHub, Cloudflare, Stripe, itch.io and others); picking one fills in the address and key style.
Running
Environment:
Manual Claude Code registration from a checkout:
Verification
Run npm run check for types, lint and unit tests, npm run verify:pack
for the package allowlist, and npm run test:e2e for the installed-server
proof. CI runs these on Windows, macOS and Linux with Node 22 and 24.
The end-to-end test packs the project, installs the tarball into an empty
directory and starts its declared stdio executable. Synthetic credentials
and a loopback HTTP API exercise tool discovery, principal-scoped grants,
denials before upstream calls, response redaction, metadata-only responses,
approval revocation and content-free audits. It also runs the installed
credential-broker-pause binary through pause, status and resume, and
starts credential-broker-keys, checks its CSP and token gate, and closes
it. No production credential, OS keychain entry or external API is used.
This does not verify the key manager page in a browser, secure-launch
deployment, independent operator workflows or the eventual npm-hosted
artifact.
Idempotency keys are required and recorded when the policy says so. They are audit identifiers, not upstream deduplication: the broker neither forwards them as headers nor suppresses repeated mutations.
The manual release workflow uses Node 24 for the npm trusted-publishing
toolchain and runs the installed proof through prepublishOnly before
publication. See npm's trusted-publisher requirements.
The package's npm trust configuration and the release tag still need
operator verification before a first publish.
What this is not
- Not an OAuth authorization server. It brokers static secrets you already hold. OAuth token acquisition and refresh are on the roadmap.
- Not a cloud service. It runs where your agents run, and your secrets stay on your machine. If you want hosted multi-tenant auth for agents, use a platform built for that.
- Not a bypass. It delegates through existing gates with scoped-down authority; it does not defeat anything.
How it compares
Other tools solve neighbouring problems. This section names only what was read on the projects' own pages on 2026-11-06; it is not a review of their security, and they may have moved on since.
- Hosted secrets managers with an MCP server, such as Doppler's. Doppler's server is described as experimental and intended for development and evaluation, calls the Doppler API with an account token kept in the system keyring, and lists "read secret values" among its tools. That is a different shape: the manager hands values to the agent on request and relies on token permissions. This broker never returns a value; the agent gets the result of a scoped call, and nothing leaves the machine.
- Governance gateways, such as Agenthof (Apache-2.0, Go, pre-release). It injects per-role or per-resource credentials the agent never holds, grants access per named tool or resource, and keeps a hash-chained audit ledger attributed to the human who asked. It is a broader control plane with OIDC human identity. This broker is narrower: one process per agent, one YAML file, per-operation method and path grants, no identity provider, no server to run.
- Credential-proxy MCP servers that follow the same "the agent calls through a process that holds the key" pattern exist (for example Janee and pincer-mcp appear in directories). Their pages could not be read for this section, so nothing is claimed about them here.
Where this broker is deliberately small: it brokers static keys you already hold, it has no OAuth flows yet, no rate limits beyond key requests, and no hosted or multi-user mode. Where it is deliberately strict: deny by default per operation, a single decision function for explain and execute, secrets redacted in every representation it creates, a content-free audit, and operator controls that only ever narrow access.
Roadmap
- OAuth 2.1 device and refresh flows for credentials that expire
- HTTP transport with mutually authenticated, audience-bound, expiring principal tokens through the same engine, matching agent-handoff-mcp
- Per-credential rate limits and spend counters
- Key manager page: provision, replace, test, and remove keys
- Keychain backends for macOS (
security) and Linux (secret-tool), unit tested with a faked command runner; real round trips on a Mac and a Linux desktop are still to be confirmed by hand - Operator CLI: approve, rotate
Licence
Apache 2.0. See LICENSE.
来源:README.md,提交 c05624c
工具
0版本历史
1- v0.1.1最新Nov 7, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分