Arcaeon Adapter

io.arcaeon由 arcaeon.io 发布v0.2.0更新于 Oct 24, 2026

MCP stdio proxy that logs the tool-call seam to the ledger without the agent's cooperation

已验证STDIO仅桌面开发者工具安全与监控

概览

AI 生成的概览

一个本地 stdio 代理,位于 MCP 客户端与服务器之间,无需代理配合即可把每次工具调用记录到哈希链账本。

可以做什么
它包裹任意 MCP 服务器命令,逐字节转发 JSON-RPC,并为每次 tools/call 写入一行哈希链记录:调用的工具、参数与结果的摘要、成功或错误、以及耗时。它还会记录会话开始与结束、initialize 和 tools/list 事件,并记录始终未获得应答的调用。默认只保存摘要而非原始载荷,--raw 选项会嵌入原始载荷。生成的日志可用配套的 arcaeon-ledger 命令行工具校验,能指出链断裂的具体行。
适用场景
当你希望自动、可防篡改地记录经过某个 MCP 接缝的全部内容,而不是依赖代理自行记录其操作时,可以使用它。它适用于对被包裹的 MCP 服务器的工具调用进行审计或复核。它只覆盖被包裹的那一个接缝,不覆盖其他工具或直接发起的网络调用。
运行要求
作为本地进程在用户机器上运行(仅桌面端,stdio 传输)。通过 pip 安装(arcaeon-adapter,可选 arcaeon-ledger)。未声明任何账号、API 密钥、环境变量或请求头,运行时也不进行网络调用。配置方式是用 --ledger 路径包裹现有的 MCP 服务器命令。
安装前请注意
账本路径应与被包裹服务器自身写入的账本分开。--raw 选项会把原始参数与结果载荷写入日志,其中可能包含敏感提示或数据;该选项默认关闭。控制配置的人可以移除该包裹,绕过代理的操作不会被记录。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Arcaeon Adapter,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

arcaeon-adapter

arcaeon-ledger proves a record wasn't altered. It can't prove the record is complete — because the agent decides what to write. This moves the pen.

arcaeon-adapter is a stdio proxy that sits between an MCP client and an MCP server. It forwards JSON-RPC byte-for-byte, and on every tools/call it writes one hash-chained row to its own ledger: which tool, digests of the arguments and the result, ok or error, how long it took.

It runs in its own OS process. The agent is not consulted, cannot skip it, and cannot see it.

pip install arcaeon-adapter arcaeon-ledger

Wrap any MCP server's command — one line of config, zero code:

json
{ "mcpServers": {    "ledger": {      "command": "python",      "args": ["-m", "arcaeon_adapter", "--ledger", "seam.log.jsonl", "--",               "python", "-m", "arcaeon_ledger.mcp_server", "--log", "agent.log.jsonl"]    }} }

Or straight from a shell:

python -m arcaeon_adapter --ledger seam.log.jsonl -- <your mcp server command...>

What lands in the log

Real rows, from wrapping arcaeon-ledger's own MCP server (the ledger logging itself through the ledger), trimmed for width. Captured on adapter 0.1.0 with ledger 0.5.7; a current run stamps the installed versions in seam_impl and ledger_backend:

json
{"evt":"session_begin","seam":"mcp-stdio","seam_impl":"arcaeon-adapter/0.1.0", "session":"25eda195-70e8-45cb-9035-be9f61cfc759","seq":1, "server":"arcaeon_ledger.mcp_server","ledger_backend":"arcaeon-ledger/0.5.7", "command":["python","-m","arcaeon_ledger.mcp_server","--log","agent.log.jsonl"], "command_digest":"sha256:json-c14n:v1:ec06bd01…","raw_payloads":false, "ts":"2026-08-19T14:18:08Z","chain":"0081ec1fe5d98395192f595e553b4133"}
{"evt":"tools_list","seq":3,"tools":["ledger_append","ledger_verify"],"tool_count":2, "tools_digest":"sha256:json-c14n:v1:b85c9fce…","status":"ok", …}
{"evt":"tool_call","seq":4,"tool":"ledger_append","rpc_id":"3", "args_digest":"sha256:json-c14n:v1:cfb2bb50…", "result_digest":"sha256:json-c14n:v1:ba037f70…", "status":"ok","ms":144, …}
{"evt":"session_end","seq":6,"reason":"child_exit","exit_code":0, "rows_before_end":5,"calls":2, …}

