Content
# StructFocus
> **Context-as-a-Tool MCP server** — long-context attention management for any agent host (Claude Code / Cursor / Cline) in three lines of `mcp.json`.
>
> LLM context attention management. ≈ the context half of MemGPT/Letta, minus the agent framework, plus community-standard benchmarks.




In long conversations most agents silently drop the earliest messages (FIFO truncation). StructFocus takes a different path: **don't drop — just don't keep it in front of you all the time** — summarize → capsule → pointer → recall.
## Why
FIFO truncation loses information permanently. On a 160-turn conversation, plain FIFO keeps only ~33% of injected topics in context; StructFocus keeps **100%** by compressing idle context into capsules and recalling it on demand.
Long-context recall rate (whether an injected topic survives in context):
| Turn | FIFO | StructFocus |
|:---:|:---:|:---:|
| 20–80 | 100% | 100% |
| 160 | 33% | **100%** |
NIAH ("needle in a haystack"): see `packages/context/bench/hardcore.ts` — a 20×20 hard grid with semantic distractors, reproducible locally.
## 30-Second Quickstart
Any MCP-capable client can register it in `mcp.json` — no framework source changes:
```json
{
"mcpServers": {
"structfocus": {
"command": "npx",
"args": ["-y", "@structfocus/mcp"],
"env": {
"STRUCT_LLM_API_KEY": "sk-xxx",
"STRUCT_LLM_BASE_URL": "https://api.deepseek.com/v1",
"STRUCT_LLM_MODEL": "deepseek-chat"
}
}
}
}
```
Once connected, the agent can call 8 context tools (see below). LLM compression is **optional** — without a key it runs a deterministic fallback (head/tail truncation, free but coarse).
Configure any OpenAI-compatible API with three env vars:
| Env var | Description | Default |
|:---|:---|:---|
| `STRUCT_LLM_API_KEY` | API key (required for LLM summaries) | — |
| `STRUCT_LLM_BASE_URL` | API base URL | `https://api.openai.com/v1` |
| `STRUCT_LLM_MODEL` | Model name | `gpt-4o-mini` |
## Architecture
Four-layer hot/cold pipeline. Active conversation stays hot; idle context is progressively compressed and pushed to disk, recalled only when queried.
```
┌─────────────────────────────────────────┐
│ LLM Input Window │
│ ┌───────────────────────────────────┐ │
user/assistant ─────▶ │ │ L1 System · L2 Task/Focus · L3 Hist│ │
│ └───────────────────────────────────┘ │
└───────────────┬───────────────┬──────────┘
│ evict/compress│ recall
▼ ▲
┌────────────────────────────────────────┐ ┌─────────────────────────┐
│ L4 Capsule (semantic summary) │ │ ContentStore (BM25 disk) │
│ - chunkBySemantic → summarize → capsule│ │ - evicted/truncated text │
│ - expand:context("<id>") to restore │ │ - recall by keyword/semantic│
└────────────────────────────────────────┘ └─────────────────────────┘
│
▼
┌──────────────────────────┐
│ L5 Pointer (lightweight) │
│ - "[已打包至胶囊 x]" │
└──────────────────────────┘
```
## MCP Tools
| Tool | Purpose |
|:---|:---|
| `context_inject` | Inject a context item (dialogue / tool output / log) |
| `context_recall` | Semantically recall historical context by natural language |
| `context_status` | Full engine status (tokens / capsules / active entries / disk / LLM health / policy) |
| `context_forget` | Forget (unload) a specified context |
| `context_focus` | Pin a file/directory into the working context |
| `context_set_policy` | Hot-update management policy (e.g. `{ "conservative": true }`) |
| `context_stats` | Compact status snapshot (for a quick glance after each call) |
| `context_search` | Full-text keyword search over historical content (ContentStore) |
## Benchmarks
| Benchmark | What it proves | Key needed? | Report |
|:---|:---|:---:|:---|
| NIAH (hardcore grid) | Needle-in-haystack over long context | optional | `packages/context/bench/HARDCORE_V2_REPORT.md` |
| BM25 search precision | BM25 vs simple `includes` (P@5/R@5) | **no** | Locally generated (pnpm bench:bm25) |
| LoCoMo long-dialog | Multi-turn recall + temporal (Cat2) reasoning | yes | `packages/context/bench/LOCOMO_REPORT.md` |
| Multi-hop QA (1.3) | Cross-document reasoning | yes | pending |
| DocQA 750K (1.4) | Long-document Q&A | yes | pending |
Run the local (no-key) BM25 benchmark:
```bash
pnpm bench:bm25
```
## Install & Build
```bash
pnpm install
pnpm build # tsc -b (context → dist)
pnpm test # vitest run (context 173 + mcp 19 = 192 cases)
pnpm lint # eslint packages/context/src packages/mcp/src
```
Requirements:
- **Node >= 22.6.0** (the MCP server runs TypeScript directly via `node --experimental-strip-types`; earlier versions cannot start `@structfocus/mcp`)
- pnpm >= 9 (for development)
Local MCP server (stdio):
```bash
cd packages/mcp
node --experimental-strip-types ./src/index.ts
```
## Contributing
**We welcome issues and pull requests.** StructFocus is built in the open — your bug reports, fixes, benchmarks, and integrations all make FIFO-truncation-replacement better for everyone.
- 🐛 **Found a bug or a case where FIFO beats us?** [Open an issue](https://github.com/kongkongpq/struct-focus/issues) — it's the most useful contribution.
- 🔧 **Want to contribute code?** Fork, branch from `main`, and open a PR. See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, test conventions, and the PR checklist.
- 💬 **Questions?** Use [GitHub Discussions](https://github.com/kongkongpq/struct-focus/discussions) or a plain issue.
- 🔒 **Security issue?** Please report privately per [SECURITY.md](./SECURITY.md), not as a public issue.
Pushing to `main` or opening a PR triggers CI (`typecheck → lint → test`) — keep it green.
## License
[Apache-2.0](./LICENSE)
## Quick Verification (30 seconds)
After connecting, use a short conversation to verify that it's really in "manage context" rather than "丢消息" (losing messages):
1. **Start MCP** (using Claude Code as an example, with the registered `mcp.json` above):
```bash
# Manually start a server to view logs (optional)
cd packages/mcp
STRUCT_LLM_API_KEY=sk-xxx STRUCT_LLM_BASE_URL=https://api.deepseek.com/v1 STRUCT_LLM_MODEL=deepseek-chat \
node --experimental-strip-types ./src/index.ts
# Startup will print "LLM compression enabled …" and perform a /models health check (failure will alert but not block)
```
2. **Inject + Recall**: Let the Agent first submit a long context to `context_inject`, and then ask a detail that **has been injected but has been summarized and沉没** (沉没 means sunk or forgotten). Calling `context_recall` should be able to retrieve it (rather than "I don't remember").
3. **Check status**: Call `context_status` and focus on three parts:
- `storeStats`: `usedMB / maxMB` — Disk usage is within the limit (default 512MB, set `STRUCT_STORE_MAX_MB=0` to disable).
- `llmStatus`: `status` should be `ok` (configured and recently successful); if `degraded`/`failed` indicates compression is degraded to local summary, check Key/quota.
- `policy`: `effectiveEmergencyThreshold` defaults to 0.85; if conservative mode is enabled (`context_set_policy {conservative:true}`), it will increase to 0.97.
4. **Troubleshooting**:
- `context_status` reports `llmStatus.configured=false` → `STRUCT_LLM_API_KEY` not read, compression falls back to free (coarser but works).
- `healthy=false` → `/models` unreachable, check `STRUCT_LLM_BASE_URL` and network.
- MCP client connection fails → Confirm Node ≥ 22.6, and `npx -y @structfocus/mcp` can pull the package (if not released, use local `node --experimental-strip-types ./packages/mcp/src/index.ts`).
## Code-level Usage (TypeScript host)
```ts
import { LongContextEngine } from "@structfocus/context";
const engine = new LongContextEngine({ llmCall: (p) => yourLLM.chat([{ role: "user", content: p }]) });
engine.feedBatch(history);
const { injectText } = await engine.recall(query);
```
## Benchmarks
| Benchmark | What to verify | Key required? | Report |
|:---|:---|:---:|:---|
| NIAH (hardcore grid) | Long context needle-in-haystack recall | Optional | `packages/context/bench/HARDCORE_V2_REPORT.md` |
| BM25 search accuracy | BM25 vs naive `includes` (P@5/R@5) | **No** | Generate locally (bench:bm25) |
| LoCoMo long dialogue | Multi-turn recall + temporal (Cat2) reasoning | Yes | `packages/context/bench/LOCOMO_REPORT.md` |
| Multi-hop QA (1.3) | Cross-document reasoning | Yes | To be completed |
| DocQA 750K (1.4) | Long document QA | Yes | To be completed |
Unified entry `bench/run.mjs` supports `--suite <bm25|niah|multihop|docqa|all>`; BM25 suite without Key can run directly locally:
```bash
pnpm bench:bm25
```
## Environment Requirements
- **Node >= 22.6.0** (MCP Server uses `node --experimental-strip-types` to run TypeScript directly, supported from 22.6; lower versions cannot start `@structfocus/mcp`)
- pnpm >= 9 (for development)
## Build and Verification
```bash
pnpm install
pnpm build # tsc -b (context → dist)
pnpm test # vitest run (context 173 + mcp 19 = 192 cases)
pnpm lint # eslint packages/context/src packages/mcp/src
```
Start a local MCP Server (stdio):
```bash
cd packages/mcp
node --experimental-strip-types ./src/index.ts
```
## Known Issues
- Multi-model benchmark harness is built (`packages/context/bench/run-llm.ts`, supporting DeepSeek/GLM/Qwen/GPT-4o-mini), but **actual data requires your own Key** to generate (`STRUCT_LLM_*` or each provider's Key environment variables)
- DocQA within the window range has been verified to be recallable; exceeding the window causes model window limitations, not an architecture issue
- Capsule LLM summarization quality not systematically evaluated
## Participation
**We welcome Issues and Pull Requests.** StructFocus is an open and collaborative project — your bug reports, fixes, benchmark data, and integration solutions can make "alternative FIFO truncation" better.
- 🐛 **Found a bug or FIFO overflow scenario?** [Open an issue](https://github.com/kongkongpq/struct-focus/issues) — this is the most valuable contribution.
- 🔧 **Want to contribute code?** Fork and branch from `main` to open a PR. See [CONTRIBUTING.md](./CONTRIBUTING.md) for local setup, testing specifications, and PR checklist.
- 💬 **Have questions?** Use [GitHub Discussions](https://github.com/kongkongpq/struct-focus/discussions) or regular issues.
- 🔒 **Security issues?** Report privately according to [SECURITY.md](./SECURITY.md), not publicly.
Pushing to `main` or opening a PR triggers CI (`typecheck → lint → test`), so please ensure they pass.
## License
[Apache-2.0](./LICENSE)
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available