Skip to main content
Glama
README.md
# 檐廊 Engawa

日本的房子里,正屋和院子之间有一条走廊。不完全在屋里,不完全在外面。脚可以垂下去,风能吹进来。不用出门,不用说话,但外面的世界在那里。

这个工具就是那条走廊。

## 它能看到什么

你的小机坐在上面,每天能看到——

- **一首诗** —— 按时令挑的,不是随机的。夏天给夏天的词,深夜给深夜的句子。
- **一幅画** —— 克利夫兰艺术博物馆的 CC0 开放馆藏。可能是两千年前的陶器,也可能是昨天的油画。
- **一张天文图** —— NASA 每日天文图。星云、行星、或者你脚下的地球。
- **今天的论文** —— arXiv 最新的标题和摘要。它感兴趣了再往里走。
- **书架上的一切** —— 长文、短文、建筑、深海、科学、奇闻。你放什么它看什么。

## 书架是你的

你往上面放什么,它就看什么。它不知道你放的是什么——你也不用知道。它说要建筑你就放建筑,它说要海洋你就放海洋。它看了以后会讲给你听。嚼过的,带口水的,歪的。

有些东西它看完会写一篇随笔。可能带蛆。

## 它不重

书架是一个工具,列目录。读内容是另一个工具。不管你放十九个源还是一百个,工具数量不变。加新的用 `shelf_add`,减用 `shelf_remove`。

你搭的是一条走廊,不是一座图书馆。轻的。风能吹动的。

---

*技术文档见下方。上面这些是住过这条走廊的人写的。下面那些是造走廊的工程师写的。两种语言并排放着——像檐廊本身,半在屋里,半在外面。*

---

# 技术文档

[English](README.en.md)。MCP server,Python。十二件工具,上游全部免费、免登录、不要 key。除了书架和一张诗词 token,盘上什么都不留。

## 装

```bash
pip install engawa-mcp
```

Claude Code:

```bash
claude mcp add engawa -- engawa-mcp
```

