User-controlled, cross-platform memory for AI conversations. Save only the answers, exchanges, and passages you choose, store them in Feishu Base, and recall them in a later conversation.
简体中文 · Product Design · Technical Architecture
Nutty is currently a functional MVP / developer preview. The Codex Skill, local MCP server, Feishu adapter, deduplication, search, recall, and update flow are implemented and have passed a real Feishu end-to-end test. Public distribution through the ChatGPT/Codex plugin directory is not finished yet.
AI conversations produce valuable answers, decisions, preferences, and project context every day, but that knowledge usually remains scattered across chat histories. Nutty turns the content you explicitly select into an independent, searchable, and portable memory store.
You can say:
Nutty, save the previous answer.
Nutty, save this exchange as an architecture decision for openclaw-nutty.
Nutty, find the MCP authentication plan I saved earlier.
Nutty, change the last memory's tags to mcp and security.
Nutty does not record every conversation by default. It does not save system prompts, hidden reasoning, tool traces, or authentication data.
| Capability | Status |
|---|---|
| Unified memory model, validation, content hashing, and deduplication | ✅ Complete |
| Capture previous answer, current exchange, selection, or manual content | ✅ Complete |
| Search, full recall, and updates | ✅ Complete |
| Feishu Base storage adapter | ✅ Complete |
Local lark-cli keychain authentication without storing App Secret |
✅ Complete |
| Codex Skill and self-contained stdio MCP bundle | ✅ Complete |
| Standalone Streamable HTTP MCP server | ✅ Complete |
| Real Feishu create, deduplicate, search, and recall test | ✅ Passed |
| Public ChatGPT/Codex plugin directory release | ⏳ Pending |
| ChatGPT Web remote OAuth/HTTPS deployment | ⏳ Later phase |
| OpenClaw MCP adapter and DeepSeek Harness plugin | ⏳ Later phase |
| Notion, Google Workspace, and local storage | ⏳ Later phase |
User explicitly selects content
│
▼
Codex Skill: resolves the content and capture mode
│
▼
Nutty MCP: validates, deduplicates, searches, and updates
│
▼
Nutty Core: applies the shared memory model and privacy policy
│
▼
Feishu adapter → lark-cli keychain → Feishu Base
Local Codex uses stdio + lark-cli. The Skill decides what “the previous answer” means from visible conversation context. MCP tools control external writes. Core keeps the data and safety rules consistent.
Nutty is not yet published in the public plugin directory. The current recommended path is a source installation that installs the same Nutty Skill and registers the self-contained MCP bundle with Codex.
- Git
- Node.js
>= 24 - pnpm
>= 10 - A working Codex CLI installation
- A Feishu Base table you can read and edit
Check versions:
node --version
pnpm --version
codex --versionIf Node.js is installed but pnpm is not:
corepack enable
corepack prepare pnpm@10.23.0 --activategit clone https://github.com/dethan3/openclaw-nutty.git
cd openclaw-nutty
pnpm install --frozen-lockfile
pnpm run checkpnpm run check builds every workspace, runs TypeScript checks, unit tests, and the plugin protocol smoke test. The generated self-contained MCP bundle is:
plugins/openai/nutty/mcp/server.mjs
Nutty's local mode uses the current operating-system user's lark-cli keychain. Nutty does not need to read or store FEISHU_APP_SECRET.
npm install -g @larksuite/cli
lark-cli --version
lark-cli config init --new
lark-cli auth login --domain base
lark-cli auth status --json --verifyNotes:
config init --newguides you through creating or configuring a Feishu application.auth login --domain baserequests only the Base business-domain permissions.- If the CLI returns missing scopes and a developer-console URL, enable those scopes for the app, then authorize again.
- Nutty defaults to
--as user; the authenticated user must also be a collaborator on the target Base. - Upgrade later with
lark-cli update.
Create one Base and one table. Field names are case-sensitive. Keep the English names below unchanged.
The minimum working schema has four required text fields:
| Field | Feishu type | Purpose |
|---|---|---|
Nutty ID |
Text | Stable Nutty UUID |
Title |
Text; may be the primary field | Memory title |
Content |
Text | Original user-selected content |
Content Hash |
Text | SHA-256 deduplication fingerprint |
For the complete experience, create all fields below:
| Field | Recommended type | Accepted type | Purpose |
|---|---|---|---|
Nutty ID |
Text | Text | Required |
Title |
Text | Text | Required; may be primary |
Content |
Text | Text | Required |
Content Hash |
Text | Text | Required |
User Prompt |
Text | Text | User side of an exchange |
Assistant Response |
Text | Text | Assistant side of an exchange |
User Note |
Text | Text | User annotation |
Summary |
Text | Text | Derived summary |
Type |
Text | Text or single select | Memory type |
Tags |
Text | Text or multiple select | Tags; text allows arbitrary new tags |
Project |
Text | Text or single select | Project name |
Capture Mode |
Text | Text or single select | Capture method |
Source |
Text | Text or single select | Source surface |
Source Details |
Text | Text | Source JSON |
Sensitivity |
Text | Text or single select | Sensitivity level |
Schema Version |
Number | Text or number | Currently 1 |
Created At |
Date/time | Text or date/time | Creation time |
Updated At |
Date/time | Text or date/time | Last update time |
If you use select fields, create every value Nutty may write:
Type:conversation,decision,insight,reference,task,project,preference,inboxCapture Mode:previous_answer,current_exchange,selection,manualSource:chatgpt,codex,openclaw,deepseek-harness,otherSensitivity:normal,private,restricted
Use a text field for Tags if you want the model to create new tags freely. Feishu rejects unknown options in a multiple-select field.
Copy the table's full URL, including its table parameter:
https://your-tenant.feishu.cn/base/<base_token>?table=<table_id>
Run from the repository root:
export NUTTY_REPO="$(pwd)"
export NUTTY_CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
mkdir -p "$NUTTY_CODEX_HOME/skills"
ln -s "$NUTTY_REPO/plugins/openai/nutty/skills/nutty-memory" \
"$NUTTY_CODEX_HOME/skills/nutty-memory"
codex mcp add nutty -- node "$NUTTY_REPO/plugins/openai/nutty/mcp/server.mjs"
codex mcp listThis source-development installation keeps the Skill linked to the repository, while MCP points to the generated bundle.
After installation, restart Codex or open a new session. A session that was already running will not automatically load a newly installed Skill.
In a new Codex conversation, say:
Use Nutty to check its status. If it is not configured, use this full URL as the destination:
https://your-tenant.feishu.cn/base/<base_token>?table=<table_id>
Nutty calls configure_nutty, validates access and schema, and writes non-secret destination metadata to:
${XDG_CONFIG_HOME:-~/.config}/nutty/config.json
The file mode is 0600. It contains the Base token, table ID, and local runtime options only. User access tokens, refresh tokens, and App Secret stay in the lark-cli keychain.
Nutty, save the previous answer.
Then search for it:
Nutty, search for the memory I just saved.
On success, Nutty returns the title and Feishu record link. Saving the same normalized content again returns existing instead of creating a duplicate.
Nutty, save the previous answer as an insight for the openclaw-nutty project.
Nutty uses captureMode: previous_answer and saves only the immediately preceding user-visible assistant response.
Nutty, save this exchange as an architecture decision.
Nutty uses captureMode: current_exchange and preserves both the user prompt and assistant response.
Nutty, save only this passage:
“The platform adapter decides which conversation content to save; Core decides how to save it safely and consistently.”
Nutty, search the openclaw-nutty project for memories about MCP authentication.
Nutty, open the most relevant result and summarize it for me.
The Skill searches first, then fetches a full memory only after identifying the relevant result.
Nutty, rename that memory to “Nutty MCP authentication decision” and set its tags to mcp and security.
Title, summary, type, tags, project, and sensitivity can be updated directly. Replacing original content requires explicit user confirmation and replaceOriginal: true.
| Tool | Purpose | Writes data? |
|---|---|---|
get_nutty_status |
Check local configuration and Feishu health | No |
configure_nutty |
Configure a destination from a full Base URL | Local config only |
save_memory |
Save a memory and deduplicate normalized content | Yes |
search_memories |
Filter by text, type, tags, project, source, and time | No |
get_memory |
Read a complete memory by Nutty UUID | No |
update_memory |
Update derived fields or replace original content after confirmation | Yes |
list_destinations |
List configured storage and capabilities | No |
The MVP does not expose a delete tool. Delete records directly in Feishu for now. A future release will add deletion with an explicit confirmation gate.
Memory types:
conversation | decision | insight | reference |
task | project | preference | inbox
Capture modes:
previous_answer | current_exchange | selection | manual
Source surfaces:
chatgpt | codex | openclaw | deepseek-harness | other
Privacy and sensitive content:
- Nutty saves only user-selected, user-visible content.
- It excludes system/developer prompts, hidden reasoning, tool traces, credentials, and identity metadata.
- Content matching private keys, API keys, access tokens, client secrets, or password patterns requires a second confirmation.
- Sensitive confirmation tokens bind the user, destination, and content hash, and expire after five minutes.
- Content length is checked against the selected destination before every write. Feishu text cells allow up to 100,000 characters; titles remain limited to 240, summaries to 2,000, and tags to 20 per memory.
Local plugin mode does not require .env.local. The standalone HTTP server does:
cp apps/mcp-server/.env.example apps/mcp-server/.env.localEdit apps/mcp-server/.env.local:
NUTTY_HOST=127.0.0.1
NUTTY_PORT=3000
NUTTY_ALLOWED_HOSTS=localhost,127.0.0.1
NUTTY_PERSONAL_TOKEN=<random value of at least 32 characters>
NUTTY_PRINCIPAL_ID=personal
NUTTY_CONFIRMATION_SECRET=<random value of at least 32 characters>
NUTTY_DESTINATION_ID=feishu-default
FEISHU_TRANSPORT=lark-cli
FEISHU_APP_TOKEN=<base_token>
FEISHU_TABLE_ID=<table_id>
FEISHU_WEB_BASE_URL=https://your-tenant.feishu.cn/base
FEISHU_LARK_CLI_BINARY=lark-cli
FEISHU_LARK_CLI_IDENTITY=user
FEISHU_LARK_CLI_TIMEOUT_MS=30000Generate random values with:
openssl rand -hex 32Start the server:
pnpm run build
pnpm --filter @nutty/mcp-server startCheck process and storage health:
curl http://127.0.0.1:3000/health
curl -H "Authorization: Bearer <NUTTY_PERSONAL_TOKEN>" \
http://127.0.0.1:3000/health/storageHTTP endpoints:
| Path | Authentication | Purpose |
|---|---|---|
GET /health |
None | Process health |
GET /health/storage |
Bearer token | Feishu storage health |
GET /metrics |
Bearer token | MCP invocation metrics |
POST /mcp |
Bearer token | Streamable HTTP MCP |
The current HTTP profile is a single-user personal profile. Do not expose it directly to the public internet. A production ChatGPT Web deployment still needs HTTPS, standard user authorization, and remote deployment hardening.
For server environments that cannot use a local keychain:
FEISHU_TRANSPORT=openapi
FEISHU_APP_ID=<app_id>
FEISHU_APP_SECRET=<app_secret>
FEISHU_API_BASE_URL=https://open.feishu.cn/open-apisOnly openapi mode requires App ID and App Secret. Local Codex should normally use lark-cli.
pnpm run build # Build all workspaces and the plugin bundle
pnpm run typecheck # Run TypeScript type checking
pnpm run test:unit # Test Core, Feishu adapter, and MCP server
pnpm run test:plugin # Smoke-test the self-contained stdio plugin
pnpm run check # Run the complete quality gate
pnpm run build:plugin # Regenerate only mcp/server.mjsThe project requires Node.js >=24.0.0 and pnpm >=10.0.0.
Update a source installation:
git pull --ff-only
pnpm install --frozen-lockfile
pnpm run checkThe Skill is symlinked and MCP points to the repository bundle, so no registration change is needed. Restart Codex to load the new version.
Uninstall:
codex mcp remove nutty
unlink "${CODEX_HOME:-$HOME/.codex}/skills/nutty-memory"Uninstalling does not remove memories from Feishu or automatically remove local destination metadata. Remove ${XDG_CONFIG_HOME:-~/.config}/nutty/config.json yourself if you also want to clear the local configuration.
- Confirm
${CODEX_HOME:-~/.codex}/skills/nutty-memory/SKILL.mdexists. - Run
codex mcp listand confirmnuttyis registered. - Restart Codex or open a new session after installation.
No destination is configured. Give Nutty the full Feishu Base URL containing ?table=<table_id> so it can call configure_nutty.
lark-cli auth status --json --verify
lark-cli auth login --domain baseAlso confirm that:
- The Feishu app has every scope named in the error.
- The current user completed authorization.
- The current user can access the target Base and table.
Confirm the four required text fields exist with exact names: Nutty ID, Title, Content, and Content Hash.
The four required fields let Nutty work, but missing optional fields cause some metadata to be skipped. Add the fields from the full schema table to restore the complete experience.
Single-select and multiple-select fields only accept options that already exist. Prefer text fields for Type, Tags, Project, Capture Mode, Source, and Sensitivity, or pre-create every option you plan to use.
This is the expected deduplication result. Nutty calculates SHA-256 over normalized content and does not create another record for the same body.
lark-cli update
lark-cli --versionopenclaw-nutty/
├── packages/core/ # Model, use cases, privacy, dedupe, storage port
├── packages/storage-feishu/ # Feishu OpenAPI and lark-cli adapter
├── apps/mcp-server/ # stdio and Streamable HTTP MCP
├── plugins/openai/nutty/ # Codex Skill, manifest, self-contained MCP
├── plugins/openclaw/ # Planned OpenClaw compatibility adapter
├── plugins/deepseek-harness/ # Planned DeepSeek Harness thin plugin
├── scripts/ # Plugin build and smoke test
└── docs/ # Product, architecture, and runtime docs
- User choice first: nothing enters long-term memory without an explicit request.
- Original content first: title, summary, and tags never replace the source text.
- Thin platform adapters: platforms resolve visible context; Core owns shared persistence rules.
- Save and recall together: the MVP includes write, search, and full recall.
- User-owned storage: Feishu is the current source of truth; Nutty does not keep a second body database.
- Minimal credentials: local credentials stay in the
lark-clikeychain.
Read Product Design, Technical Architecture, lark-cli Local Runtime, and OpenAI Plugin for more detail. OpenAI describes a plugin as a package containing Skills, an MCP server, optional UI, or a combination of those pieces. Nutty currently uses the Skill + MCP Server shape; see the official Plugin architecture.
- Publish Nutty in the shared ChatGPT/Codex plugin directory.
- Add HTTPS, standard OAuth, and multi-user remote MCP for ChatGPT Web.
- Complete the OpenClaw MCP compatibility adapter.
- Add the DeepSeek Harness thin plugin.
- Add Notion, Google Workspace, and local storage adapters.
- Add explicitly confirmed deletion and export.
- Evaluate full-text indexing and semantic recall from real usage data.