Skip to content

Commit 6bfa4f4

Browse files
authored
Merge pull request #144 from QuantGeekDev/feat/mcp-framework-docs
feat: add @mcp-framework/docs package
2 parents 5984017 + ab2cc5d commit 6bfa4f4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4773
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ coverage/
1515
*~
1616
.yalc
1717
yalc.lock
18+
plans/

docs/docs-package/caching.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Caching
2+
3+
`@mcp-framework/docs` includes a built-in caching layer that reduces HTTP requests to your documentation site. All source adapters use caching automatically.
4+
5+
## Default Cache
6+
7+
The default `MemoryCache` is an in-memory LRU (Least Recently Used) cache with TTL (Time-To-Live) expiry.
8+
9+
### What Gets Cached
10+
11+
| Content | Cache Key | Default TTL |
12+
|---------|-----------|-------------|
13+
| `llms.txt` content | `index:{baseUrl}` | `refreshInterval` |
14+
| `llms-full.txt` content | `full:{baseUrl}` | `refreshInterval` |
15+
| Individual page content | `page:{slug}` | `refreshInterval` |
16+
| Search results | `search:{query}:{section}:{limit}` | `refreshInterval` |
17+
| Parsed section tree | `sections:{baseUrl}` | `refreshInterval` |
18+
19+
### Configuration
20+
21+
```typescript
22+
import { MemoryCache, LlmsTxtSource } from "@mcp-framework/docs";
23+
24+
const cache = new MemoryCache({
25+
maxEntries: 200, // Default: 100
26+
ttlMs: 600_000, // Default: 300_000 (5 minutes)
27+
});
28+
29+
const source = new LlmsTxtSource({
30+
baseUrl: "https://docs.example.com",
31+
cache,
32+
});
33+
```
34+
35+
The `refreshInterval` option on source adapters sets the TTL for the default cache. If you provide a custom cache, the `ttlMs` on the cache takes precedence.
36+
37+
## Cache Behavior
38+
39+
- **Lazy expiry** — Expired entries are cleaned on access, not via a background timer. This avoids interval leaks.
40+
- **LRU eviction** — When `maxEntries` is reached, the oldest entry is evicted to make room for new ones.
41+
- **Per-entry TTL** — Each `set()` call can override the default TTL.
42+
- **Overwrite resets TTL** — Storing the same key again resets the expiry timer.
43+
44+
## Cache Interface
45+
46+
Implement this interface for custom backends (Redis, SQLite, etc.):
47+
48+
```typescript
49+
interface Cache {
50+
get<T>(key: string): Promise<T | null>;
51+
set<T>(key: string, value: T, ttlMs?: number): Promise<void>;
52+
delete(key: string): Promise<void>;
53+
clear(): Promise<void>;
54+
stats(): { hits: number; misses: number; size: number };
55+
}
56+
```
57+
58+
### Example: Redis Cache
59+
60+
```typescript
61+
import { Cache, CacheStats } from "@mcp-framework/docs";
62+
import { createClient } from "redis";
63+
64+
class RedisCache implements Cache {
65+
private client;
66+
private defaultTtl: number;
67+
private _hits = 0;
68+
private _misses = 0;
69+
70+
constructor(redisUrl: string, ttlMs = 300_000) {
71+
this.client = createClient({ url: redisUrl });
72+
this.defaultTtl = ttlMs;
73+
}
74+
75+
async get<T>(key: string): Promise<T | null> {
76+
const value = await this.client.get(`docs:${key}`);
77+
if (!value) {
78+
this._misses++;
79+
return null;
80+
}
81+
this._hits++;
82+
return JSON.parse(value);
83+
}
84+
85+
async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {
86+
const ttl = Math.ceil((ttlMs ?? this.defaultTtl) / 1000);
87+
await this.client.set(`docs:${key}`, JSON.stringify(value), { EX: ttl });
88+
}
89+
90+
async delete(key: string): Promise<void> {
91+
await this.client.del(`docs:${key}`);
92+
}
93+
94+
async clear(): Promise<void> {
95+
// Careful: only clear docs: keys
96+
const keys = await this.client.keys("docs:*");
97+
if (keys.length > 0) await this.client.del(keys);
98+
this._hits = 0;
99+
this._misses = 0;
100+
}
101+
102+
stats(): CacheStats {
103+
return { hits: this._hits, misses: this._misses, size: -1 };
104+
}
105+
}
106+
```
107+
108+
## Cache Invalidation
109+
110+
In v1, cache invalidation is **TTL-based only**. When the TTL expires, the next request triggers a fresh fetch. There is no webhook-based or push-based invalidation.
111+
112+
For near-real-time updates, set a shorter `refreshInterval`:
113+
114+
```typescript
115+
const source = new FumadocsRemoteSource({
116+
baseUrl: "https://docs.example.com",
117+
refreshInterval: 60_000, // Re-fetch every minute
118+
});
119+
```
120+
121+
For less frequently changing docs, increase it:
122+
123+
```typescript
124+
const source = new FumadocsRemoteSource({
125+
baseUrl: "https://docs.example.com",
126+
refreshInterval: 3_600_000, // Re-fetch every hour
127+
});
128+
```

