关于 nope-mcp — Open Educational Resources search
nope-mcp 是面向教育资源搜索与发布场景的 MCP 服务,可查询 AMB Nostr 中继上的开放教育资源及相关内容。它还支持元数据提取、署名和发布 Nostr 事件。
主要功能
- 01跨内容全文搜索与筛选
- 02按问题检索带引文段落
- 03浏览学科与教育层级
- 04提取网页教育元数据
- 05签名并发布 Nostr 事件
适用场景
- 01查找开放许可的学习资源
- 02为问题答案检索原文依据
- 03将网页信息整理为教育资源元数据
- 04发布 AMB 教育资源
MCP 相关
它向 MCP 客户端提供搜索、浏览、资源查询、元数据提取及 Nostr 签名发布工具;HTTP、stdio 和 Nostr 传输提供不同的工具访问范围。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://mcp.edufeed.org/mcpclaude mcp add --transport http org-edufeed-nope https://mcp.edufeed.org/mcp{"mcpServers":{"org-edufeed-nope":{"type":"http","url":"https://mcp.edufeed.org/mcp"}}}在 Cursor 中打开{"servers":{"org-edufeed-nope":{"type":"http","url":"https://mcp.edufeed.org/mcp"}}}在 VS Code 中打开{"mcpServers":{"org-edufeed-nope":{"type":"http","url":"https://mcp.edufeed.org/mcp"}}}{"mcpServers":{"org-edufeed-nope":{"serverUrl":"https://mcp.edufeed.org/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
18| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | number | 否 | 结果数量上限(1–250,默认值:20) |
| query | string | 否 | 自由文本搜索查询(例如:"mathematik"、"machine learning") |
| since | number | 否 | 返回创建时间不早于此 Unix 时间戳的资源 |
| until | number | 否 | 返回创建时间不晚于此 Unix 时间戳的资源 |
| relays | array | 否 | 将搜索范围限制为指定的中继服务。仅接受 list_relays 返回的中继服务(默认或额外),可使用完整 URL 或短名称(例如:"oersi"、"sodix")。默认值:默认中继服务集合。 |
| authors | array | 否 | 按作者公钥筛选(十六进制格式) |
| language | string | 否 | 标签筛选所用语言(默认值:"de") |
| creatorName | string | 否 | 按创建者/作者姓名筛选——与 AMB 元数据进行完整字符串精确匹配(不区分大小写)。不确定拼写?请先调用 resolve_publisher。 |
| subjectLabel | string | 否 | 按学科/主题标签筛选——标签必须完全匹配(例如:"Mathematik"、"Physik");有效标签请查看 browse_subjects |
| publisherName | string | 否 | 按发布者名称筛选——与 AMB 元数据进行完整字符串精确匹配(不区分大小写),例如:"e-teaching.org" 或 "LEHRE LADEN"(不是 "Lehreladen")。不确定拼写?请先调用 resolve_publisher。 |
| resourceTypeLabel | string | 否 | 按资源类型标签筛选——标签必须完全匹配(例如:"Video"、"Kurs"、"Arbeitsblatt");有效标签请查看 browse_resource_types |
| educationalLevelLabel | string | 否 | 按教育阶段筛选——标签必须完全匹配(例如:"Sekundarstufe I"、"Hochschule");有效标签请查看 browse_educational_levels |
详细介绍
nope-mcp(原名 amb-mcp)
一个 MCP(Model Context Protocol)服务,用于从 AMB(Allgemeines Metadatenprofil für Bildungsressourcen)Nostr 中继查询教育资源。
仓库
权威仓库位于 Nostr(NIP-34)上——可在 gitworkshop.dev 浏览,或使用 ngit 远程辅助工具克隆:
git clone nostr://laoc.xyz/relay.ngit.dev/amb-mcp
镜像:git.edufeed.org/edufeed/nope-mcp · github.com/edufeed-org/nope-mcp。欢迎在这三个仓库中的任意一个提交问题和 PR——Nostr PR 会以 pr/* 分支的形式到达。
版本发布均带有标签(v0.1.0、……),并列于 CHANGELOG.md。
MCP Registry
server.json 是 nope-mcp 发布到官方 MCP Registry的清单,名称为 org.edufeed/nope-mcp,指向 https://mcp.edufeed.org/mcp 远程服务。其 version 始终与 package.json 保持一致(由 test/server-json.test.ts 强制校验)。
发布使用 Registry 的 mcp-publisher CLI,在通过 DNS 验证域名 edufeed.org 后进行(Registry 通过 TXT 记录证明 org.edufeed 命名空间的所有权,而非 CLI 持有的密钥)——无需管理密钥,也没有任何命令会嵌入密钥。
功能
查询与浏览
- 跨内容全文搜索(
search_content),通过一次排序调用搜索教育资源、长篇文章、维基、transferkiosk 项目/措施以及 NKBIP-01 科学出版物 - 基于语料全文检索相关段落(
search_passages),并附带引用;可按 grimoire 咒语(kind 777)或内联范围限定;需要索引器 - 中继启用重新排序时,每个结果会展示经分块重新排序得到的语义片段段落
- 使用 NIP-50 进行全文搜索
- 按发布者、创作者、主题、资源类型、教育阶段筛选
- 浏览可用主题、资源类型和教育阶段
- 将作者/机构名称解析为 pubkey(
resolve_author),以便按作者限定查询 - 支持按时间、地理哈希和主题标签筛选的 NIP-52 日历事件搜索
- SKOS 受控词表查找与搜索(
skos_*) - 按标识符获取单项资源
- 中继统计信息与详情
URL → 表单预填元数据
extract_metadata(url, variant, skosSchemes?)— 获取公开网页并生成完整的 AMB/EKW 表单预填数据。默认返回 OpenGraph 回退数据;设置ANTHROPIC_API_KEY后,由基于已配置 SKOS 词表的大模型为 SKOS 类型字段填入概念 ID 和逐字段证据引文。- 库导出:可通过
import { extractMetadata } from 'nope-mcp/lib'在进程内直接使用(例如用于 SvelteKit 服务端路由)。
签名与发布
- 通过 QR 码连接流程进行 NIP-46 远程签名(bunker)
- 对任意 Nostr 事件进行签名并发布
- 创建并发布 kind 0(个人资料/元数据)事件
- 创建并发布 kind 30142(AMB 教育资源)事件
- 支持 NIP-42 中继身份验证
- 多用户会话隔离
安装
bun install
配置
将 .env.example 复制为 .env 并进行配置:
cp .env.example .env
环境变量
| 名称 | 使用方 | 默认值 | 说明 |
|---|---|---|---|
AMB_RELAYS | 所有传输方式 | wss://relay.edufeed.org | 以逗号分隔的 AMB 中继 URL。每个内容工具(search_content、search_resources、get_resource、browse_*、resolve_author、relay_stats)都会查询这些中继,并将其用作签名工具的默认发布目标。 |
AMB_EXTRA_RELAYS | 所有传输方式 | (empty) | 以逗号分隔的 AMB 中继 URL,这些中继可供选择,但默认不会搜索。search_content、search_resources 和 get_resource 接受 relays 参数,用于指定来自 AMB_RELAYS ∪ AMB_EXTRA_RELAYS 的中继(其他值会被拒绝);list_relays 会将这两组分别列为 defaultRelays/extraRelays。可用此设置按需提供其他内容库(例如 OERSI 和 SODIX 聚合中继 wss://oersi.edufeed.org,wss://sodix.edufeed.org),而无需将它们并入每次搜索。通过 HTTP 使用时,此列表也会填充 ?relays= 连接器参数——请参阅选择连接器的默认中继。 |
AMB_AUTHOR_SETS | 所有传输方式 | (empty) | 以逗号分隔的 NIP-51 关注列表(kind 30000)naddr 标识符。启动时会加载一次,并放入由 list_known_authors 提供的作者目录;随后可将返回的公钥作为 authors 传给 search_* 工具。 |
CALENDAR_RELAYS | 所有传输方式 | wss://relay.edufeed.org | 以逗号分隔的 NIP-52 日历中继 URL,供 search_calendar_events 使用。AMB 中继本身也提供日历事件,因此默认值为同一中继;仅在使用专用日历中继的拆分部署中设置此项。 |
CALENDAR_AUTHOR_SETS | 所有传输方式 | (empty) | 与 AMB_AUTHOR_SETS 相同,但用于由 list_calendar_authors 提供的日历作者目录。 |
INDEXER_ENDPOINTS | search_passages | (unset — tool disabled) | 以逗号分隔的 wss://relay=https://indexer 键值对,将每个 AMB 中继映射到其 amb-indexer 基础 URL。启用 search_passages。 |
INDEXER_API_TOKEN | search_passages | (unset) | 索引器 /search_chunks 的 Bearer 令牌(所有端点共用默认值)。 |
INDEXER_API_TOKENS | search_passages | (unset) | 按中继覆盖令牌,以逗号分隔的 wss://relay=token 键值对——每个已部署的索引器实例都有自己的令牌。每个 INDEXER_ENDPOINTS 条目都必须由此项或 INDEXER_API_TOKEN 提供令牌;令牌配置不完整时,启动会失败。 |
OPEN_LICENSES_ONLY | 所有传输方式 | true | 搜索结果(search_resources、search_content、search_passages)仅包含采用开放许可的学习资源:CC0、Public Domain Mark、CC BY、CC BY-SA。其他内容类型不带许可信息,因此始终包含;get_resource 不会筛选资源,但会报告 openLicense。设为 false(或 0/no/off)则返回所有资源。 |
SPELL_RELAYS | search_passages | wss://relay.edufeed.org | 用于获取 kind-777 spells(以及 kind-3 联系人列表)的中继。 |
LOG_LEVEL | 所有传输方式 | info | 在 stderr 中记录每次工具调用(stdout 留给 stdio 传输使用)。每次调用都会输出一行 JSON:{"ts":"2026-09-14T12:00:00.000Z","tool":"search_passages","ms":412,"ok":true,"session":"<id>","args":{"question":"…","kinds":[30142]}}——对于 isError 结果、抛出的错误,以及 JSON 正文包含 error 代码的文本结果(例如 relay_unreachable 这类带类型的失败),ok 为 false;此时 error 保存错误消息或代码;args 中的字符串会截断至 120 个字符,超过五项的数组会折叠为 {length, head}。info(默认值)会记录每次调用;warn/error 仅记录失败;silent 则禁用这些日志行。 |
EDUFEED_APP_BASE_URL | 所有传输方式 | (unset) | 前端基础 URL(末尾不带斜杠,例如 https://app.edufeed.org)。设置后,search_content、search_resources、get_resource 和 search_calendar_events 的结果会包含 url 字段,指向 edufeed-app 查看器页面(<base>/<naddr>),以便大模型客户端呈现直接链接。未设置时不包含 url 字段。 |
SERVER_PRIVATE_KEY | src/index.ts(+ 发现脚本) | Nostr 传输必填 | Nostr 私钥(nsec 或十六进制),即服务器自身的 ContextVM 身份。派生出的公钥是客户端通过 cvmi use <pubkey> 连接时使用的公钥。stdio 或 HTTP 传输不会读取此项。 |
RELAYS | src/index.ts(+ 发现脚本) | wss://relay.contextvm.org、wss://cvm.otherstuff.ai | 以逗号分隔的中继 URL,用于 ContextVM 传输公告和请求/响应流量。stdio 或 HTTP 传输不会读取此项。 |
ANTHROPIC_API_KEY | extract_metadata | (unset) | 启用基于大模型的 SKOS 字段提取。未设置时,工具会平稳降级,仅输出 OpenGraph/JSON-LD 数据。 |
ANTHROPIC_MODEL | extract_metadata | claude-sonnet-4-6 | 覆盖用于提取的 Anthropic 模型。 |
SKOS_SCHEMES | extract_metadata | (unset) | JSON 映射 { "<form-field>": "<scheme-uri>" },当调用方未明确传入 skosSchemes 时用作默认词表。 |
VOCAB_RELAYS | extract_metadata | 回退为 AMB_RELAYS | 用于将 naddr1… SKOS 方案标识符解析为由中继托管的词表的中继。 |
SCHEME_NADDR_* | extract_metadata | (unset) | 按词表设置的 naddr 覆盖项(例如 SCHEME_NADDR_HCRT、SCHEME_NADDR_SCHULFAECHER),将常见方案 URI 映射到由中继托管的 SKOS 词表。完整列表请参阅 .env.example。 |
HTTP_*、OAUTH_* | 仅 src/http.ts | 见下文 | HTTP 绑定和 OAuth 资源服务器设置——详见选项 4。 |
EMBED_TOKEN | 仅 docker-compose.yml | (unset) | 随附的本地测试中继所用嵌入服务的令牌。服务器本身不会读取此项。 |
使用方法
选项 1:添加到 Claude Code(推荐)
claude mcp add nope-mcp -- bun run /path/to/nope-mcp/src/stdio.ts
此方法使用默认的公共中继(wss://relay.edufeed.org)。若要连接到其他中继(例如 开发 中的本地 Docker 中继),请添加 -e AMB_RELAYS=ws://localhost:3337。
选项 2:使用 cvmi(ContextVM)运行
启动服务:
cvmi serve -- bun run src/stdio.ts
从另一台机器连接:
cvmi use <server-pubkey>
选项 3:使用 Nostr 传输独立运行
bun run src/index.ts
选项 4:使用 Streamable HTTP 传输运行
适用于基于 Web 的 MCP 客户端(Claude.ai 连接器、MCP Inspector、自定义浏览器应用):
bun run src/http.ts # dev
node dist/http.js # production (after `npm run build`)
| 变量 | 默认值 | 描述 |
|---|---|---|
HTTP_PORT | 3000 | 绑定端口。 |
HTTP_HOST | 0.0.0.0 | 绑定主机。使用 127.0.0.1 可将其限制为本地代理。 |
HTTP_ALLOWED_HOSTS | (unset) | 以逗号分隔的 Host 允许列表。设置后可启用 DNS 重绑定防护。 |
HTTP_ALLOWED_ORIGINS | (unset) | 以逗号分隔的 Origin 允许列表。 |
OAUTH_ISSUER | https://auth.edufeed.org/realms/edufeed | 接受其令牌的 OIDC 颁发者。 |
OAUTH_AUDIENCE | nope-mcp,amb-mcp | 以逗号分隔的接受受众;令牌的 aud 包含其中任意一个即为有效。 |
OAUTH_JWKS_URI | <issuer>/protocol/openid-connect/certs | 用于验证令牌签名的 JWKS 端点。 |
OAUTH_RESOURCE_URL | https://mcp.amb.edufeed.org/mcp | 对于不在 HTTP_ALLOWED_HOSTS 中的主机,使用此备用 PRM 资源。否则,将按请求派生资源(RFC 9728):/.well-known/oauth-protected-resource → https://<host>/,/.well-known/oauth-protected-resource/mcp → https://<host>/mcp。 |
身份验证模型: HTTP 传输是一个 OAuth 2.0 资源服务。
- 不带
Authorization标头的请求会获得匿名的只读会话(mcp:read):搜索、获取、浏览、解析、SKOS 查找。 - 带有有效 JWT 的请求(由
OAUTH_ISSUER针对OAUTH_AUDIENCE中的某个受众颁发)将获得令牌中的作用域:mcp:read和/或mcp:extract(会消耗预算的extract_metadata工具)。无效令牌将被拒绝并返回 401。 - HTTP 不会公开写入/签名工具——这些工具仅在 stdio 和 Nostr 传输中可用。作用域不足时,该工具不会出现在
tools/list中。
服务公开以下端点:
POST /和POST /mcp— JSON-RPC 请求(初始化、工具调用等),共用同一会话映射GET /和GET /mcp— 当前会话的服务器推送 SSE 流GET /(普通浏览器请求——无Mcp-Session-Id,无Accept: text/event-stream)— 返回简短的 JSON 信息文档(name、version、mcp、docs、transport),而非 MCP 404;GET /mcp始终作为 MCP 会话请求处理DELETE /和DELETE /mcp— 终止当前会话GET /.well-known/oauth-protected-resource/GET /.well-known/oauth-protected-resource/mcp— 分别为/和/mcp提供 RFC 9728 受保护资源元数据,并根据主机进行处理GET /healthz— 无需身份验证的存活探测
使用 curl 进行握手的示例:
# 1. initialize (anonymous = read-only session), capture the Mcp-Session-Id response header
curl -i http://localhost:3000/mcp -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
# 2. reuse the session id for tools/list, tools/call, etc.
curl http://localhost:3000/mcp -X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'Mcp-Session-Id: <id-from-step-1>' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# To use extract_metadata, add an OAuth token to the *initialize* request:
# -H "Authorization: Bearer $ACCESS_TOKEN"
# (scopes are fixed at session init; a token on later requests does not upgrade the session)
使用 MCP Inspector 进行冒烟测试:
npx @modelcontextprotocol/inspector http://localhost:3000/mcp
选择连接器的默认中继
大多数连接器 UI(例如 Claude.ai 的“添加自定义连接器”)都会提供两个 字段:名称和 URL。因此,URL 用于指定连接默认要搜索哪些中继:
https://mcp.edufeed.org/mcp?relays=sodix
https://mcp.edufeed.org/mcp?relays=amb-relay,sodix,oersi
(之前的主机 https://mcp.amb.edufeed.org/mcp 仍可照常使用——两者接受相同的 ?relays= 语法。)
?relays= 中指定的中继会成为该会话的默认集合——每次调用 search_content / search_resources 时都会搜索这些中继。部署所提供的其他中继则留在额外集合中:list_relays 仍会列出它们,工具调用也仍可通过自己的 relays 参数访问它们。不带该参数时,会话使用服务器配置的默认集合,与之前完全相同。
名称会根据部署已提供的中继解析(AMB_RELAYS ∪ AMB_EXTRA_RELAYS)。每个中继支持三种形式:
| 形式 | 示例 |
|---|---|
| 完整 URL | wss://sodix.edufeed.org |
| 主机名 | sodix.edufeed.org |
| 主机名的第一个标签 | sodix |
如果两个中继使用了同一个短标签,系统会弃用该标签,而不会自行猜测——请改用主机名。名称因部署而异:在普通会话中调用 list_relays 会准确显示服务器提供的中继;如果 ?relays= 中有名称被拒绝,响应会列出所有可接受的名称。部署未提供的名称会导致 initialize 请求返回 HTTP 400 和 JSON-RPC 错误,并列出服务器确实接受的名称;连接绝不会悄悄指向服务器默认设置。不接受任意中继 URL,因此无法利用公共端点让服务器向调用方任意指定的主机建立 WebSocket 连接。
在这样的会话中,list_relays 会报告 defaultRelaysSource: "connector-url",模型据此可以判断语料库是经过刻意缩小的,而不是部署的标准语料库。
公共部署
托管实例位于:
https://mcp.edufeed.org/mcp
之前的 URL https://mcp.amb.edufeed.org/mcp 仍然有效,并指向同一部署。
该实例提供三个中继——amb-relay(wss://amb-relay.edufeed.org,默认中继),以及作为按次调用额外中继的 oersi 和 sodix——因此,…/mcp?relays=sodix 会创建一个默认搜索 SODIX 语料库的连接器。请参阅选择连接器的默认中继。
它使用与本地服务器相同的可流式 HTTP 协议。读取工具可公开使用——不带 Authorization 标头的请求会获得只读会话(搜索/获取/浏览/解析)。会消耗预算的 extract_metadata 工具需要有效的 OAuth 令牌,且令牌必须包含 mcp:extract 范围;令牌由 Keycloak realm 通过带外方式签发——请联系运营方。除此之外,握手过程与上面的 curl 示例相同;只需替换 URL,并在只读使用时去掉 Authorization 标头。
公共端点会在边缘层(Traefik)按来源 IP 限流——尤其是 search_passages,它会在栈内 CPU 服务上为每个查询生成嵌入,因此突发请求会受到节流。正常的交互式使用不受影响。
可用工具
工具分为三个配置组:
- 读取——搜索、获取、浏览、解析、SKOS 查询和日历。所有传输方式均可使用;通过 HTTP 匿名提供。
- 提取——
extract_metadata。通过 HTTP 使用时,需要带有mcp:extract范围的 OAuth 令牌。 - 写入——签名、发布、中继管理和 SKOS 词汇表构建工具。仅通过 stdio 和 Nostr 传输方式提供,绝不通过 HTTP 提供。
search_content
在一次排序调用中搜索所有内容类型的主题 — 教育资源(30142)、长篇文章(30023)、Wiki(30818)、transferkiosk 项目(30143)、transferkiosk 措施(30144)以及 NKBIP-01 科学出版物(30040 索引 + 30041 章节 — 学术文章、书籍)。
结果交错排列,并按语义段落匹配度排序;当中继的分块重排功能启用时,每条结果都会附带匹配的段落(snippet)。
发现内容与提问意图。 当 search_passages 可用时(索引器已配置),这两个工具按意图区分:search_content 用于发现 — 用户想浏览材料(“寻找/推荐有关 X 的材料”),你应将这些内容以链接形式呈现。对于希望从来源中获得解答的问题(“关于 X,如何/为什么/什么有帮助?”),应改用 search_passages。索引器未配置时,search_content 是自然语言问题的默认入口。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
query | string | 自由文本主题 |
types | string[] | ["resource","article","wiki","project","measure","publication"] 的子集(默认:全部) |
language | string | 标签语言(默认 de) |
since / until | number | Unix 时间戳范围 |
authors | string[] | 作者公钥(十六进制) |
limit | number | 结果上限,1-250(默认 20) |
community | string | 返回分享至此社区的内容(十六进制公钥或 npub) |
relays | string[] | 限定特定中继 — 可使用完整 URL 或简称(oersi、sodix)。参见 list_relays。 |
每条结果:{ type, kind, title, url?, naddr?, snippet?, score?, ...type-specific }。
如需查找同一主题的近期活动,请继续调用 search_calendar_events。
如果所选中继超时或无法访问,响应会添加 relaysIncomplete(受影响的中继)和 warning — 在此警告下,结果为空或较少并不能证明内容库为空。若没有这些字段,则表示所有中继均已响应。
search_passages
有依据的 RAG:为问题检索最匹配的全文段落,并附上引用(来源资源、页码、标题、来源 URL)返回——根据这些段落回答用户,并引用每个来源。这是处理疑问句查询时的默认工具;探索内容请使用 search_content。需要索引器(INDEXER_ENDPOINTS);如果未设置该配置,此工具不会注册。
范围为必填项(此工具绝不会在未指定范围的情况下执行搜索),但通常很简单——如果不限制来源,请传入内容类型。限制条件会映射到范围:元数据中的发布者(resolve_publisher 可查找准确拼写)映射到 search,作为带引号的字段筛选器(publisher.name:"LEHRE LADEN");Nostr 签名者(resolve_author)映射到 authors。范围也可以来自已发布的 grimoire 咒语(kind 777),通过 nevent/事件 id 传入;每个响应都会回显该范围对应的规范咒语,以便发布和重复使用。咒语可以使用 $me/$contacts,它们会解析为调用者(如果传输方式为匿名,请传入 me)。
排序。 索引器运行 Typesense 混合搜索;search_passages 请求向量权重 alpha: 0.7(索引器自身默认值为偏重关键词的 0.3,该值保留给中继的重排路径),因此语义排序优先,套话关键词匹配(“GRUNDSCHULE”、“Kinder”)不再占据优势。它会超量获取 min(limit × 3, 100) 个分块,然后每份文档(event_coord)最多保留两段,丢弃低于最高分 10 % 的命中,并返回前 limit 段。请将 question 写成点明主题和目标群体的主题陈述(“Friedenserziehung in der Grundschule: Einstieg in das Thema Frieden mit Kindern”),而不要照搬用户的原句(“Wie kann ich …?”)。只有摘要而没有正文的段落,可能是受到许可限制,也可能是尚未建立全文索引。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
question | 字符串 | 要查找依据段落的问题/主题 |
spell | 字符串 | 已发布的咒语:nevent、note id 或 64 位十六进制事件 id(可替代内联范围) |
authors | 字符串[] | 内联范围:签名者公钥(hex/npub/$me/$contacts) |
kinds | 数字[] | 内联范围:内容类型(例如 [30142]) |
tag | 对象 | 内联范围:单个标签筛选器,{ letter, values } |
search | 字符串 | 内联范围:用于选择范围内事件的 NIP-50 词项(多词字段值需加引号) |
since / until | 字符串 | 内联范围:绝对 Unix 秒数或相对值(7d、1mo、now) |
me | 字符串 | $me 所指对象(npub/hex);默认为调用者身份 |
relays | 字符串[] | 中继选择(完整 URL 或短名称);使用映射到的第一个中继 |
limit | 数字 | 返回的段落数,1-25(默认 10) |
失败会明确返回,绝不会静默执行无范围搜索:empty_scope(中继已响应,但没有匹配项)、relay_unreachable(内容中继未响应——请重试)、spell_not_found、no_indexer、indexer_error。
**查询中的方面筛选语法:**中继端的方面筛选通过 NIP-50 字段筛选器放在 query 中,而不是作为独立参数——将它们附加到自由文本词项后,中继会在服务端解析。示例:type:academic(出版物显示类型)、doi:10.1234/abcd.5678(裸 DOI,存储值不带 doi: 前缀)、keywords:<term>(主题词)或 partOf:30143:<pubkey>:<d>(属于指定项目 coord 的出版物/措施)。这些筛选条件可以与主题词组合,例如 query: "seminardidaktik partOf:30143:<pubkey>:<d>"。
search_resources
使用全文搜索和元数据筛选条件搜索教育资源。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
query | string | 自由文本搜索查询 |
publisherName | string | 按发布者名称筛选 |
creatorName | string | 按创作者/作者名称筛选 |
subjectLabel | string | 按学科标签筛选(例如,“Mathematik”) |
resourceTypeLabel | string | 按资源类型筛选(例如,“Video”“Kurs”) |
educationalLevelLabel | string | 按教育阶段筛选 |
language | string | 标签语言(默认值:“de”) |
authors | string[] | 按作者公钥筛选(十六进制)——例如,来自 resolve_author 或 list_known_authors |
since / until | number | 资源创建时间的 Unix 时间戳范围 |
limit | number | 最大结果数,1-250(默认值:20) |
relays | string[] | 限定特定中继——可使用完整 URL 或简称(oersi、sodix) |
subjectLabel / resourceTypeLabel / educationalLevelLabel 筛选条件会进行
精确标签匹配(不区分大小写),而非自由文本匹配——不在存储标签中的短语会返回 0 条结果。请将主题词放在 query 中;使用 browse_*
查找有效标签。publisherName / creatorName 也会进行精确匹配,但
在没有匹配结果时会提供相近拼写建议(actorCandidates)。
get_resource
通过 naddr、d-tag 标识符或事件 ID 检索单个内容。
任何 search_content 结果中的 naddr 均可解析——资源、文章、
wiki、项目、措施和出版物皆可;非资源类型会以与其 search_content 结果相同的格式返回({ type, kind, title, ... }),而非完整的 AMB 资源格式。不带 naddr 的纯 identifier/eventId
查询始终会解析完整的教育资源元数据
(kind 30142),包括创作者/发布者和教育属性。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
identifier | string | 资源 d-tag(URL 标识符)——仅解析 kind 30142 |
author | string | 用于消歧的作者公钥 |
eventId | string | 直接查询 Nostr 事件 ID——仅解析 kind 30142 |
naddr | string | 来自 search_content 结果的 NIP-19 naddr——首选;适用于任何内容类型 |
响应格式(search_resources 和 get_resource)
每个返回的资源都包含标准 AMB 字段,以及:
nostr.naddr— NIP-19 可寻址标识符(kind=30142、公钥、d-tag)。适用于任何 Nostr 客户端。url— 指向该资源 edufeed-app 页面的直接链接。仅在配置了EDUFEED_APP_BASE_URL时存在。 LLM 客户端推荐资源时应将此链接作为 Markdown 链接([name](url))引用,以便用户打开。
当选定的中继超时或无法访问时,search_content、search_resources 和 search_calendar_events 会添加
relaysIncomplete 和 warning——出现此警告时,空结果并不能证明语料库为空,因此请告知用户某个来源无法访问,并提出重试。当
所有中继均有响应时,不会出现这两个字段。
resolve_author
使用中继的 kind-0 作者个人资料索引(NIP-50 搜索),将组织或个人名称
解析为候选公钥。对于按名称查询的问题(“Jörg Lohrer 最近发表的文章”),请从此处开始:解析名称,选出最佳候选,然后将其公钥传给 search_content({ authors: [pubkey] })
或 search_calendar_events。最多返回 limit 个按相关性排序的候选项。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
name | string | 要解析的组织或个人名称 |
limit | number | 候选项上限,1-25(默认值 10) |
list_known_authors
列出从已配置的关注集合(NIP-51 kind 30000,参见 AMB_AUTHOR_SETS)中载入的
已知教育资源作者。返回姓名、公钥和 NIP-05
标识符。此功能不同于 resolve_author,后者搜索的是整个中继的
个人资料索引,而非人工整理的集合。
browse_subjects
列出可用的主题及其资源数量。
browse_resource_types
列出可用的学习资源类型(视频、课程、练习单等)。
browse_educational_levels
列出可用的教育阶段(小学、中学、高等教育等)。
relay_stats
获取中继信息,包括名称、描述和支持的 NIP。
list_relays
列出当前会话配置的所有 AMB 中继,并分为
defaultRelays(每次查询都会搜索)和 extraRelays(可按次调用选择)。relays 参数(用于 search_content、search_resources、
get_resource、search_passages)可通过完整
URL 或短名称指定任一组中的中继——主机名或首个标签(oersi、sodix、
amb-relay),不区分大小写;如果两个中继具有相同的标签,则只会通过较长的形式解析。defaultRelaysSource 指明默认集合由谁选择——
server-config;如果连接 URL 通过 ?relays= 指定,则为 connector-url。写入配置
还提供 add_relay / remove_relay,可在运行时调整会话的中继
集合(HTTP 下按会话生效;stdio 下按进程生效)。
relay_list_get
获取用户的 NIP-65 中继列表(kind 10002)。响应格式请参阅下方的 NIP-65 Outbox Model。
SKOS 词表工具
用于读取托管为 Nostr 事件或通过 URI 引用的受控词表的工具:
skos_list_vocabularies— 列出服务器已知的词表skos_get_vocabulary/skos_get_vocabulary_status— 获取包含概念的方案 / 检查可用性skos_get_concept— 获取包含标签和关系的单个概念skos_search— 按标签跨词表搜索概念
写入配置还提供一套词表构建工具(skos_create_vocabulary、
skos_add_concept、skos_update_concept、skos_remove_concept、
skos_set_relationship、skos_add_mapping、skos_import_turtle、
skos_export_turtle、skos_delete_vocabulary),用于编写 SKOS 词表
并将其发布为 Nostr 事件。
extract_metadata
获取一个或多个公开网页(或 PDF),并生成 AMB/EKW 表单预填
载荷。默认返回 OpenGraph/JSON-LD 回退结果;设置 ANTHROPIC_API_KEY
后,基于配置的 SKOS 词表的大模型会填充 SKOS 类型的字段,并提供每个字段对应的证据引文。也可作为库导出使用:
import { extractMetadata } from 'nope-mcp/lib'。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
url | string | 要提取内容的公开 http(s) URL(多个来源请使用 urls) |
urls | string[] | 合并到一次提取中的多个来源 URL(例如多个 PDF) |
variant | string | 表单变体:amb(默认)、ekw(添加宗教教育字段)、konfi(添加 Konfi-Arbeit 字段) |
skosSchemes | object | 表单字段 → SKOS 方案 URI 的映射,可覆盖 SKOS_SCHEMES |
抓取过程会防范 SSRF(私有/回环地址段会被阻止)。
search_calendar_events
搜索 NIP-52 日历事件(基于日期的 31922、基于时间的 31923)。支持 时间筛选、地理哈希位置筛选和话题标签筛选。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
query | string | 自由文本主题。**注意:**与时间/地理范围筛选结合使用时,中继会在服务器端优先处理范围并忽略此字段——对于“下周关于 X 的事件”,请传入时间范围,并在客户端按主题筛选返回的事件。 |
startAfter / startBefore | number | 事件开始时间的 Unix 时间戳范围 |
endAfter / endBefore | number | 事件结束时间的 Unix 时间戳范围 |
geohash | string | 用于基于位置搜索的地理哈希前缀 |
hashtags | string[] | 按话题标签筛选 |
authors | string[] | 作者公钥(十六进制) |
kinds | number[] | 要查询的事件类型(默认值:[31922, 31923]) |
since / until | number | 事件创建时间的 Unix 时间戳范围 |
limit | number | 结果上限,1-250(默认值:20) |
每个事件都包含 naddr(NIP-19 可寻址标识符);设置
EDUFEED_APP_BASE_URL 后,还会包含 url(指向 <base>/<naddr> 的 edufeed-app 查看器)。
请优先引用 url 而非 sourcePage,因为查看器展示的事件详情更完整。
list_calendar_authors
列出从已配置的关注集合(NIP-51 kind 30000)中加载的已知日历事件作者。
返回作者名称、公钥和 NIP-05 标识符。使用返回的公钥,通过
search_calendar_events(authors: [...]) 按作者筛选事件。
Signing and Publishing
MCP 服务支持通过 NIP-46 远程签名(bunker)对 Nostr 事件进行签名和发布。
Connecting a Signer
Option 1: QR Code Flow (Recommended)
- 调用
signer_init生成 nostrconnect:// URL 和二维码 - 使用你的 bunker 应用(Amber、nsecBunker 等)扫描二维码
- 调用
signer_await等待连接完成
Option 2: Bunker URL
如果你的签名器应用提供了 bunker:// URL,可直接使用 signer_connect。
Option 3: Private Key (Development Only)
测试时,可使用 nsec 和 allowInsecure=true 调用 signer_connect。切勿在生产环境中使用此方式。
Signer Tools
signer_init
生成用于连接签名器应用的二维码。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
relays | string[] | 用于 NIP-46 通信的中继(默认为 AMB 中继) |
name | string | 在 bunker 应用中显示的客户端名称 |
permissions | string[] | 请求的权限(例如,["sign_event:0", "sign_event:30142"]) |
返回:
sessionId- 用于signer_await的会话 IDnostrconnectUrl- nostrconnect:// URLqrCode- 用于在终端显示的 ASCII 二维码
signer_await
扫描二维码后,等待 bunker 应用连接。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
sessionId | string | 来自 signer_init 的会话 ID |
timeout | number | 超时时间(秒,默认值:120) |
signer_connect
使用 bunker URL 或私钥直接连接。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
bunkerUrl | string | 来自你的签名器应用的 bunker:// URL |
nsec | string | 私钥(nsec 或十六进制)- 仅限开发环境 |
allowInsecure | boolean | 使用 nsec 时必需 |
signer_disconnect
断开当前签名器会话。
signer_status
检查当前签名器的连接状态。
返回:
connected- 是否已连接签名器userPubkey- 已连接用户的公钥connectedAt- 连接时间的 ISO 时间戳
Publishing Tools
sign_event
使用已连接的签名器为未签名的 Nostr 事件签名。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
kind | number | 事件类型(例如,0 表示元数据,1 表示笔记) |
content | string | 事件内容 |
tags | string[][] | 以数组嵌套数组形式表示的事件标签 |
publish_event
将预签名的 Nostr 事件发布到中继。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
event | object | 已签名的 Nostr 事件,包含 id、pubkey、created_at、kind、tags、content、sig |
relays | string[] | 要发布到的中继(默认为 AMB 中继) |
useOutbox | boolean | 使用 NIP-65 发件箱模型选择中继(默认:true) |
create_and_publish_metadata
构建、签名并发布 kind 0 配置文件元数据事件。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
name | string | 显示名称 |
about | string | 个人简介/描述 |
picture | string | 头像 URL |
banner | string | 横幅图片 URL |
nip05 | string | NIP-05 标识符(例如,[email protected]) |
lud16 | string | Lightning 地址 |
website | string | 网站 URL |
relays | string[] | 要发布到的中继 |
useOutbox | boolean | 使用 NIP-65 发件箱模型选择中继(默认:true) |
create_and_publish_resource
构建、签名并发布 kind 30142 AMB 教育资源事件。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
identifier | string | 资源的唯一标识符(d-tag) |
name | string | 资源名称/标题 |
description | string | 资源描述 |
url | string | 资源 URL |
image | string | 图片/缩略图 URL |
type | string[] | 资源类型(例如,["LearningResource", "VideoObject"]) |
inLanguage | string[] | 语言代码(例如,["de", "en"]) |
about | object[] | 主题概念,包含 id 和可选的 prefLabel |
learningResourceType | object[] | 来自 HCRT 词汇表的学习资源类型 |
educationalLevel | object[] | 教育级别概念 |
creator | object[] | 内容创作者,包含 name、type、id |
publisher | object[] | 发布者,包含 name、type、id |
license | object | 许可证,包含 id(URL)和可选的 name |
isAccessibleForFree | boolean | 资源是否可免费访问 |
datePublished | string | 发布日期(ISO 8601) |
relays | string[] | 要发布到的中继 |
useOutbox | boolean | 使用 NIP-65 发件箱模型选择中继(默认:true) |
NIP-65 发件箱模型
发布工具默认使用 NIP-65 发件箱模型,以智能选择中继:
- 作者的写入中继 - 从 kind 10002 事件中获取
- 被标记用户的读取中继 - 对于带有 p 标签的提及,获取其读取中继
- 默认中继 - 回退到配置的 AMB 中继
这可确保事件发送到作者发布事件的中继,以及被标记用户预期接收事件的中继。
relay_list_get
获取用户的 NIP-65 中继列表(kind 10002)。
参数:
| 名称 | 类型 | 描述 |
|---|---|---|
pubkey | string | 要获取中继列表的公钥(默认为已连接的签名器) |
返回:
pubkey- 查询的公钥readRelays- 标记为读取的中继 URL 数组writeRelays- 标记为写入的中继 URL 数组totalRead- 读取中继的数量totalWrite- 写入中继的数量
可用资源
| URI | 描述 |
|---|---|
amb://schema | AMB JSON-LD 架构 |
amb://vocabularies/subjects | 主题词汇表 |
amb://vocabularies/resource-types | 学习资源类型词汇表 |
amb://vocabularies/educational-levels | 教育级别词汇表 |
amb://relay-info | NIP-11 中继信息 |
部署
该服务有三个入口点;请选择与客户端匹配的入口:
src/index.ts(默认CMD)——Nostr/ContextVM 传输。无 HTTP 端口。客户端通过在配置的 ContextVMRELAYS上指定其公钥来连接。需要SERVER_PRIVATE_KEY。src/stdio.ts——用于cvmi serve和 Claude Code 本地子进程的 stdio 传输。src/http.ts——在HTTP_PORT(默认3000)上提供 Streamable HTTP 传输。适用于基于 Web 的 MCP 客户端。有关环境变量和握手,请参阅上文的 选项 4。
主机上的先决条件
- 运行时需要 Node ≥ 20(或 Bun ≥ 1.1)。
- 可通过出站 WebSocket 访问已配置的 AMB 和 ContextVM 中继。
extract_metadata工具需要出站 HTTPS(目标页面,以及可选的 Anthropic API)。- 安装期间需要访问
git.edufeed.org网络——amb-nostr-converter依赖项从 edufeed npm 注册表获取已发布的 tarball(参见package.json)。
构建和运行
bun install # or: npm install
bun run build # tsc -> dist/
node dist/index.js # production entry; uses Nostr transport
无需构建步骤的开发运行方式:bun run src/index.ts。
身份和密钥
SERVER_PRIVATE_KEY是服务持久的 Nostr 身份。丢失或轮换此密钥会改变客户端用于连接服务的公钥,因此应将其视为长期状态。使用nak key generate(或任何 Nostr 密钥生成工具)生成密钥,并通过密钥管理器存储——切勿提交该密钥。- 对应的公钥是用户传给
cvmi use <pubkey>的值。首次启动后打印一次,以便运维人员记录。 - 如果使用
ANTHROPIC_API_KEY,应将其权限范围限定为此服务;每次调用extract_metadata工具并请求 SKOS grounding 时,都会消耗令牌。
状态和持久化
服务本身不会在磁盘上存储状态——所有状态都保存在已配置的中继上。重启后唯一需要保留的是包含 SERVER_PRIVATE_KEY 的环境变量文件。MCP 容器无需挂载卷。
发现
使用 Nostr 传输启动时,服务会向 RELAYS 发布 ContextVM 公告。要移除旧公告(例如轮换密钥或停用服务后),请使用 scripts/unpublish-server.ts。
运维说明
- **日志记录:**纯 stdout/stderr。通过进程管理器(systemd journal、Docker logs 等)捕获。
- **健康检查:**Nostr 和 stdio 入口没有 HTTP 健康检查——存活状态约等于“进程正在运行,且中继订阅未发生错误”,请在进程管理器层面集成检查。HTTP 入口提供未经过身份验证的
GET /healthz,供探测使用。 - **
extract_metadata出站访问:**该工具会获取调用方提供的任意 URL。获取过程会考虑 SSRF 风险(会阻止私有地址和环回地址范围),但如果你的家庭实验室网络限制出站流量,仍应考虑让其通过出站代理运行。 - **资源占用:**较低——单个 Node 进程和少量 WebSocket 连接。无需数据库,也没有缓存目录。
开发
运行测试
bun run test # vitest, no network needed
使用本地中继进行测试
此仓库中的 docker-compose.yml 会在 ws://localhost:3337 上启动本地 AMB 中继(包含 Typesense)——构建镜像时需要在 ../amb-relay 处检出同级的 amb-relay 仓库:
docker compose up -d
AMB_RELAY_URL=ws://localhost:3337 bun run scripts/test-client.ts
脚本
scripts/ask.ts——向search_content提出自然语言问题,并打印 LLM 客户端会收到的结构化结果scripts/test-client.ts——快速中继客户端冒烟测试(中继信息、查询、转换)scripts/smoke-search-content.ts/scripts/smoke-extract-metadata.ts——针对开发中继的实时冒烟测试scripts/unpublish-server.ts——从公开的 ContextVM 发现中移除服务scripts/delete-announcements.ts——尝试删除公告事件
架构
src/
├── index.ts # Nostr/ContextVM transport entry point
├── stdio.ts # Stdio transport entry point (for cvmi/Claude Code)
├── http.ts # Streamable HTTP transport entry point (OAuth resource server)
├── session.ts # Per-session MCP server factory (relay clients + tool profile)
├── server-info.ts # Server name/version constants
├── authors.ts # NIP-51 follow-set author directories
├── transport/
│ ├── http.ts # Express app + StreamableHTTPServerTransport wiring
│ ├── auth.ts # JWT verification (JWKS)
│ └── prm.ts # OAuth protected-resource metadata document
├── relay/ # AMB relay client + NIP-50 filter builders
├── calendar/ # NIP-52 calendar filters + transforms
├── content/ # Multi-kind content transforms + snippet handling (search_content)
├── profiles/ # Kind-0 profile transforms (resolve_author)
├── skos/ # SKOS vocabulary client, parser, cache, Nostr loader
├── lib/ # extract_metadata library (fetch, PDF, LLM, vocab grounding)
├── signer/ # NIP-46 signing, publishing, NIP-65 relay lists, event builders
├── tools/ # One module per MCP tool (registered via tools/index.ts profiles)
├── resources/ # amb:// MCP resources (schema, vocabularies, relay info)
├── types/ # AMB TypeScript types
└── utils/ # Event → AMB resource transforms, community helpers
许可证
这是一款免费且不受限制的软件,已发布到公共领域。详情请参阅 UNLICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。