In that same run, the wrapped server's own log — the diary it keeps when the agent remembers to call ledger_append — had one row. The seam log had six. That difference is the entire product.

fieldwhy it's there
evttool_call, session_begin, session_end, mcp_initialize, tools_list
seamalways "mcp-stdio" — the provenance tier. A separate process saw this at the protocol level. A downstream verifier reads this field to tell infrastructure-grade capture from a cooperative in-process callback the agent could simply not call.
session / seqone proxy process = one session; seq is a dense total order within it. session_begin + session_end bracket the run so a reviewer can pair a session's start and end.
args_digest / result_digestsha256:json-c14n:v1:<hex> — self-describing, so a stranger holding only the row can reproduce the computation.
status / msok / error / unanswered, and wall-clock duration. error covers both a JSON-RPC error and a result carrying isError: true.

Digests, not payloads, by default. The row proves which bytes crossed the seam without keeping them. An audit log that quietly becomes a copy of every prompt and every result is a liability, not an asset — and a person-free core is much easier to retain for years. --raw embeds the payloads for deployers who own that risk; the digests stay either way.

A call that never gets an answer still gets a row (status: "unanswered"). Otherwise "kill the server mid-call" would be a way to make an action leave no trace at the seam, which is the exact hole this exists to close.

Verify it

The seam log is an ordinary arcaeon-ledger file:

python -m arcaeon_ledger.cli verify seam.log.jsonl

Edit a row, delete one, reorder them — every later link breaks and verify names the exact line.

The honest limit

An adapter on one seam logs that seam completely — and nothing else. An agent can still act around it: a direct HTTP call, an un-wrapped MCP server, a shell command never crosses this proxy and never hits this ledger. The second-set-of-books problem does not go away; no logging layer can force total honesty. What the adapter guarantees is narrower and real: everything that crossed the instrumented seam is in the record, automatically, and the record proves itself. Honesty is forced at the seams, not everywhere — not a lock, a neighborhood.

Specifically blind to:

  • your harness's built-in tools (Bash, file edits, web fetch) — they never touch MCP
  • any MCP server you did not wrap
  • HTTP the agent makes natively
  • the model's reasoning, which is not an action and leaves no wire trace
  • frames larger than --max-frame (relayed fine; counted and reported in session_end as oversize_frames_unlogged, never a silent gap)

And on compliance, plainly: this does not make anyone compliant with anything. Regulations like the EU AI Act's Art. 12 place duties on a provider or deployer, and duties land on people, not libraries. What this is: a mechanism that makes recording at one seam automatic rather than voluntary, which is a thing you would otherwise have to build. Retention policy, log semantics, risk classification, and every other obligation remain yours. Anyone selling you a package that "makes you compliant" is selling you a story.

Whoever controls the config can also remove the wrapper. That is the same class of limit every logging layer has, and it is stated here rather than in a footnote.

Fidelity is the P0 property