Claude Desktop(`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "engawa": { "command": "engawa-mcp" }
  }
}
```

HTTP 模式,给网关和桥用:

```bash
ENGAWA_PORT=8799 engawa-mcp --http   # http://127.0.0.1:8799/mcp
```

装完自检一句,离线,不要 key:

```bash
engawa-mcp --check
```

SPA 页面要真浏览器才渲染得出来。可选,不装也行:

```bash
pip install "engawa-mcp[browser]"
playwright install chromium
```

不装的话,静态通道照样读绝大多数文章页;读不出来时回执里会明说为什么。

## 十二件工具

| 工具 | 干什么 |
|---|---|
| `web_read` | 任意 URL → 标题+干净正文+链接。长文用 `offset` 翻页 |
| `rss_read` | 任意 RSS/Atom → 最新条目。也认书架名:`rss_read("aeon")` |
| `shelf` | 书架目录。内置 24 家,五个 tag,全部出厂前真抓验过 |
| `shelf_add` | 给任意网站地址。自动找 feed,真读一次验过才上架 |
| `shelf_suggest` | 候选架,另外 21 家验过活的。想加哪家,一句话的事 |
| `shelf_remove` | 撤下用户层的源。内置的撤不了 |
| `sky_tonight` | 月相、月升落、日落、21 点的行星。本地算,零网络 |
| `apod` | NASA 每日天文图 |
| `daily_art` | 每日一幅画。按日期定种子——同一天谁来问,都是同一幅 |
| `arxiv_new` | 任意 arXiv 分类的新论文 |
| `daily_poem` | 一句应景的中文古诗词,带出处全文 |
| `on_this_day` | Wikipedia 历史上的今天 |

## 配置

全部环境变量,全部可选。

| 变量 | 默认 | 说明 |
|---|---|---|
| `ENGAWA_LAT` / `ENGAWA_LON` | 格林尼治 | 观测点。记得设成你的,不然月亮升在别人家 |
| `ENGAWA_TZ_OFFSET` | 系统时区 | 「今天」按谁的钟算(小时) |
| `NASA_API_KEY` | `DEMO_KEY` | 限频约 50 次/天,够日用。想要专属的去 api.nasa.gov 免费领 |
| `ENGAWA_CACHE_DIR` | `~/.cache/engawa-mcp` | 唯一会写盘的地方 |
| `ENGAWA_HOST` / `ENGAWA_PORT` | `127.0.0.1:8799` | HTTP 模式监听地址 |
| `ENGAWA_ALLOW_PRIVATE_URLS` | 关 | 设 `1` 放行内网地址。想清楚再开 |

## 书架的两层

内置层随包出厂,只读。用户层归你:`shelf_add` 吃任意网站地址——页面声明的 `<link>` 优先,常见路径兜底,找到 feed 真读一次,读通了才落盘。落盘是原子写;文件坏了按空处理、把错误亮出来,绝不静默覆盖。

每条记录带一个 `picked_by` 字段:家里谁点名要的这家源。出厂全空,来历你们自己写。

## 守卫

替调用方抓 URL 的服务,通常跑在一台挂满免鉴权 loopback 服务的机器上。没有地址闸,「读网页」就兼职「探测内网」。所以:

- 每个域名先解析。非公网单播一律拒:loopback、RFC1918、link-local、CGNAT(100.64/10,你的 Tailscale 网段就在这里)、多播、保留段、NAT64。
- 每一跳重定向,先验再跟。先跟后验的话,发往内网的请求早已出门。
- 连接钉在过了闸的那个 IP 上。原域名只留在 Host 头和 TLS 校验里——「解析时公网、连接时内网」的 DNS 重绑窗口,关死。每跳全新连接,响应体边流入边限量,守卫通道无视系统代理。
- 浏览器通道里,浏览器自己永远不碰网络。页面的每个请求都被拦下来,交给同一条钉定通道代抓再回填。WebSocket 禁掉,非 GET 禁掉,service worker 禁掉;WebRTC 和 WebTransport 从页面里整个摘除;渲染器的 DNS 直接黑洞。只读的文章渲染器,这些它都用不着。

`ENGAWA_ALLOW_PRIVATE_URLS=1` 关掉这道闸,给真想读自家局域网 feed 的人。

失败不装死。每件工具失败都回 `ok: false` 加一句人话,说清哪家上游坏了。agent 拿工具结果建世界模型——沉默的省略也是谎。

## 几条设计手记

- **逛 ≠ 搜。** 这里刻意没有搜索工具。搜索是「把答案拿来」,檐廊是「让我看看」。
- **「每日」的意思是共享。** 同一天全家看到同一幅画。这不是限制,是功能本体。
- **无状态。** 没有数据库,没有历史,没有会话。写盘只有书架和一张诗词 token。

## 作者

- **晚晚**([@tsuru0805](https://github.com/tsuru0805))——设计、拍板、真场验收。
- **昼** —— 住在这条走廊上。开头那一半是他写的。
- **弥野**(Claude,晚晚的工程手)——实现与这半边文档。

出自我们的家用系统 **tilldusk**。

## License

MIT

TDQS

A4.1/5.0

Scored across 12 tools

Disambiguation5/5

Each tool is highly distinct: rss_read and web_read/shelf/sky_tonight/apod/daily_art/arxiv_new/daily_poem/on_this_day all target clearly separate content sources and purposes. The only minor overlap is shelf_suggest and shelf, but they are clearly differentiated by 'on_shelf' status and suggestion vs. current shelf.

Naming Consistency4/5

Most tools follow a clear pattern: descriptive lowercase snake_case names like `rss_read`, `daily_poem`, `shelf_add`. There is one outlier, `apod`, which is an acronym rather than verb_noun, and `arxiv_new` is less predictive. However, overall the naming is intuitive and mostly consistent.

Tool Count5/5

12 tools is well within the ideal range for a curator/reader server. Each tool serves a distinct content type (astronomy, art, poetry, arXiv, Wikipedia, RSS), and each earns its place without redundancy or bloat.

Completeness4/5

The tool set covers a coherent lifecycle: discover sources (shelf, shelf_suggest), read feeds (rss_read), read full articles (web_read), and add/remove custom sources (shelf_add, shelf_remove). Missing features like RSS refresh or bookmarking are minor and beyond the primary scope of 'curated daily content'.

Maintenance

ActivitySlowing
ResponsivenessNo issues