mcpField
N
Nodiom Cloud

Nodiom Cloud

远程服务
nodiom.md生产力91 周前更新

关于 Nodiom Cloud

Nodiom Cloud 提供基于 Markdown 的共享看板和文档,让 AI Agent 提交工作供人审核。它沿用 Nodiom 的结构化选择器,并支持跨 Agent 与人员共享状态、记录操作归属和审批流程。

主要功能

  1. 01共享 Markdown 文档
  2. 02由标题和清单项构成的看板
  3. 03原子化领取任务
  4. 04人工批准或退回步骤
  5. 05Agent 操作归属与审核提醒

适用场景

  1. 01协调多个 Agent 之间的任务交接
  2. 02在 Agent 工作继续前审核成果
  3. 03跨设备和人员共享文档状态

MCP 相关

Nodiom Cloud 提供 MCP 端点,可供 Claude、ChatGPT、Cursor 和 VS Code 等客户端连接。MCP 工具支持共享文档与看板工作流,包括 Agent 交接和人工审核。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://api.nodiom.md/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

工具

详细介绍

nodiom

以结构化方式读取和写入 Markdown。

npm version license test

让 AI Agent 能够可靠地处理文档结构,无需脆弱的字符串操作。

Nodiom 将 Markdown 作为树来读取和编辑。标题是分支;列表、段落、表格和代码块是节点。你可以像查询 DOM 一样,使用类似路径的选择器定位节点,并修改它,而不必重写文件的其余部分,也不会破坏其他内容。


三种使用方式

是什么位置
库(本仓库)在 Node.js 中对文件或字符串进行 Markdown 结构化读写npm install @synexiom-labs/nodiom
nodiom-mcp一个 MCP 服务,让 AI Agent 能对本地 Markdown 文件执行这些操作Synexiom-Labs/nodiom-mcp
Nodiom Cloud托管服务:让 Agent 与人员共享状态。AI Agent 在同一看板上提交已完成的工作,由监督人员审批或退回nodiom.md

在此处,结构化状态位于 Markdown 中;在 Cloud 中,Agent 与人员共享状态。三种方式都使用相同的引擎和选择器。


问题所在

当 AI Agent 或任何程序需要修改 Markdown 文档时,通常会将整个文件加载为字符串,用正则表达式查找相应部分,插入新文本,然后祈祷格式没有被破坏。若两个写入者同时操作,其中一个的修改就会悄无声息地丢失。