A proxy that corrupts, reorders, or delays a customer's JSON-RPC is worse than no proxy at all. So the design puts fidelity ahead of observation, structurally:

  • Bytes are forwarded first, observed second, from a copy. A parsing defect here cannot alter or withhold traffic; the worst it can do is produce a wrong row.
  • Raw binary end to end. No text mode anywhere — on Windows that would rewrite \n as \r\n and silently alter every frame.
  • No reframing, ever. We never parse a frame and re-emit it. The classic proxy bug is json.dumps(json.loads(frame)): semantically identical, byte-different.
  • One thread per direction, so ordering within a direction is the OS's.
  • The child's stderr is inherited, not piped. Its diagnostics land exactly where they did unwrapped, and the proxy itself prints nothing on an honest run.
  • Malformed frames are relayed untouched and not logged. A server that prints a stray line to stdout keeps working; we don't guess, because a guess in an audit record is fiction.
  • The child's exit code is the proxy's exit code.

This is tested by running a scripted client against a synthetic MCP server directly, then through the proxy, and requiring the two stdout streams to be identical bytes — including a 4 MB frame spanning ~64 pipe reads, unicode, embedded CRLF, malformed lines, a notification with no id, and a final frame with no trailing newline.

Prove the checks can fail

python -m arcaeon_adapter.selftest

An instrument that has never failed proves nothing. Every case runs GREEN on a clean fixture, then mutates and requires the same check to go RED — with a no-op guard in between, because a mutation that changes nothing proves nothing either. Output abridged (two header lines and the tail of each PASS line cut; the drop_byte length differs by a byte or two between runs):

PASS passthrough_fidelity GREEN: 2001202 bytes identical byte-for-byte through the proxyPASS passthrough_fidelity RED on reserialize: corruption detected (length 2001202 vs 2001097)PASS passthrough_fidelity RED on drop_byte: corruption detected (length 2001202 vs 2001168)PASS one_row_per_call GREEN: 5 tools/call frames -> 5 tool_call rows, 9 rows total, seq 1..9PASS one_row_per_call RED on log_on_request: count check caught 10 rows for 5 callsPASS unanswered_call_logged GREEN: unanswered `quiet` call rowed with args_digest boundPASS unanswered_call_logged RED on no_flush: dropping the shutdown flush loses the rowPASS tamper_detected GREEN: seam log verifies (ok=True, rows=9)PASS tamper_detected RED on edited args_digest: first_break='line 4: chain mismatch'PASS digest_recipe_frozen GREEN: 3 frozen json-c14n:v1 vectors reproduce exactlyPASS digest_recipe_frozen RED on unsorted_keys: drifted canonicalizer produces a different digest
ALL CHECKS PASSED — and every one was observed failing on its own defect.

The fidelity mutations are injected into the real relay, not a mock, so what's proven is that the comparison would catch a regression in the shipping code path.

Options

--ledger PATH      seam ledger (required). Keep it SEPARATE from any ledger the                   wrapped server writes: one file, one writer, clean provenance.--server NAME      label for the wrapped server in every row (default: derived                   from the command — `-m pkg.mod` reads as "pkg.mod", not "python")--session ID       session id (default: a fresh uuid4 per proxy process)--raw              embed raw argument and result payloads. Off by default.--max-frame BYTES  frames larger than this are relayed but not logged (default 64 MiB)

Install and dependencies

Stdlib only at runtime. arcaeon-ledger is the intended writer and is what makes a row provable — but it is not a hard dependency, on purpose: this gets wrapped around somebody else's working MCP server by editing one line of config, and a missing package must never be why their server fails to start. Without it, rows are written by a byte-compatible fallback and session_begin says so in ledger_backend, so a reviewer never has to guess which writer produced a chain. (That byte-compatibility is asserted in the test suite, not just claimed here.)

No network calls at runtime. Ever.

pytest -q                            # 146 testspython -m arcaeon_adapter.selftest   # the mutation harness

Status

v0. Works, tested, dogfooded on our own MCP server. Deliberately not yet built: --authority stamping, --bind-inputs artefact binding, --auto-pin witness publication, and harness-hook / HTTP-gateway seams. See CHANGELOG.md.

MIT.

来源:adapter/README.md,提交 4aaceaf

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

2
  1. v0.2.0最新Sep 23, 2026
  2. v0.1.4Sep 23, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分