docs/docs-package/cli.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# CLI & Project Scaffolding
2+
3+
`@mcp-framework/docs` includes a CLI tool that scaffolds a ready-to-run documentation MCP server project.
4+
5+
## Creating a Project
6+
7+
```bash
8+
npx create-docs-server my-api-docs
9+
```
10+
11+
This creates a project with the following structure:
12+
13+
```
14+
my-api-docs/
15+
├── src/
16+
│ └── index.ts # Pre-configured DocsServer with FumadocsRemoteSource
17+
├── package.json # Dependencies on mcp-framework + @mcp-framework/docs
18+
├── tsconfig.json # TypeScript configuration
19+
├── .env.example # DOCS_BASE_URL, optional DOCS_API_KEY
20+
├── .gitignore
21+
└── README.md # Setup instructions and MCP client config
22+
```
23+
24+
## Generated Files
25+
26+
### `src/index.ts`
27+
28+
```typescript
29+
import { DocsServer, FumadocsRemoteSource } from "@mcp-framework/docs";
30+
31+
const source = new FumadocsRemoteSource({
32+
baseUrl: process.env.DOCS_BASE_URL || "https://docs.example.com",
33+
headers: process.env.DOCS_API_KEY
34+
? { Authorization: `Bearer ${process.env.DOCS_API_KEY}` }
35+
: undefined,
36+
});
37+
38+
const server = new DocsServer({
39+
source,
40+
name: process.env.DOCS_SERVER_NAME || "my-api-docs",
41+
version: "1.0.0",
42+
});
43+
44+
server.start();
45+
```
46+
47+
### `.env.example`
48+
49+
```bash
50+
DOCS_BASE_URL=https://docs.example.com
51+
DOCS_SERVER_NAME=my-api-docs
52+
# DOCS_API_KEY=your-api-key-here
53+
```
54+
55+
## Setup After Scaffolding
56+
57+
```bash
58+
cd my-api-docs
59+
cp .env.example .env
60+
# Edit .env with your documentation site URL
61+
npm run build
62+
npm start
63+
```
64+
65+
## Connecting to MCP Clients
66+
67+
### Claude Code
68+
69+
```bash
70+
claude mcp add my-api-docs -- node /path/to/my-api-docs/dist/index.js
71+
```
72+
73+
### Claude Desktop
74+
75+
Add to `claude_desktop_config.json`:
76+
77+
```json
78+
{
79+
"mcpServers": {
80+
"my-api-docs": {
81+
"command": "node",
82+
"args": ["/path/to/my-api-docs/dist/index.js"],
83+
"env": {
84+
"DOCS_BASE_URL": "https://docs.myapi.com"
85+
}
86+
}
87+
}
88+
}
89+
```
90+
91+
### Cursor
92+
93+
Add to MCP settings:
94+
95+
```json
96+
{
97+
"my-api-docs": {
98+
"command": "node",
99+
"args": ["/path/to/my-api-docs/dist/index.js"],
100+
"env": {
101+
"DOCS_BASE_URL": "https://docs.myapi.com"
102+
}
103+
}
104+
}
105+
```
106+
107+
## SKILL.md Template
108+
109+
The package includes a `SKILL.md.template` that you can customize to teach Claude Code how to approach integrations against your API. Copy it into your project:
110+
111+
```bash
112+
cp node_modules/@mcp-framework/docs/SKILL.md.template SKILL.md
113+
# Edit SKILL.md with your API-specific patterns
114+
```
115+
116+
The template includes `<!-- CUSTOMIZE -->` markers for sections you should edit:
117+
- Authentication patterns
118+
- SDK initialization
119+
- Common API call patterns
120+
- Error handling
121+
- Common pitfalls

0 commit comments

Comments
 (0)