Nodiom 将 Markdown 视为它本来的样子:一棵树。你指定位置(# Project > ## Tasks > li[contains="c_7f3a"])和操作(读取、写入、追加、删除),该节点之外的内容不会改变。


快速示例

import { Nodiom } from '@synexiom-labs/nodiom';

const doc = await Nodiom.fromFile('./project.md', { lock: true });

// Read everything under a heading (the heading line itself isn't included)
const summary = doc.read('# Project > ## Summary');

// Add a task
doc.append('# Project > ## Tasks', '- [ ] Deploy to staging');

// Tick it, addressed by its text rather than its position
doc.write('# Project > ## Tasks > li[contains="Deploy to staging"]', '- [x] Deploy to staging');

// Remove the first finished item
doc.delete('# Project > ## Done > li[0]');

// Write back to disk and release the lock
await doc.save();

安装

npm install @synexiom-labs/nodiom

要求: Node.js 18+


选择器

选择器是由 " > " 分隔的路径,由多个片段组成,每个片段都与文档结构中的一个节点匹配。

标题片段
"# Project Alpha"              → The H1 "Project Alpha" and all its content
"## Tasks"                     → A top-level H2 named "Tasks"
"# Project Alpha > ## Tasks"   → The H2 "Tasks" nested under H1 "Project Alpha"

# 字符的数量必须与标题在文档中的层级相符。

范围。“## Tasks 下的内容”指该标题之后、下一个同级或更高级标题之前的所有内容,这也是读取时的方式。

**重复标题。**如果同一范围内出现多个相同标题,单独使用标题片段会产生歧义并抛出 AmbiguousSelectorError;nodiom 绝不会悄悄选中第一个。可使用父标题限定,或按索引指定其中一个;索引从零开始,也支持负数,例如 li[n]:

"# Doc > ## Notes[0]"    → The first "## Notes" under "# Doc"
"# Doc > ## Notes[1]"    → The second
"# Doc > ## Notes[-1]"   → The last

索引必须紧挨文本。带空格的 "## References [1]" 会被当作字面标题匹配;若标题本身确实是 Tasks[1],则始终会优先尝试按字面匹配。

**Frontmatter。**文档顶部的 YAML(---)或 TOML(+++)区块会被识别为 frontmatter。它不会出现在 tree() 中,也无法通过选择器定位;任何编辑都不会改变其中的任何字节。

元素片段
"## Tasks > li[0]"     → First list item under ## Tasks
"## Tasks > li[-1]"    → Last list item (negative indexing)
"## Notes > p[0]"      → First paragraph under ## Notes
"## Arch > table[0]"   → First table under ## Arch
"## Arch > code[0]"    → First code block under ## Arch

元素类型:p、li、code、blockquote、table、hr。

内容片段

索引表示位置,而非身份。如果在你计算出 li[2] 和写入该位置之间,另一个写入者向同一部分追加了内容,那么该索引现在指向不同的节点,写入就会悄无声息地落到错误的位置。

只要有多个写入者会操作同一文档,就应改为按内容定位:

'## In Progress > li[contains="c_7f3a"]'   → The list item whose text contains "c_7f3a"
"## Notes > p[contains='TODO']"            → The paragraph containing "TODO"

子字符串会与节点自身的源文本进行匹配,并且在范围内必须只匹配一个节点。如果匹配到多个节点,则会抛出 AmbiguousSelectorError,而不是选中第一个:这正是为了避免悄无声息地编辑多个候选项之一。


API

加载
// From a file
const doc = await Nodiom.fromFile('./wiki.md');

// From a file, with an advisory lock (see Concurrency)
const doc = await Nodiom.fromFile('./wiki.md', { lock: true, lockTimeout: 5000 });

// From a string: no filesystem, safe for serverless
const doc = Nodiom.fromString(markdown);
读取
// The Markdown under a selector. For a heading, its body without the heading
// line, so write(sel, read(sel)) changes nothing.
const content: string = doc.read('# Project > ## Summary');

// Each list item's source under a selector, marker and nested lines included:
// ['- [ ] Task A', '- [x] Task B\n  - [x] sub-step']
const items: string[] = doc.readList('# Project > ## Tasks');

// Whether a location exists, and what it is
const info = doc.query('# Project > ## Tasks');
// { exists: true, type: 'heading', depth: 2, childCount: 7, index: 3 }

// The heading outline
const outline = doc.tree();
// [{ heading: 'Project', depth: 1, children: [{ heading: 'Tasks', depth: 2, children: [] }] }]
更改

修改操作会返回 this,因此可以链式调用。

// Replace the body of a section; the heading is kept
doc.write('# Project > ## Summary', 'On track for Q3.');

// Replace one list item. Text without its own bullet keeps the item's marker
// and checkbox: this stays "- [ ] Final review (Friday)"
doc.write('## Tasks > li[contains="Final review"]', 'Final review (Friday)');

// Add after the last node in a section, separated by a blank line
doc.append('# Project > ## Tasks', '- [ ] Task C');

// Remove a node, or a heading together with its whole section
doc.delete('# Project > ## Done > li[0]');

doc
  .append('# Project > ## Tasks', '- [ ] Task D')
  .delete('# Project > ## Done > li[-1]');

你的内容会按原样插入(仅进行修剪),不会重新序列化;未涉及的内容则保持逐字节不变。

保存
const markdown: string = doc.toString();

await doc.save();               // write back to the original file, release any lock
await doc.saveAs('./out.md');   // write to a new file (keeps the original lock)
await doc.unlock();             // release the lock explicitly
错误
import {
  NodiomError,            // base class of the errors below
  SelectorNotFoundError,  // nothing matched; .suggestions has near misses
  SelectorParseError,     // the selector itself is malformed
  AmbiguousSelectorError, // more than one node matched
  LockError,              // couldn't get the file lock in time
} from '@synexiom-labs/nodiom';

try {
  doc.read('# Project > ## Taks'); // typo
} catch (e) {
  if (e instanceof SelectorNotFoundError) {
    console.log(e.selector);     // "# Project > ## Taks"
    console.log(e.suggestions);  // ["Tasks"]
  }
}

保真度

只有在不会损坏未触及内容的前提下,结构化编辑器才有用。Nodiom 会在节点的确切位置编辑源文本,而不是重新打印整个文档,因此编辑节点之外的格式、空格、列表标记和转义都保持原样。在 2,369 个真实 Markdown 文件(早期用户自己的笔记)样本中,每个文件往返后都与原文件逐字节一致。


在 AI Agent 中使用

在自己的机器上:nodiom-mcp

nodiom-mcp 将这些操作作为 MCP 工具(nodiom_tree、nodiom_read、nodiom_read_list、nodiom_query、nodiom_write、nodiom_append、nodiom_delete)提供给 Claude Code、Claude Desktop、Cursor 或任何 MCP 客户端,用于处理磁盘上的 Markdown 文件。

claude mcp add -s user nodiom -- npx -y @synexiom-labs/nodiom-mcp
跨机器、Agent 和人员:Nodiom Cloud

当 Agent 在不同机器上运行,或来自不同供应商时,他们需要一个共享状态的地方。Nodiom Cloud 就是这样一个地方,基于此引擎构建:

  • 共享文档:多个 Agent 可以同时编辑,彼此不会覆盖对方的更改。
  • 看板:列对应标题,卡片对应待办事项,因此看板就是一份可在任何地方阅读的 Markdown 文档。Agent 通过一次原子操作认领卡片,因此不会有两个 Agent 领取同一项任务。
  • 人工审核步骤。 Agent 将已完成的工作移交到审核列,然后停止。人员可以批准,也可以附上备注退回;Agent 会读取备注,而且 Agent 无法跳过此步骤。
  • 归属信息和提醒。 每次写入都会标明执行操作的 Agent,审核提醒会通过电子邮件、Slack 或 Discord 发出。

在 Claude、ChatGPT、Cursor 或 VS Code 中,只需添加一个地址并登录即可连接,无需复制密钥:

https://api.nodiom.md/mcp

提供免费套餐。各应用的设置说明见 nodiom.md,完整工具参考见文档。


并发

在单台机器上,nodiom 通过 proper-lockfile 获取建议式文件锁。fromFile 会在读取前获取锁,因此两个进程无法同时读取旧版本并相互覆盖。

const doc = await Nodiom.fromFile('./shared-wiki.md', { lock: true });
// Other nodiom processes locking this file wait, or throw LockError after lockTimeout

doc.append('## Log', `- ${new Date().toISOString()} Agent B finished the task`);
await doc.save(); // writes and releases the lock

该锁适用于同一台机器上的 Node.js 进程。跨机器或使用无服务器函数时,请使用 Nodiom Cloud,它会在数据库文档行锁下执行每次写入。


对比

方案结构化读取?结构化写入?往返保真度无服务器并发
fs.readFile + 正则表达式否脆弱取决于正则表达式否否
gray-matter仅 Frontmatter仅 Frontmatter是是否
数据库否否否(会丢失 Markdown)是是
向量数据库仅语义否否是是
nodiom是是是是(fromString)同一台机器
Nodiom Cloud是是是是跨机器

路线图

计划中的事项,顺序未定:

  • 支持带位置控制的 insert():在同级节点之前或之后插入。
  • diff():对文档的两个版本进行结构化比较。
  • 通配标题选择器(## *Tasks*)。
  • 1.0:稳定的选择器语法和 API。

发布记录见 CHANGELOG。


贡献

欢迎贡献。对于较大的更改,请在提交 PR 前先提出 issue。

git clone https://github.com/Synexiom-Labs/nodiom
cd nodiom
npm install
npm test

许可证

MIT — Synexiom Labs Inc.

源自为 Cortexiom 推理架构提供支持的状态层。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。