noyalib-mcp
Model Context Protocol server exposing noyalib's
lossless YAML editing to AI agents (Claude Desktop, Claude
Code, Cursor, Zed, Continue.dev, β¦).
Contents
Install
cargo install noyalib-mcp
For environments without a Rust toolchain (the typical AI-agent
deployment shape):
npx @sebastienrousseau/noyalib-mcp
docker run --rm -i ghcr.io/sebastienrousseau/noyalib-mcp:latest
Split from the monorepo since v0.0.13. Prior versions
shipped from sebastienrousseau/noyalib/crates/noyalib-mcp/
under the workspace-lockstep release cadence. From v0.0.13
onward noyalib-mcp lives here as its own crate, still
released in strict lockstep with the parent
noyalib at
the same version. See
ADR-0005
for the rationale and rollback recipe.
Both consume the same signed binary attached to every GitHub
Release. See Verification for the verify
commands.
Requirements
- Rust 1.86.0 or newer to build from source:
rust-version in
the manifest, enforced by the msrv-core CI job on every push.
- Any tier-1 platform. CI runs the tests on Linux, macOS, and
Windows with the stable, beta, and nightly toolchains; stable is the
gate, beta and nightly are early warning.
- The matching core. This crate pins
noyalib at the identical
=0.0.X and releases in lockstep with it; Cargo resolves that pin
for you.
- An MCP client speaking JSON-RPC 2.0 over stdio (2025-06-18 or
2026-07-28 protocol eras); the README's Connect section lists tested
hosts.
Quick Start
The server speaks JSON-RPC 2.0 over stdio with newline-delimited
frames, per the
MCP specification. A typical
agent launches the binary as a child process, sends
initialize, then dispatches tool calls:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"agent","version":"0.0.1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
{"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"format","arguments":{"yaml":"a:1\nb:2\n"}}}
Why this approach?
AI agents that edit YAML configuration today regex-replace and
corrupt comments, indentation, and document structure. The same
agent fixing a port number in a Kubernetes manifest can shift
every comment by a line, reorder sibling keys, or strip
trailing whitespace that a downstream linter cared about.
noyalib's CST does the edits losslessly β a set("server.port", "9090") rewrites only the byte span of the 8080 scalar; the
surrounding comments and indentation pass through untouched.
This server is the protocol shim that lets MCP-aware clients
drive that engine safely:
- Lossless mutation.
tools/call set returns a document
byte-identical to the input outside the touched span.
- Surgical reads.
tools/call get walks the dotted path
and returns just the value, not the whole tree.
- Schema validation.
tools/call validate --schema runs
the same JSON Schema 2020-12 engine noyavalidate ships.
- Stdio transport. Standard MCP. Works with every
spec-compliant client.
Connect
Claude Desktop / Claude Code
claude mcp add noyalib $(which noyalib-mcp)
Cursor
~/.cursor/mcp.json:
{
"mcpServers": {
"noyalib": {
"command": "noyalib-mcp"
}
}
}
Zed
~/.config/zed/settings.json:
{
"context_servers": {
"noyalib": {
"command": { "path": "noyalib-mcp" }
}
}
}
Continue.dev
~/.continue/config.json:
{
"experimental": {
"modelContextProtocolServers": [
{ "transport": { "type": "stdio", "command": "noyalib-mcp" } }
]
}
}
Any other MCP-aware client
Point at the binary; the transport is stdio with newline-
delimited JSON-RPC 2.0.
The v0.0.1 server registers two file-oriented tools β both
operate on a YAML file at file: <path>, not on inline source
strings, so an agent's edits land on disk losslessly:
noyalib_get β Takes { file: string, path: string }; returns the raw source fragment at the dotted/indexed path (e.g. server.host, items[0].name). No re-quoting; no canonicalisation.
noyalib_set β Takes { file: string, path: string, value: string }; returns the file rewritten via the lossless CST so only the touched span changes; comments, blank lines, and sibling formatting survive byte-for-byte. The value is a YAML fragment (0.0.2, "hello", [1, 2, 3]); a parse failure leaves the file unchanged.
noyalib_parse β Takes { yaml: string }; returns the JSON data model of the text (tags stripped, a stream as an array). Stateless: nothing on disk is touched.
noyalib_edit β Takes { yaml: string, path: string, value: string }; returns the whole text with that one value replaced losslessly. Stateless.
noyalib_validate β Takes { yaml: string, schema?: string }; returns valid with either the parse error (line and column) or every JSON Schema violation with its path. Stateless.
Each tool's full input schema lives in the response to
tools/list. The server also handles the standard
initialize / initialized / notifications/cancelled
lifecycle.
Format / parse / validate are not exposed as MCP tools today β
they're available via the noya-cli
binaries (noyafmt, noyavalidate) and the
noyalib library API. Promotion to
first-class MCP tools is on the v0.0.2+ roadmap.
Examples
Agent-driving demos under
crates/noyalib-mcp/examples/:
| Script | What it shows |
|---|
handshake.sh | initialize β tools/list smoke test. Confirms the binary speaks the protocol and announces the expected tools. |
format-call.sh | tools/call format on a poorly-spaced document. Demonstrates that comments + indentation pass through the CST formatter unchanged. |
set-then-get.sh | Round-trip the mutation surface: set rewrites server.port, get reads it back. Surgical edit; surrounding bytes untouched. |
chmod +x crates/noyalib-mcp/examples/*.sh
crates/noyalib-mcp/examples/handshake.sh | jq -c .
POSIX-shell only β no jq, no node dependencies. Pipe
through jq -c . if you want pretty-printed JSON responses.
Verification
GitHub Releases ship the crate archive and a CycloneDX SBOM,
each with a sigstore bundle and checksums; the GHCR image is
built from the tagged source. Pre-built binaries are not
attached to releases yet. To verify a release artefact:
COSIGN_EXPERIMENTAL=1 cosign verify-blob \
--certificate-identity-regexp 'https://github.com/sebastienrousseau/noyalib-mcp/' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--bundle <artefact>.bundle \
<artefact>
The npm wrapper additionally carries an
npm provenance attestation:
npm view noyalib-mcp provenance
Full cookbook: pkg/VERIFY.md.
When not to use noyalib-mcp
- You don't trust your AI agent with filesystem access at
all. noyalib-mcp doesn't read or write files itself β
every operation takes the YAML document as a string argument
and returns the result as a string. The agent decides what
to do with the result. If the agent has filesystem access,
it can persist the response wherever it wants.
- You need a sandboxed schema registry. noyalib-mcp accepts
schemas as inline strings in
tools/call validate; it does
not fetch schemas from URLs. If your workflow needs
network-resolved schemas, the agent is responsible for
fetching the schema first and passing the bytes.
Compatibility
MSRV: Rust 1.86.0 stable β the lowest toolchain this crate
can be built and tested on, matching the noyalib core floor.
criterion 0.8 (the benchmark dev-dependency) declares
rust-version = 1.86, so cargo check --all-targets and the
bench suite fail on 1.85 with criterion@0.8.2 requires rustc 1.86 β cargo check --lib alone still builds on 1.85. We publish
the number we verify. The MCP wire surface itself is text-only
JSON-RPC and pulls no nightly-only deps. CI verifies the floor on every
PR via the Per-crate MSRV workflow job. The bump policy
lives in
docs/POLICIES.md.
Tier-1 platforms (CI-verified each PR): aarch64-apple-darwin,
x86_64-unknown-linux-gnu, x86_64-pc-windows-msvc. The
binary writes via atomic file replacement on every platform β
on Windows via MoveFileExW(MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) semantics.
Documentation
The four entry points, identical across every repo in the family:
-
User Manual β this crate's rendered book: its guides, architecture, and release notes; the family manual for the core library is at https://sebastienrousseau.github.io/noyalib/manual/
-
API reference β rustdoc on docs.rs
-
Developer docs β this repo's dev entry point, pointing at the family guide
-
Ecosystem map β the six crates, the lockstep model, the scorecard
-
Engineering policies (MSRV, SemVer, security, performance, concurrency, platform support, feature flags):
docs/POLICIES.md
-
Security policy:
SECURITY.md
-
API reference: https://docs.rs/noyalib-mcp
-
Tools reference (input schemas + error codes):
docs/tools-reference.md
-
Agent integration (Claude Desktop, Cursor, Continue.dev):
docs/agent-integration.md
-
MCP specification: https://modelcontextprotocol.io
-
Workspace README:
https://github.com/sebastienrousseau/noyalib#readme
Sibling MCP servers by the same author β open-source, Apache-2.0 licensed, targeting banking and financial-services AI agents. noyalib-mcp complements them by giving agents lossless YAML editing for structured configuration files:
| Server | Purpose |
|---|
pain001-mcp | Generate & validate ISO 20022 pain.001 payment initiation files (Customer Credit Transfer) |
bankstatementparser-mcp | Parse bank statements (BAI2, MT940/MT942, CAMT.053, OFX, CSV) into structured transactions |
camt053-mcp | Parse & reconcile ISO 20022 camt.053 bank-to-customer statements β CBPR+/HVPS+ ready |
acmt001-mcp | Generate & validate ISO 20022 acmt.001 account management messages |
MCP Registry
mcp-name: io.github.sebastienrousseau/noyalib-mcp
Every push runs the official yaml-test-suite
through this server's tools, from the same vendored suite and the same core
commit as the noyalib core: 195 of 195 addressable cases (the other 211 have
no top-level key for noyalib_get to read; noyalib_parse sees all of them).
A two-document configuration that uses most of YAML at once
(tests/fixtures/ultra-complex/) parses to exactly its expected JSON through
noyalib_parse. Details and the family table:
noyalib.com/conformance.
License
Dual-licensed under Apache 2.0
or MIT, at your option.